mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 11:46:19 +00:00
Merge branch 'suspend' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6
* 'suspend' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (38 commits) suspend: cleanup reference to swsusp_pg_dir[] PM: Remove obsolete /sys/devices/.../power/state docs Hibernation: Invoke suspend notifications after console switch Suspend: Invoke suspend notifications after console switch Suspend: Clean up suspend_64.c Suspend: Add config option to disable the freezer if architecture wants that ACPI: Print message before calling _PTS ACPI hibernation: Call _PTS before suspending devices Hibernation: Introduce begin() and end() callbacks ACPI suspend: Call _PTS before suspending devices ACPI: Separate disabling of GPEs from _PTS ACPI: Separate invocations of _GTS and _BFS from _PTS and _WAK Suspend: Introduce begin() and end() callbacks suspend: fix ia64 allmodconfig build ACPI: clear GPE earily in resume to avoid warning Suspend: Clean up Kconfig (V2) Hibernation: Clean up Kconfig (V2) Hibernation: Update messages Suspend: Use common prefix in messages Hibernation: Remove unnecessary variable declaration ...
This commit is contained in:
commit
687fcdf741
34 changed files with 1032 additions and 505 deletions
|
@ -168,6 +168,11 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||
acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA
|
||||
Format: <irq>,<irq>...
|
||||
|
||||
acpi_new_pts_ordering [HW,ACPI]
|
||||
Enforce the ACPI 2.0 ordering of the _PTS control
|
||||
method wrt putting devices into low power states
|
||||
default: pre ACPI 2.0 ordering of _PTS
|
||||
|
||||
acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT
|
||||
|
||||
acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
|
||||
|
|
|
@ -1,45 +1,111 @@
|
|||
Debugging suspend and resume
|
||||
Debugging hibernation and suspend
|
||||
(C) 2007 Rafael J. Wysocki <rjw@sisk.pl>, GPL
|
||||
|
||||
1. Testing suspend to disk (STD)
|
||||
1. Testing hibernation (aka suspend to disk or STD)
|
||||
|
||||
To verify that the STD works, you can try to suspend in the "reboot" mode:
|
||||
To check if hibernation works, you can try to hibernate in the "reboot" mode:
|
||||
|
||||
# echo reboot > /sys/power/disk
|
||||
# echo disk > /sys/power/state
|
||||
|
||||
and the system should suspend, reboot, resume and get back to the command prompt
|
||||
where you have started the transition. If that happens, the STD is most likely
|
||||
to work correctly, but you need to repeat the test at least a couple of times in
|
||||
a row for confidence. This is necessary, because some problems only show up on
|
||||
a second attempt at suspending and resuming the system. You should also test
|
||||
the "platform" and "shutdown" modes of suspend:
|
||||
and the system should create a hibernation image, reboot, resume and get back to
|
||||
the command prompt where you have started the transition. If that happens,
|
||||
hibernation is most likely to work correctly. Still, you need to repeat the
|
||||
test at least a couple of times in a row for confidence. [This is necessary,
|
||||
because some problems only show up on a second attempt at suspending and
|
||||
resuming the system.] Moreover, hibernating in the "reboot" and "shutdown"
|
||||
modes causes the PM core to skip some platform-related callbacks which on ACPI
|
||||
systems might be necessary to make hibernation work. Thus, if you machine fails
|
||||
to hibernate or resume in the "reboot" mode, you should try the "platform" mode:
|
||||
|
||||
# echo platform > /sys/power/disk
|
||||
# echo disk > /sys/power/state
|
||||
|
||||
or
|
||||
which is the default and recommended mode of hibernation.
|
||||
|
||||
Unfortunately, the "platform" mode of hibernation does not work on some systems
|
||||
with broken BIOSes. In such cases the "shutdown" mode of hibernation might
|
||||
work:
|
||||
|
||||
# echo shutdown > /sys/power/disk
|
||||
# echo disk > /sys/power/state
|
||||
|
||||
in which cases you will have to press the power button to make the system
|
||||
resume. If that does not work, you will need to identify what goes wrong.
|
||||
(it is similar to the "reboot" mode, but it requires you to press the power
|
||||
button to make the system resume).
|
||||
|
||||
a) Test mode of STD
|
||||
If neither "platform" nor "shutdown" hibernation mode works, you will need to
|
||||
identify what goes wrong.
|
||||
|
||||
To verify if there are any drivers that cause problems you can run the STD
|
||||
in the test mode:
|
||||
a) Test modes of hibernation
|
||||
|
||||
# echo test > /sys/power/disk
|
||||
To find out why hibernation fails on your system, you can use a special testing
|
||||
facility available if the kernel is compiled with CONFIG_PM_DEBUG set. Then,
|
||||
there is the file /sys/power/pm_test that can be used to make the hibernation
|
||||
core run in a test mode. There are 5 test modes available:
|
||||
|
||||
freezer
|
||||
- test the freezing of processes
|
||||
|
||||
devices
|
||||
- test the freezing of processes and suspending of devices
|
||||
|
||||
platform
|
||||
- test the freezing of processes, suspending of devices and platform
|
||||
global control methods(*)
|
||||
|
||||
processors
|
||||
- test the freezing of processes, suspending of devices, platform
|
||||
global control methods(*) and the disabling of nonboot CPUs
|
||||
|
||||
core
|
||||
- test the freezing of processes, suspending of devices, platform global
|
||||
control methods(*), the disabling of nonboot CPUs and suspending of
|
||||
platform/system devices
|
||||
|
||||
(*) the platform global control methods are only available on ACPI systems
|
||||
and are only tested if the hibernation mode is set to "platform"
|
||||
|
||||
To use one of them it is necessary to write the corresponding string to
|
||||
/sys/power/pm_test (eg. "devices" to test the freezing of processes and
|
||||
suspending devices) and issue the standard hibernation commands. For example,
|
||||
to use the "devices" test mode along with the "platform" mode of hibernation,
|
||||
you should do the following:
|
||||
|
||||
# echo devices > /sys/power/pm_test
|
||||
# echo platform > /sys/power/disk
|
||||
# echo disk > /sys/power/state
|
||||
|
||||
in which case the system should freeze tasks, suspend devices, disable nonboot
|
||||
CPUs (if any), wait for 5 seconds, enable nonboot CPUs, resume devices, thaw
|
||||
tasks and return to your command prompt. If that fails, most likely there is
|
||||
a driver that fails to either suspend or resume (in the latter case the system
|
||||
may hang or be unstable after the test, so please take that into consideration).
|
||||
To find this driver, you can carry out a binary search according to the rules:
|
||||
Then, the kernel will try to freeze processes, suspend devices, wait 5 seconds,
|
||||
resume devices and thaw processes. If "platform" is written to
|
||||
/sys/power/pm_test , then after suspending devices the kernel will additionally
|
||||
invoke the global control methods (eg. ACPI global control methods) used to
|
||||
prepare the platform firmware for hibernation. Next, it will wait 5 seconds and
|
||||
invoke the platform (eg. ACPI) global methods used to cancel hibernation etc.
|
||||
|
||||
Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal
|
||||
hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test
|
||||
contains a space-separated list of all available tests (including "none" that
|
||||
represents the normal functionality) in which the current test level is
|
||||
indicated by square brackets.
|
||||
|
||||
Generally, as you can see, each test level is more "invasive" than the previous
|
||||
one and the "core" level tests the hardware and drivers as deeply as possible
|
||||
without creating a hibernation image. Obviously, if the "devices" test fails,
|
||||
the "platform" test will fail as well and so on. Thus, as a rule of thumb, you
|
||||
should try the test modes starting from "freezer", through "devices", "platform"
|
||||
and "processors" up to "core" (repeat the test on each level a couple of times
|
||||
to make sure that any random factors are avoided).
|
||||
|
||||
If the "freezer" test fails, there is a task that cannot be frozen (in that case
|
||||
it usually is possible to identify the offending task by analysing the output of
|
||||
dmesg obtained after the failing test). Failure at this level usually means
|
||||
that there is a problem with the tasks freezer subsystem that should be
|
||||
reported.
|
||||
|
||||
If the "devices" test fails, most likely there is a driver that cannot suspend
|
||||
or resume its device (in the latter case the system may hang or become unstable
|
||||
after the test, so please take that into consideration). To find this driver,
|
||||
you can carry out a binary search according to the rules:
|
||||
- if the test fails, unload a half of the drivers currently loaded and repeat
|
||||
(that would probably involve rebooting the system, so always note what drivers
|
||||
have been loaded before the test),
|
||||
|
@ -47,23 +113,46 @@ have been loaded before the test),
|
|||
recently and repeat.
|
||||
|
||||
Once you have found the failing driver (there can be more than just one of
|
||||
them), you have to unload it every time before the STD transition. In that case
|
||||
please make sure to report the problem with the driver.
|
||||
them), you have to unload it every time before hibernation. In that case please
|
||||
make sure to report the problem with the driver.
|
||||
|
||||
It is also possible that a cycle can still fail after you have unloaded
|
||||
all modules. In that case, you would want to look in your kernel configuration
|
||||
for the drivers that can be compiled as modules (testing again with them as
|
||||
modules), and possibly also try boot time options such as "noapic" or "noacpi".
|
||||
It is also possible that the "devices" test will still fail after you have
|
||||
unloaded all modules. In that case, you may want to look in your kernel
|
||||
configuration for the drivers that can be compiled as modules (and test again
|
||||
with these drivers compiled as modules). You may also try to use some special
|
||||
kernel command line options such as "noapic", "noacpi" or even "acpi=off".
|
||||
|
||||
If the "platform" test fails, there is a problem with the handling of the
|
||||
platform (eg. ACPI) firmware on your system. In that case the "platform" mode
|
||||
of hibernation is not likely to work. You can try the "shutdown" mode, but that
|
||||
is rather a poor man's workaround.
|
||||
|
||||
If the "processors" test fails, the disabling/enabling of nonboot CPUs does not
|
||||
work (of course, this only may be an issue on SMP systems) and the problem
|
||||
should be reported. In that case you can also try to switch the nonboot CPUs
|
||||
off and on using the /sys/devices/system/cpu/cpu*/online sysfs attributes and
|
||||
see if that works.
|
||||
|
||||
If the "core" test fails, which means that suspending of the system/platform
|
||||
devices has failed (these devices are suspended on one CPU with interrupts off),
|
||||
the problem is most probably hardware-related and serious, so it should be
|
||||
reported.
|
||||
|
||||
A failure of any of the "platform", "processors" or "core" tests may cause your
|
||||
system to hang or become unstable, so please beware. Such a failure usually
|
||||
indicates a serious problem that very well may be related to the hardware, but
|
||||
please report it anyway.
|
||||
|
||||
b) Testing minimal configuration
|
||||
|
||||
If the test mode of STD works, you can boot the system with "init=/bin/bash"
|
||||
and attempt to suspend in the "reboot", "shutdown" and "platform" modes. If
|
||||
that does not work, there probably is a problem with a driver statically
|
||||
compiled into the kernel and you can try to compile more drivers as modules,
|
||||
so that they can be tested individually. Otherwise, there is a problem with a
|
||||
modular driver and you can find it by loading a half of the modules you normally
|
||||
use and binary searching in accordance with the algorithm:
|
||||
If all of the hibernation test modes work, you can boot the system with the
|
||||
"init=/bin/bash" command line parameter and attempt to hibernate in the
|
||||
"reboot", "shutdown" and "platform" modes. If that does not work, there
|
||||
probably is a problem with a driver statically compiled into the kernel and you
|
||||
can try to compile more drivers as modules, so that they can be tested
|
||||
individually. Otherwise, there is a problem with a modular driver and you can
|
||||
find it by loading a half of the modules you normally use and binary searching
|
||||
in accordance with the algorithm:
|
||||
- if there are n modules loaded and the attempt to suspend and resume fails,
|
||||
unload n/2 of the modules and try again (that would probably involve rebooting
|
||||
the system),
|
||||
|
@ -71,19 +160,19 @@ the system),
|
|||
load n/2 modules more and try again.
|
||||
|
||||
Again, if you find the offending module(s), it(they) must be unloaded every time
|
||||
before the STD transition, and please report the problem with it(them).
|
||||
before hibernation, and please report the problem with it(them).
|
||||
|
||||
c) Advanced debugging
|
||||
|
||||
In case the STD does not work on your system even in the minimal configuration
|
||||
and compiling more drivers as modules is not practical or some modules cannot
|
||||
be unloaded, you can use one of the more advanced debugging techniques to find
|
||||
the problem. First, if there is a serial port in your box, you can boot the
|
||||
kernel with the 'no_console_suspend' parameter and try to log kernel
|
||||
messages using the serial console. This may provide you with some information
|
||||
about the reasons of the suspend (resume) failure. Alternatively, it may be
|
||||
possible to use a FireWire port for debugging with firescope
|
||||
(ftp://ftp.firstfloor.org/pub/ak/firescope/). On i386 it is also possible to
|
||||
In case that hibernation does not work on your system even in the minimal
|
||||
configuration and compiling more drivers as modules is not practical or some
|
||||
modules cannot be unloaded, you can use one of the more advanced debugging
|
||||
techniques to find the problem. First, if there is a serial port in your box,
|
||||
you can boot the kernel with the 'no_console_suspend' parameter and try to log
|
||||
kernel messages using the serial console. This may provide you with some
|
||||
information about the reasons of the suspend (resume) failure. Alternatively,
|
||||
it may be possible to use a FireWire port for debugging with firescope
|
||||
(ftp://ftp.firstfloor.org/pub/ak/firescope/). On x86 it is also possible to
|
||||
use the PM_TRACE mechanism documented in Documentation/s2ram.txt .
|
||||
|
||||
2. Testing suspend to RAM (STR)
|
||||
|
@ -91,16 +180,25 @@ use the PM_TRACE mechanism documented in Documentation/s2ram.txt .
|
|||
To verify that the STR works, it is generally more convenient to use the s2ram
|
||||
tool available from http://suspend.sf.net and documented at
|
||||
http://en.opensuse.org/s2ram . However, before doing that it is recommended to
|
||||
carry out the procedure described in section 1.
|
||||
carry out STR testing using the facility described in section 1.
|
||||
|
||||
Assume you have resolved the problems with the STD and you have found some
|
||||
failing drivers. These drivers are also likely to fail during the STR or
|
||||
during the resume, so it is better to unload them every time before the STR
|
||||
transition. Now, you can follow the instructions at
|
||||
http://en.opensuse.org/s2ram to test the system, but if it does not work
|
||||
"out of the box", you may need to boot it with "init=/bin/bash" and test
|
||||
s2ram in the minimal configuration. In that case, you may be able to search
|
||||
for failing drivers by following the procedure analogous to the one described in
|
||||
1b). If you find some failing drivers, you will have to unload them every time
|
||||
before the STR transition (ie. before you run s2ram), and please report the
|
||||
problems with them.
|
||||
Namely, after writing "freezer", "devices", "platform", "processors", or "core"
|
||||
into /sys/power/pm_test (available if the kernel is compiled with
|
||||
CONFIG_PM_DEBUG set) the suspend code will work in the test mode corresponding
|
||||
to given string. The STR test modes are defined in the same way as for
|
||||
hibernation, so please refer to Section 1 for more information about them. In
|
||||
particular, the "core" test allows you to test everything except for the actual
|
||||
invocation of the platform firmware in order to put the system into the sleep
|
||||
state.
|
||||
|
||||
Among other things, the testing with the help of /sys/power/pm_test may allow
|
||||
you to identify drivers that fail to suspend or resume their devices. They
|
||||
should be unloaded every time before an STR transition.
|
||||
|
||||
Next, you can follow the instructions at http://en.opensuse.org/s2ram to test
|
||||
the system, but if it does not work "out of the box", you may need to boot it
|
||||
with "init=/bin/bash" and test s2ram in the minimal configuration. In that
|
||||
case, you may be able to search for failing drivers by following the procedure
|
||||
analogous to the one described in section 1. If you find some failing drivers,
|
||||
you will have to unload them every time before an STR transition (ie. before
|
||||
you run s2ram), and please report the problems with them.
|
||||
|
|
|
@ -502,52 +502,3 @@ If the CPU can have a "cpufreq" driver, there also may be opportunities
|
|||
to shift to lower voltage settings and reduce the power cost of executing
|
||||
a given number of instructions. (Without voltage adjustment, it's rare
|
||||
for cpufreq to save much power; the cost-per-instruction must go down.)
|
||||
|
||||
|
||||
/sys/devices/.../power/state files
|
||||
==================================
|
||||
For now you can also test some of this functionality using sysfs.
|
||||
|
||||
DEPRECATED: USE "power/state" ONLY FOR DRIVER TESTING, AND
|
||||
AVOID USING dev->power.power_state IN DRIVERS.
|
||||
|
||||
THESE WILL BE REMOVED. IF THE "power/state" FILE GETS REPLACED,
|
||||
IT WILL BECOME SOMETHING COUPLED TO THE BUS OR DRIVER.
|
||||
|
||||
In each device's directory, there is a 'power' directory, which contains
|
||||
at least a 'state' file. The value of this field is effectively boolean,
|
||||
PM_EVENT_ON or PM_EVENT_SUSPEND.
|
||||
|
||||
* Reading from this file displays a value corresponding to
|
||||
the power.power_state.event field. All nonzero values are
|
||||
displayed as "2", corresponding to a low power state; zero
|
||||
is displayed as "0", corresponding to normal operation.
|
||||
|
||||
* Writing to this file initiates a transition using the
|
||||
specified event code number; only '0', '2', and '3' are
|
||||
accepted (without a newline); '2' and '3' are both
|
||||
mapped to PM_EVENT_SUSPEND.
|
||||
|
||||
On writes, the PM core relies on that recorded event code and the device/bus
|
||||
capabilities to determine whether it uses a partial suspend() or resume()
|
||||
sequence to change things so that the recorded event corresponds to the
|
||||
numeric parameter.
|
||||
|
||||
- If the bus requires the irqs-disabled suspend_late()/resume_early()
|
||||
phases, writes fail because those operations are not supported here.
|
||||
|
||||
- If the recorded value is the expected value, nothing is done.
|
||||
|
||||
- If the recorded value is nonzero, the device is partially resumed,
|
||||
using the bus.resume() and/or class.resume() methods.
|
||||
|
||||
- If the target value is nonzero, the device is partially suspended,
|
||||
using the class.suspend() and/or bus.suspend() methods and the
|
||||
PM_EVENT_SUSPEND message.
|
||||
|
||||
Drivers have no way to tell whether their suspend() and resume() calls
|
||||
have come through the sysfs power/state file or as part of entering a
|
||||
system sleep state, except that when accessed through sysfs the normal
|
||||
parent/child sequencing rules are ignored. Drivers (such as bus, bridge,
|
||||
or hub drivers) which expose child devices may need to enforce those rules
|
||||
on their own.
|
||||
|
|
|
@ -6,9 +6,9 @@ Testing suspend and resume support in device drivers
|
|||
Unfortunately, to effectively test the support for the system-wide suspend and
|
||||
resume transitions in a driver, it is necessary to suspend and resume a fully
|
||||
functional system with this driver loaded. Moreover, that should be done
|
||||
several times, preferably several times in a row, and separately for the suspend
|
||||
to disk (STD) and the suspend to RAM (STR) transitions, because each of these
|
||||
cases involves different ordering of operations and different interactions with
|
||||
several times, preferably several times in a row, and separately for hibernation
|
||||
(aka suspend to disk or STD) and suspend to RAM (STR), because each of these
|
||||
cases involves slightly different operations and different interactions with
|
||||
the machine's BIOS.
|
||||
|
||||
Of course, for this purpose the test system has to be known to suspend and
|
||||
|
@ -22,20 +22,24 @@ for more information about the debugging of suspend/resume functionality.
|
|||
Once you have resolved the suspend/resume-related problems with your test system
|
||||
without the new driver, you are ready to test it:
|
||||
|
||||
a) Build the driver as a module, load it and try the STD in the test mode (see:
|
||||
Documents/power/basic-pm-debugging.txt, 1a)).
|
||||
a) Build the driver as a module, load it and try the test modes of hibernation
|
||||
(see: Documents/power/basic-pm-debugging.txt, 1).
|
||||
|
||||
b) Load the driver and attempt to suspend to disk in the "reboot", "shutdown"
|
||||
and "platform" modes (see: Documents/power/basic-pm-debugging.txt, 1).
|
||||
b) Load the driver and attempt to hibernate in the "reboot", "shutdown" and
|
||||
"platform" modes (see: Documents/power/basic-pm-debugging.txt, 1).
|
||||
|
||||
c) Compile the driver directly into the kernel and try the STD in the test mode.
|
||||
c) Compile the driver directly into the kernel and try the test modes of
|
||||
hibernation.
|
||||
|
||||
d) Attempt to suspend to disk with the driver compiled directly into the kernel
|
||||
in the "reboot", "shutdown" and "platform" modes.
|
||||
d) Attempt to hibernate with the driver compiled directly into the kernel
|
||||
in the "reboot", "shutdown" and "platform" modes.
|
||||
|
||||
e) Attempt to suspend to RAM using the s2ram tool with the driver loaded (see:
|
||||
Documents/power/basic-pm-debugging.txt, 2). As far as the STR tests are
|
||||
concerned, it should not matter whether or not the driver is built as a module.
|
||||
e) Try the test modes of suspend (see: Documents/power/basic-pm-debugging.txt,
|
||||
2). [As far as the STR tests are concerned, it should not matter whether or
|
||||
not the driver is built as a module.]
|
||||
|
||||
f) Attempt to suspend to RAM using the s2ram tool with the driver loaded
|
||||
(see: Documents/power/basic-pm-debugging.txt, 2).
|
||||
|
||||
Each of the above tests should be repeated several times and the STD tests
|
||||
should be mixed with the STR tests. If any of them fails, the driver cannot be
|
||||
|
|
|
@ -28,6 +28,14 @@ PM_POST_HIBERNATION The system memory state has been restored from a
|
|||
hibernation. Device drivers' .resume() callbacks have
|
||||
been executed and tasks have been thawed.
|
||||
|
||||
PM_RESTORE_PREPARE The system is going to restore a hibernation image.
|
||||
If all goes well the restored kernel will issue a
|
||||
PM_POST_HIBERNATION notification.
|
||||
|
||||
PM_POST_RESTORE An error occurred during the hibernation restore.
|
||||
Device drivers' .resume() callbacks have been executed
|
||||
and tasks have been thawed.
|
||||
|
||||
PM_SUSPEND_PREPARE The system is preparing for a suspend.
|
||||
|
||||
PM_POST_SUSPEND The system has just resumed or an error occured during
|
||||
|
|
|
@ -14,7 +14,7 @@ are going to develop your own suspend/resume utilities.
|
|||
|
||||
The interface consists of a character device providing the open(),
|
||||
release(), read(), and write() operations as well as several ioctl()
|
||||
commands defined in kernel/power/power.h. The major and minor
|
||||
commands defined in include/linux/suspend_ioctls.h . The major and minor
|
||||
numbers of the device are, respectively, 10 and 231, and they can
|
||||
be read from /sys/class/misc/snapshot/dev.
|
||||
|
||||
|
@ -27,17 +27,17 @@ once at a time.
|
|||
The ioctl() commands recognized by the device are:
|
||||
|
||||
SNAPSHOT_FREEZE - freeze user space processes (the current process is
|
||||
not frozen); this is required for SNAPSHOT_ATOMIC_SNAPSHOT
|
||||
not frozen); this is required for SNAPSHOT_CREATE_IMAGE
|
||||
and SNAPSHOT_ATOMIC_RESTORE to succeed
|
||||
|
||||
SNAPSHOT_UNFREEZE - thaw user space processes frozen by SNAPSHOT_FREEZE
|
||||
|
||||
SNAPSHOT_ATOMIC_SNAPSHOT - create a snapshot of the system memory; the
|
||||
SNAPSHOT_CREATE_IMAGE - create a snapshot of the system memory; the
|
||||
last argument of ioctl() should be a pointer to an int variable,
|
||||
the value of which will indicate whether the call returned after
|
||||
creating the snapshot (1) or after restoring the system memory state
|
||||
from it (0) (after resume the system finds itself finishing the
|
||||
SNAPSHOT_ATOMIC_SNAPSHOT ioctl() again); after the snapshot
|
||||
SNAPSHOT_CREATE_IMAGE ioctl() again); after the snapshot
|
||||
has been created the read() operation can be used to transfer
|
||||
it out of the kernel
|
||||
|
||||
|
@ -49,39 +49,37 @@ SNAPSHOT_ATOMIC_RESTORE - restore the system memory state from the
|
|||
|
||||
SNAPSHOT_FREE - free memory allocated for the snapshot image
|
||||
|
||||
SNAPSHOT_SET_IMAGE_SIZE - set the preferred maximum size of the image
|
||||
SNAPSHOT_PREF_IMAGE_SIZE - set the preferred maximum size of the image
|
||||
(the kernel will do its best to ensure the image size will not exceed
|
||||
this number, but if it turns out to be impossible, the kernel will
|
||||
create the smallest image possible)
|
||||
|
||||
SNAPSHOT_AVAIL_SWAP - return the amount of available swap in bytes (the last
|
||||
argument should be a pointer to an unsigned int variable that will
|
||||
SNAPSHOT_GET_IMAGE_SIZE - return the actual size of the hibernation image
|
||||
|
||||
SNAPSHOT_AVAIL_SWAP_SIZE - return the amount of available swap in bytes (the
|
||||
last argument should be a pointer to an unsigned int variable that will
|
||||
contain the result if the call is successful).
|
||||
|
||||
SNAPSHOT_GET_SWAP_PAGE - allocate a swap page from the resume partition
|
||||
SNAPSHOT_ALLOC_SWAP_PAGE - allocate a swap page from the resume partition
|
||||
(the last argument should be a pointer to a loff_t variable that
|
||||
will contain the swap page offset if the call is successful)
|
||||
|
||||
SNAPSHOT_FREE_SWAP_PAGES - free all swap pages allocated with
|
||||
SNAPSHOT_GET_SWAP_PAGE
|
||||
|
||||
SNAPSHOT_SET_SWAP_FILE - set the resume partition (the last ioctl() argument
|
||||
should specify the device's major and minor numbers in the old
|
||||
two-byte format, as returned by the stat() function in the .st_rdev
|
||||
member of the stat structure)
|
||||
SNAPSHOT_FREE_SWAP_PAGES - free all swap pages allocated by
|
||||
SNAPSHOT_ALLOC_SWAP_PAGE
|
||||
|
||||
SNAPSHOT_SET_SWAP_AREA - set the resume partition and the offset (in <PAGE_SIZE>
|
||||
units) from the beginning of the partition at which the swap header is
|
||||
located (the last ioctl() argument should point to a struct
|
||||
resume_swap_area, as defined in kernel/power/power.h, containing the
|
||||
resume device specification, as for the SNAPSHOT_SET_SWAP_FILE ioctl(),
|
||||
and the offset); for swap partitions the offset is always 0, but it is
|
||||
different to zero for swap files (please see
|
||||
Documentation/swsusp-and-swap-files.txt for details).
|
||||
The SNAPSHOT_SET_SWAP_AREA ioctl() is considered as a replacement for
|
||||
SNAPSHOT_SET_SWAP_FILE which is regarded as obsolete. It is
|
||||
recommended to always use this call, because the code to set the resume
|
||||
partition may be removed from future kernels
|
||||
resume_swap_area, as defined in kernel/power/suspend_ioctls.h,
|
||||
containing the resume device specification and the offset); for swap
|
||||
partitions the offset is always 0, but it is different from zero for
|
||||
swap files (see Documentation/swsusp-and-swap-files.txt for details).
|
||||
|
||||
SNAPSHOT_PLATFORM_SUPPORT - enable/disable the hibernation platform support,
|
||||
depending on the argument value (enable, if the argument is nonzero)
|
||||
|
||||
SNAPSHOT_POWER_OFF - make the kernel transition the system to the hibernation
|
||||
state (eg. ACPI S4) using the platform (eg. ACPI) driver
|
||||
|
||||
SNAPSHOT_S2RAM - suspend to RAM; using this call causes the kernel to
|
||||
immediately enter the suspend-to-RAM state, so this call must always
|
||||
|
@ -93,24 +91,6 @@ SNAPSHOT_S2RAM - suspend to RAM; using this call causes the kernel to
|
|||
to resume the system from RAM if there's enough battery power or restore
|
||||
its state on the basis of the saved suspend image otherwise)
|
||||
|
||||
SNAPSHOT_PMOPS - enable the usage of the hibernation_ops->prepare,
|
||||
hibernate_ops->enter and hibernation_ops->finish methods (the in-kernel
|
||||
swsusp knows these as the "platform method") which are needed on many
|
||||
machines to (among others) speed up the resume by letting the BIOS skip
|
||||
some steps or to let the system recognise the correct state of the
|
||||
hardware after the resume (in particular on many machines this ensures
|
||||
that unplugged AC adapters get correctly detected and that kacpid does
|
||||
not run wild after the resume). The last ioctl() argument can take one
|
||||
of the three values, defined in kernel/power/power.h:
|
||||
PMOPS_PREPARE - make the kernel carry out the
|
||||
hibernation_ops->prepare() operation
|
||||
PMOPS_ENTER - make the kernel power off the system by calling
|
||||
hibernation_ops->enter()
|
||||
PMOPS_FINISH - make the kernel carry out the
|
||||
hibernation_ops->finish() operation
|
||||
Note that the actual constants are misnamed because they surface
|
||||
internal kernel implementation details that have changed.
|
||||
|
||||
The device's read() operation can be used to transfer the snapshot image from
|
||||
the kernel. It has the following limitations:
|
||||
- you cannot read() more than one virtual memory page at a time
|
||||
|
@ -122,7 +102,7 @@ The device's write() operation is used for uploading the system memory snapshot
|
|||
into the kernel. It has the same limitations as the read() operation.
|
||||
|
||||
The release() operation frees all memory allocated for the snapshot image
|
||||
and all swap pages allocated with SNAPSHOT_GET_SWAP_PAGE (if any).
|
||||
and all swap pages allocated with SNAPSHOT_ALLOC_SWAP_PAGE (if any).
|
||||
Thus it is not necessary to use either SNAPSHOT_FREE or
|
||||
SNAPSHOT_FREE_SWAP_PAGES before closing the device (in fact it will also
|
||||
unfreeze user space processes frozen by SNAPSHOT_UNFREEZE if they are
|
||||
|
@ -133,16 +113,12 @@ snapshot image from/to the kernel will use a swap parition, called the resume
|
|||
partition, or a swap file as storage space (if a swap file is used, the resume
|
||||
partition is the partition that holds this file). However, this is not really
|
||||
required, as they can use, for example, a special (blank) suspend partition or
|
||||
a file on a partition that is unmounted before SNAPSHOT_ATOMIC_SNAPSHOT and
|
||||
a file on a partition that is unmounted before SNAPSHOT_CREATE_IMAGE and
|
||||
mounted afterwards.
|
||||
|
||||
These utilities SHOULD NOT make any assumptions regarding the ordering of
|
||||
data within the snapshot image, except for the image header that MAY be
|
||||
assumed to start with an swsusp_info structure, as specified in
|
||||
kernel/power/power.h. This structure MAY be used by the userland utilities
|
||||
to obtain some information about the snapshot image, such as the size
|
||||
of the snapshot image, including the metadata and the header itself,
|
||||
contained in the .size member of swsusp_info.
|
||||
These utilities MUST NOT make any assumptions regarding the ordering of
|
||||
data within the snapshot image. The contents of the image are entirely owned
|
||||
by the kernel and its structure may be changed in future kernel releases.
|
||||
|
||||
The snapshot image MUST be written to the kernel unaltered (ie. all of the image
|
||||
data, metadata and header MUST be written in _exactly_ the same amount, form
|
||||
|
@ -159,7 +135,7 @@ means, such as checksums, to ensure the integrity of the snapshot image.
|
|||
The suspending and resuming utilities MUST lock themselves in memory,
|
||||
preferrably using mlockall(), before calling SNAPSHOT_FREEZE.
|
||||
|
||||
The suspending utility MUST check the value stored by SNAPSHOT_ATOMIC_SNAPSHOT
|
||||
The suspending utility MUST check the value stored by SNAPSHOT_CREATE_IMAGE
|
||||
in the memory location pointed to by the last argument of ioctl() and proceed
|
||||
in accordance with it:
|
||||
1. If the value is 1 (ie. the system memory snapshot has just been
|
||||
|
@ -173,7 +149,7 @@ in accordance with it:
|
|||
image has been saved.
|
||||
(b) The suspending utility SHOULD NOT attempt to perform any
|
||||
file system operations (including reads) on the file systems
|
||||
that were mounted before SNAPSHOT_ATOMIC_SNAPSHOT has been
|
||||
that were mounted before SNAPSHOT_CREATE_IMAGE has been
|
||||
called. However, it MAY mount a file system that was not
|
||||
mounted at that time and perform some operations on it (eg.
|
||||
use it for saving the image).
|
||||
|
|
|
@ -1030,6 +1030,9 @@ menu "Power management options"
|
|||
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
|
||||
endmenu
|
||||
|
||||
source "net/Kconfig"
|
||||
|
|
|
@ -52,7 +52,7 @@ static suspend_state_t target_state;
|
|||
/*
|
||||
* Called after processes are frozen, but before we shutdown devices.
|
||||
*/
|
||||
static int at91_pm_set_target(suspend_state_t state)
|
||||
static int at91_pm_begin(suspend_state_t state)
|
||||
{
|
||||
target_state = state;
|
||||
return 0;
|
||||
|
@ -202,11 +202,20 @@ error:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called right prior to thawing processes.
|
||||
*/
|
||||
static void at91_pm_end(void)
|
||||
{
|
||||
target_state = PM_SUSPEND_ON;
|
||||
}
|
||||
|
||||
|
||||
static struct platform_suspend_ops at91_pm_ops ={
|
||||
.valid = at91_pm_valid_state,
|
||||
.set_target = at91_pm_set_target,
|
||||
.begin = at91_pm_begin,
|
||||
.enter = at91_pm_enter,
|
||||
.end = at91_pm_end,
|
||||
};
|
||||
|
||||
static int __init at91_pm_init(void)
|
||||
|
|
|
@ -898,6 +898,10 @@ endmenu
|
|||
menu "Power management options"
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on !SMP
|
||||
|
||||
choice
|
||||
prompt "Select PM Wakeup Event Source"
|
||||
default PM_WAKEUP_GPIO_BY_SIC_IWR
|
||||
|
|
|
@ -352,6 +352,11 @@ source "drivers/pcmcia/Kconfig"
|
|||
# should probably wait a while.
|
||||
|
||||
menu "Power management options"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on !SMP
|
||||
|
||||
source kernel/power/Kconfig
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -2081,6 +2081,10 @@ endmenu
|
|||
|
||||
menu "Power management options"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on !SMP
|
||||
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -151,11 +151,25 @@ config DEFAULT_UIMAGE
|
|||
config REDBOOT
|
||||
bool
|
||||
|
||||
config PPC64_SWSUSP
|
||||
config HIBERNATE_32
|
||||
bool
|
||||
depends on PPC64 && (BROKEN || (PPC_PMAC64 && EXPERIMENTAL))
|
||||
depends on (PPC_PMAC && !SMP) || BROKEN
|
||||
default y
|
||||
|
||||
config HIBERNATE_64
|
||||
bool
|
||||
depends on BROKEN || (PPC_PMAC64 && EXPERIMENTAL)
|
||||
default y
|
||||
|
||||
config ARCH_HIBERNATION_POSSIBLE
|
||||
bool
|
||||
depends on (PPC64 && HIBERNATE_64) || (PPC32 && HIBERNATE_32)
|
||||
default y
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200
|
||||
|
||||
config PPC_DCR_NATIVE
|
||||
bool
|
||||
default n
|
||||
|
@ -391,6 +405,10 @@ config CMDLINE
|
|||
most cases you will need to specify the root device here.
|
||||
|
||||
if !44x || BROKEN
|
||||
config ARCH_WANTS_FREEZER_CONTROL
|
||||
def_bool y
|
||||
depends on ADB_PMU
|
||||
|
||||
source kernel/power/Kconfig
|
||||
endif
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ static int lite5200_pm_valid(suspend_state_t state)
|
|||
}
|
||||
}
|
||||
|
||||
static int lite5200_pm_set_target(suspend_state_t state)
|
||||
static int lite5200_pm_begin(suspend_state_t state)
|
||||
{
|
||||
if (lite5200_pm_valid(state)) {
|
||||
lite5200_pm_target_state = state;
|
||||
|
@ -219,12 +219,18 @@ static void lite5200_pm_finish(void)
|
|||
mpc52xx_pm_finish();
|
||||
}
|
||||
|
||||
static void lite5200_pm_end(void)
|
||||
{
|
||||
lite5200_pm_target_state = PM_SUSPEND_ON;
|
||||
}
|
||||
|
||||
static struct platform_suspend_ops lite5200_pm_ops = {
|
||||
.valid = lite5200_pm_valid,
|
||||
.set_target = lite5200_pm_set_target,
|
||||
.begin = lite5200_pm_begin,
|
||||
.prepare = lite5200_pm_prepare,
|
||||
.enter = lite5200_pm_enter,
|
||||
.finish = lite5200_pm_finish,
|
||||
.end = lite5200_pm_end,
|
||||
};
|
||||
|
||||
int __init lite5200_pm_init(void)
|
||||
|
|
|
@ -882,6 +882,10 @@ endmenu
|
|||
menu "Power management options (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && SYS_SUPPORTS_PM
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on !SMP
|
||||
|
||||
source kernel/power/Kconfig
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -112,6 +112,14 @@ config ARCH_SUPPORTS_OPROFILE
|
|||
|
||||
select HAVE_KVM
|
||||
|
||||
config ARCH_HIBERNATION_POSSIBLE
|
||||
def_bool y
|
||||
depends on !SMP || !X86_VOYAGER
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on !X86_VOYAGER
|
||||
|
||||
config ZONE_DMA32
|
||||
bool
|
||||
default X86_64
|
||||
|
|
|
@ -140,7 +140,12 @@ static void fix_processor_context(void)
|
|||
int cpu = smp_processor_id();
|
||||
struct tss_struct *t = &per_cpu(init_tss, cpu);
|
||||
|
||||
set_tss_desc(cpu,t); /* This just modifies memory; should not be necessary. But... This is necessary, because 386 hardware has concept of busy TSS or some similar stupidity. */
|
||||
/*
|
||||
* This just modifies memory; should not be necessary. But... This
|
||||
* is necessary, because 386 hardware has concept of busy TSS or some
|
||||
* similar stupidity.
|
||||
*/
|
||||
set_tss_desc(cpu, t);
|
||||
|
||||
get_cpu_gdt_table(cpu)[GDT_ENTRY_TSS].type = 9;
|
||||
|
||||
|
@ -160,7 +165,6 @@ static void fix_processor_context(void)
|
|||
loaddebug(¤t->thread, 6);
|
||||
loaddebug(¤t->thread, 7);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
|
|
|
@ -423,23 +423,23 @@ static void __init pagetable_init(void)
|
|||
paravirt_pagetable_setup_done(pgd_base);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_HIBERNATION) || defined(CONFIG_ACPI)
|
||||
#ifdef CONFIG_ACPI_SLEEP
|
||||
/*
|
||||
* Swap suspend & friends need this for resume because things like the intel-agp
|
||||
* ACPI suspend needs this for resume, because things like the intel-agp
|
||||
* driver might have split up a kernel 4MB mapping.
|
||||
*/
|
||||
char __nosavedata swsusp_pg_dir[PAGE_SIZE]
|
||||
char swsusp_pg_dir[PAGE_SIZE]
|
||||
__attribute__ ((aligned(PAGE_SIZE)));
|
||||
|
||||
static inline void save_pg_dir(void)
|
||||
{
|
||||
memcpy(swsusp_pg_dir, swapper_pg_dir, PAGE_SIZE);
|
||||
}
|
||||
#else
|
||||
#else /* !CONFIG_ACPI_SLEEP */
|
||||
static inline void save_pg_dir(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif /* !CONFIG_ACPI_SLEEP */
|
||||
|
||||
void zap_low_mappings(void)
|
||||
{
|
||||
|
|
|
@ -192,18 +192,13 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
|
|||
arg.type = ACPI_TYPE_INTEGER;
|
||||
arg.integer.value = sleep_state;
|
||||
|
||||
/* Run the _PTS and _GTS methods */
|
||||
/* Run the _PTS method */
|
||||
|
||||
status = acpi_evaluate_object(NULL, METHOD_NAME__PTS, &arg_list, NULL);
|
||||
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
status = acpi_evaluate_object(NULL, METHOD_NAME__GTS, &arg_list, NULL);
|
||||
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
/* Setup the argument to _SST */
|
||||
|
||||
switch (sleep_state) {
|
||||
|
@ -234,10 +229,6 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
|
|||
"While executing method _SST"));
|
||||
}
|
||||
|
||||
/* Disable/Clear all GPEs */
|
||||
|
||||
status = acpi_hw_disable_all_gpes();
|
||||
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
|
@ -262,6 +253,8 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
|
|||
struct acpi_bit_register_info *sleep_type_reg_info;
|
||||
struct acpi_bit_register_info *sleep_enable_reg_info;
|
||||
u32 in_value;
|
||||
struct acpi_object_list arg_list;
|
||||
union acpi_object arg;
|
||||
acpi_status status;
|
||||
|
||||
ACPI_FUNCTION_TRACE(acpi_enter_sleep_state);
|
||||
|
@ -307,6 +300,18 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
|
|||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
/* Execute the _GTS method */
|
||||
|
||||
arg_list.count = 1;
|
||||
arg_list.pointer = &arg;
|
||||
arg.type = ACPI_TYPE_INTEGER;
|
||||
arg.integer.value = sleep_state;
|
||||
|
||||
status = acpi_evaluate_object(NULL, METHOD_NAME__GTS, &arg_list, NULL);
|
||||
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
/* Get current value of PM1A control */
|
||||
|
||||
status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol);
|
||||
|
@ -473,17 +478,18 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
|
|||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: acpi_leave_sleep_state
|
||||
* FUNCTION: acpi_leave_sleep_state_prep
|
||||
*
|
||||
* PARAMETERS: sleep_state - Which sleep state we just exited
|
||||
* PARAMETERS: sleep_state - Which sleep state we are exiting
|
||||
*
|
||||
* RETURN: Status
|
||||
*
|
||||
* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
|
||||
* Called with interrupts ENABLED.
|
||||
* DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
|
||||
* sleep.
|
||||
* Called with interrupts DISABLED.
|
||||
*
|
||||
******************************************************************************/
|
||||
acpi_status acpi_leave_sleep_state(u8 sleep_state)
|
||||
acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
|
||||
{
|
||||
struct acpi_object_list arg_list;
|
||||
union acpi_object arg;
|
||||
|
@ -493,7 +499,7 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state)
|
|||
u32 PM1Acontrol;
|
||||
u32 PM1Bcontrol;
|
||||
|
||||
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
|
||||
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
|
||||
|
||||
/*
|
||||
* Set SLP_TYPE and SLP_EN to state S0.
|
||||
|
@ -540,6 +546,41 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state)
|
|||
}
|
||||
}
|
||||
|
||||
/* Execute the _BFS method */
|
||||
|
||||
arg_list.count = 1;
|
||||
arg_list.pointer = &arg;
|
||||
arg.type = ACPI_TYPE_INTEGER;
|
||||
arg.integer.value = sleep_state;
|
||||
|
||||
status = acpi_evaluate_object(NULL, METHOD_NAME__BFS, &arg_list, NULL);
|
||||
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||
ACPI_EXCEPTION((AE_INFO, status, "During Method _BFS"));
|
||||
}
|
||||
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: acpi_leave_sleep_state
|
||||
*
|
||||
* PARAMETERS: sleep_state - Which sleep state we just exited
|
||||
*
|
||||
* RETURN: Status
|
||||
*
|
||||
* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
|
||||
* Called with interrupts ENABLED.
|
||||
*
|
||||
******************************************************************************/
|
||||
acpi_status acpi_leave_sleep_state(u8 sleep_state)
|
||||
{
|
||||
struct acpi_object_list arg_list;
|
||||
union acpi_object arg;
|
||||
acpi_status status;
|
||||
|
||||
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
|
||||
|
||||
/* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
|
||||
|
||||
acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;
|
||||
|
@ -558,12 +599,6 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state)
|
|||
ACPI_EXCEPTION((AE_INFO, status, "During Method _SST"));
|
||||
}
|
||||
|
||||
arg.integer.value = sleep_state;
|
||||
status = acpi_evaluate_object(NULL, METHOD_NAME__BFS, &arg_list, NULL);
|
||||
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||
ACPI_EXCEPTION((AE_INFO, status, "During Method _BFS"));
|
||||
}
|
||||
|
||||
/*
|
||||
* GPEs must be enabled before _WAK is called as GPEs
|
||||
* might get fired there
|
||||
|
|
|
@ -26,9 +26,24 @@ u8 sleep_states[ACPI_S_STATE_COUNT];
|
|||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static u32 acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
static bool acpi_sleep_finish_wake_up;
|
||||
|
||||
/*
|
||||
* ACPI 2.0 and later want us to execute _PTS after suspending devices, so we
|
||||
* allow the user to request that behavior by using the 'acpi_new_pts_ordering'
|
||||
* kernel command line option that causes the following variable to be set.
|
||||
*/
|
||||
static bool new_pts_ordering;
|
||||
|
||||
static int __init acpi_new_pts_ordering(char *str)
|
||||
{
|
||||
new_pts_ordering = true;
|
||||
return 1;
|
||||
}
|
||||
__setup("acpi_new_pts_ordering", acpi_new_pts_ordering);
|
||||
#endif
|
||||
|
||||
int acpi_sleep_prepare(u32 acpi_state)
|
||||
static int acpi_sleep_prepare(u32 acpi_state)
|
||||
{
|
||||
#ifdef CONFIG_ACPI_SLEEP
|
||||
/* do we have a wakeup address for S2 and S3? */
|
||||
|
@ -44,6 +59,8 @@ int acpi_sleep_prepare(u32 acpi_state)
|
|||
ACPI_FLUSH_CPU_CACHE();
|
||||
acpi_enable_wakeup_device_prep(acpi_state);
|
||||
#endif
|
||||
printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n",
|
||||
acpi_state);
|
||||
acpi_enter_sleep_state_prep(acpi_state);
|
||||
return 0;
|
||||
}
|
||||
|
@ -63,17 +80,25 @@ static u32 acpi_suspend_states[] = {
|
|||
static int init_8259A_after_S1;
|
||||
|
||||
/**
|
||||
* acpi_pm_set_target - Set the target system sleep state to the state
|
||||
* acpi_pm_begin - Set the target system sleep state to the state
|
||||
* associated with given @pm_state, if supported.
|
||||
*/
|
||||
|
||||
static int acpi_pm_set_target(suspend_state_t pm_state)
|
||||
static int acpi_pm_begin(suspend_state_t pm_state)
|
||||
{
|
||||
u32 acpi_state = acpi_suspend_states[pm_state];
|
||||
int error = 0;
|
||||
|
||||
if (sleep_states[acpi_state]) {
|
||||
acpi_target_sleep_state = acpi_state;
|
||||
if (new_pts_ordering)
|
||||
return 0;
|
||||
|
||||
error = acpi_sleep_prepare(acpi_state);
|
||||
if (error)
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
else
|
||||
acpi_sleep_finish_wake_up = true;
|
||||
} else {
|
||||
printk(KERN_ERR "ACPI does not support this state: %d\n",
|
||||
pm_state);
|
||||
|
@ -91,12 +116,17 @@ static int acpi_pm_set_target(suspend_state_t pm_state)
|
|||
|
||||
static int acpi_pm_prepare(void)
|
||||
{
|
||||
if (new_pts_ordering) {
|
||||
int error = acpi_sleep_prepare(acpi_target_sleep_state);
|
||||
|
||||
if (error)
|
||||
if (error) {
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
|
||||
return error;
|
||||
}
|
||||
acpi_sleep_finish_wake_up = true;
|
||||
}
|
||||
|
||||
return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,11 +150,9 @@ static int acpi_pm_enter(suspend_state_t pm_state)
|
|||
if (acpi_state == ACPI_STATE_S3) {
|
||||
int error = acpi_save_state_mem();
|
||||
|
||||
if (error) {
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
acpi_enable_wakeup_device(acpi_state);
|
||||
|
@ -139,6 +167,9 @@ static int acpi_pm_enter(suspend_state_t pm_state)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Reprogram control registers and execute _BFS */
|
||||
acpi_leave_sleep_state_prep(acpi_state);
|
||||
|
||||
/* ACPI 3.0 specs (P62) says that it's the responsabilty
|
||||
* of the OSPM to clear the status bit [ implying that the
|
||||
* POWER_BUTTON event should not reach userspace ]
|
||||
|
@ -146,6 +177,13 @@ static int acpi_pm_enter(suspend_state_t pm_state)
|
|||
if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
|
||||
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
|
||||
|
||||
/*
|
||||
* Disable and clear GPE status before interrupt is enabled. Some GPEs
|
||||
* (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
|
||||
* acpi_leave_sleep_state will reenable specific GPEs later
|
||||
*/
|
||||
acpi_hw_disable_all_gpes();
|
||||
|
||||
local_irq_restore(flags);
|
||||
printk(KERN_DEBUG "Back to C!\n");
|
||||
|
||||
|
@ -157,7 +195,7 @@ static int acpi_pm_enter(suspend_state_t pm_state)
|
|||
}
|
||||
|
||||
/**
|
||||
* acpi_pm_finish - Finish up suspend sequence.
|
||||
* acpi_pm_finish - Instruct the platform to leave a sleep state.
|
||||
*
|
||||
* This is called after we wake back up (or if entering the sleep state
|
||||
* failed).
|
||||
|
@ -174,6 +212,7 @@ static void acpi_pm_finish(void)
|
|||
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
|
||||
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
acpi_sleep_finish_wake_up = false;
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
if (init_8259A_after_S1) {
|
||||
|
@ -183,6 +222,20 @@ static void acpi_pm_finish(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_pm_end - Finish up suspend sequence.
|
||||
*/
|
||||
|
||||
static void acpi_pm_end(void)
|
||||
{
|
||||
/*
|
||||
* This is necessary in case acpi_pm_finish() is not called directly
|
||||
* during a failing transition to a sleep state.
|
||||
*/
|
||||
if (acpi_sleep_finish_wake_up)
|
||||
acpi_pm_finish();
|
||||
}
|
||||
|
||||
static int acpi_pm_state_valid(suspend_state_t pm_state)
|
||||
{
|
||||
u32 acpi_state;
|
||||
|
@ -201,10 +254,11 @@ static int acpi_pm_state_valid(suspend_state_t pm_state)
|
|||
|
||||
static struct platform_suspend_ops acpi_pm_ops = {
|
||||
.valid = acpi_pm_state_valid,
|
||||
.set_target = acpi_pm_set_target,
|
||||
.begin = acpi_pm_begin,
|
||||
.prepare = acpi_pm_prepare,
|
||||
.enter = acpi_pm_enter,
|
||||
.finish = acpi_pm_finish,
|
||||
.end = acpi_pm_end,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -229,15 +283,36 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
|
|||
#endif /* CONFIG_SUSPEND */
|
||||
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
static int acpi_hibernation_start(void)
|
||||
static int acpi_hibernation_begin(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
acpi_target_sleep_state = ACPI_STATE_S4;
|
||||
if (new_pts_ordering)
|
||||
return 0;
|
||||
|
||||
error = acpi_sleep_prepare(ACPI_STATE_S4);
|
||||
if (error)
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
else
|
||||
acpi_sleep_finish_wake_up = true;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int acpi_hibernation_prepare(void)
|
||||
{
|
||||
return acpi_sleep_prepare(ACPI_STATE_S4);
|
||||
if (new_pts_ordering) {
|
||||
int error = acpi_sleep_prepare(ACPI_STATE_S4);
|
||||
|
||||
if (error) {
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
return error;
|
||||
}
|
||||
acpi_sleep_finish_wake_up = true;
|
||||
}
|
||||
|
||||
return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
|
||||
}
|
||||
|
||||
static int acpi_hibernation_enter(void)
|
||||
|
@ -251,6 +326,8 @@ static int acpi_hibernation_enter(void)
|
|||
acpi_enable_wakeup_device(ACPI_STATE_S4);
|
||||
/* This shouldn't return. If it returns, we have a problem */
|
||||
status = acpi_enter_sleep_state(ACPI_STATE_S4);
|
||||
/* Reprogram control registers and execute _BFS */
|
||||
acpi_leave_sleep_state_prep(ACPI_STATE_S4);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
|
||||
|
@ -263,15 +340,12 @@ static void acpi_hibernation_leave(void)
|
|||
* enable it here.
|
||||
*/
|
||||
acpi_enable();
|
||||
/* Reprogram control registers and execute _BFS */
|
||||
acpi_leave_sleep_state_prep(ACPI_STATE_S4);
|
||||
}
|
||||
|
||||
static void acpi_hibernation_finish(void)
|
||||
{
|
||||
/*
|
||||
* If ACPI is not enabled by the BIOS and the boot kernel, we need to
|
||||
* enable it here.
|
||||
*/
|
||||
acpi_enable();
|
||||
acpi_disable_wakeup_device(ACPI_STATE_S4);
|
||||
acpi_leave_sleep_state(ACPI_STATE_S4);
|
||||
|
||||
|
@ -279,6 +353,17 @@ static void acpi_hibernation_finish(void)
|
|||
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
|
||||
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
acpi_sleep_finish_wake_up = false;
|
||||
}
|
||||
|
||||
static void acpi_hibernation_end(void)
|
||||
{
|
||||
/*
|
||||
* This is necessary in case acpi_hibernation_finish() is not called
|
||||
* directly during a failing transition to the sleep state.
|
||||
*/
|
||||
if (acpi_sleep_finish_wake_up)
|
||||
acpi_hibernation_finish();
|
||||
}
|
||||
|
||||
static int acpi_hibernation_pre_restore(void)
|
||||
|
@ -296,7 +381,8 @@ static void acpi_hibernation_restore_cleanup(void)
|
|||
}
|
||||
|
||||
static struct platform_hibernation_ops acpi_hibernation_ops = {
|
||||
.start = acpi_hibernation_start,
|
||||
.begin = acpi_hibernation_begin,
|
||||
.end = acpi_hibernation_end,
|
||||
.pre_snapshot = acpi_hibernation_prepare,
|
||||
.finish = acpi_hibernation_finish,
|
||||
.prepare = acpi_hibernation_prepare,
|
||||
|
@ -403,6 +489,7 @@ static void acpi_power_off_prepare(void)
|
|||
{
|
||||
/* Prepare to power off the system */
|
||||
acpi_sleep_prepare(ACPI_STATE_S5);
|
||||
acpi_hw_disable_all_gpes();
|
||||
}
|
||||
|
||||
static void acpi_power_off(void)
|
||||
|
|
|
@ -5,5 +5,3 @@ extern int acpi_suspend (u32 state);
|
|||
extern void acpi_enable_wakeup_device_prep(u8 sleep_state);
|
||||
extern void acpi_enable_wakeup_device(u8 sleep_state);
|
||||
extern void acpi_disable_wakeup_device(u8 sleep_state);
|
||||
|
||||
extern int acpi_sleep_prepare(u32 acpi_state);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
obj-$(CONFIG_PM) += sysfs.o
|
||||
obj-$(CONFIG_PM_SLEEP) += main.o
|
||||
obj-$(CONFIG_PM_TRACE) += trace.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG
|
||||
|
|
|
@ -335,6 +335,8 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state);
|
|||
|
||||
acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void);
|
||||
|
||||
acpi_status acpi_leave_sleep_state_prep(u8 sleep_state);
|
||||
|
||||
acpi_status acpi_leave_sleep_state(u8 sleep_state);
|
||||
|
||||
#endif /* __ACXFACE_H__ */
|
||||
|
|
|
@ -143,6 +143,7 @@ header-y += snmp.h
|
|||
header-y += sockios.h
|
||||
header-y += som.h
|
||||
header-y += sound.h
|
||||
header-y += suspend_ioctls.h
|
||||
header-y += taskstats.h
|
||||
header-y += telephony.h
|
||||
header-y += termios.h
|
||||
|
|
|
@ -228,6 +228,8 @@ static inline int notifier_to_errno(int ret)
|
|||
#define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */
|
||||
#define PM_SUSPEND_PREPARE 0x0003 /* Going to suspend the system */
|
||||
#define PM_POST_SUSPEND 0x0004 /* Suspend finished */
|
||||
#define PM_RESTORE_PREPARE 0x0005 /* Going to restore a saved image */
|
||||
#define PM_POST_RESTORE 0x0006 /* Restore failed */
|
||||
|
||||
/* Console keyboard events.
|
||||
* Note: KBD_KEYCODE is always sent before KBD_UNBOUND_KEYCODE, KBD_UNICODE and
|
||||
|
|
|
@ -38,18 +38,16 @@ typedef int __bitwise suspend_state_t;
|
|||
* There is the %suspend_valid_only_mem function available that can be
|
||||
* assigned to this if the platform only supports mem sleep.
|
||||
*
|
||||
* @set_target: Tell the platform which system sleep state is going to be
|
||||
* entered.
|
||||
* @set_target() is executed right prior to suspending devices. The
|
||||
* information conveyed to the platform code by @set_target() should be
|
||||
* disregarded by the platform as soon as @finish() is executed and if
|
||||
* @prepare() fails. If @set_target() fails (ie. returns nonzero),
|
||||
* @begin: Initialise a transition to given system sleep state.
|
||||
* @begin() is executed right prior to suspending devices. The information
|
||||
* conveyed to the platform code by @begin() should be disregarded by it as
|
||||
* soon as @end() is executed. If @begin() fails (ie. returns nonzero),
|
||||
* @prepare(), @enter() and @finish() will not be called by the PM core.
|
||||
* This callback is optional. However, if it is implemented, the argument
|
||||
* passed to @enter() is meaningless and should be ignored.
|
||||
* passed to @enter() is redundant and should be ignored.
|
||||
*
|
||||
* @prepare: Prepare the platform for entering the system sleep state indicated
|
||||
* by @set_target().
|
||||
* by @begin().
|
||||
* @prepare() is called right after devices have been suspended (ie. the
|
||||
* appropriate .suspend() method has been executed for each device) and
|
||||
* before the nonboot CPUs are disabled (it is executed with IRQs enabled).
|
||||
|
@ -57,8 +55,8 @@ typedef int __bitwise suspend_state_t;
|
|||
* error code otherwise, in which case the system cannot enter the desired
|
||||
* sleep state (@enter() and @finish() will not be called in that case).
|
||||
*
|
||||
* @enter: Enter the system sleep state indicated by @set_target() or
|
||||
* represented by the argument if @set_target() is not implemented.
|
||||
* @enter: Enter the system sleep state indicated by @begin() or represented by
|
||||
* the argument if @begin() is not implemented.
|
||||
* This callback is mandatory. It returns 0 on success or a negative
|
||||
* error code otherwise, in which case the system cannot enter the desired
|
||||
* sleep state.
|
||||
|
@ -69,13 +67,22 @@ typedef int __bitwise suspend_state_t;
|
|||
* This callback is optional, but should be implemented by the platforms
|
||||
* that implement @prepare(). If implemented, it is always called after
|
||||
* @enter() (even if @enter() fails).
|
||||
*
|
||||
* @end: Called by the PM core right after resuming devices, to indicate to
|
||||
* the platform that the system has returned to the working state or
|
||||
* the transition to the sleep state has been aborted.
|
||||
* This callback is optional, but should be implemented by the platforms
|
||||
* that implement @begin(), but platforms implementing @begin() should
|
||||
* also provide a @end() which cleans up transitions aborted before
|
||||
* @enter().
|
||||
*/
|
||||
struct platform_suspend_ops {
|
||||
int (*valid)(suspend_state_t state);
|
||||
int (*set_target)(suspend_state_t state);
|
||||
int (*begin)(suspend_state_t state);
|
||||
int (*prepare)(void);
|
||||
int (*enter)(suspend_state_t state);
|
||||
void (*finish)(void);
|
||||
void (*end)(void);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
|
@ -129,14 +136,17 @@ extern void mark_free_pages(struct zone *zone);
|
|||
/**
|
||||
* struct platform_hibernation_ops - hibernation platform support
|
||||
*
|
||||
* The methods in this structure allow a platform to override the default
|
||||
* mechanism of shutting down the machine during a hibernation transition.
|
||||
* The methods in this structure allow a platform to carry out special
|
||||
* operations required by it during a hibernation transition.
|
||||
*
|
||||
* All three methods must be assigned.
|
||||
* All the methods below must be implemented.
|
||||
*
|
||||
* @start: Tell the platform driver that we're starting hibernation.
|
||||
* @begin: Tell the platform driver that we're starting hibernation.
|
||||
* Called right after shrinking memory and before freezing devices.
|
||||
*
|
||||
* @end: Called by the PM core right after resuming devices, to indicate to
|
||||
* the platform that the system has returned to the working state.
|
||||
*
|
||||
* @pre_snapshot: Prepare the platform for creating the hibernation image.
|
||||
* Called right after devices have been frozen and before the nonboot
|
||||
* CPUs are disabled (runs with IRQs on).
|
||||
|
@ -171,7 +181,8 @@ extern void mark_free_pages(struct zone *zone);
|
|||
* thawing devices (runs with IRQs on).
|
||||
*/
|
||||
struct platform_hibernation_ops {
|
||||
int (*start)(void);
|
||||
int (*begin)(void);
|
||||
void (*end)(void);
|
||||
int (*pre_snapshot)(void);
|
||||
void (*finish)(void);
|
||||
int (*prepare)(void);
|
||||
|
@ -213,17 +224,8 @@ void save_processor_state(void);
|
|||
void restore_processor_state(void);
|
||||
|
||||
/* kernel/power/main.c */
|
||||
extern struct blocking_notifier_head pm_chain_head;
|
||||
|
||||
static inline int register_pm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&pm_chain_head, nb);
|
||||
}
|
||||
|
||||
static inline int unregister_pm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&pm_chain_head, nb);
|
||||
}
|
||||
extern int register_pm_notifier(struct notifier_block *nb);
|
||||
extern int unregister_pm_notifier(struct notifier_block *nb);
|
||||
|
||||
#define pm_notifier(fn, pri) { \
|
||||
static struct notifier_block fn##_nb = \
|
||||
|
|
32
include/linux/suspend_ioctls.h
Normal file
32
include/linux/suspend_ioctls.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef _LINUX_SUSPEND_IOCTLS_H
|
||||
#define _LINUX_SUSPEND_IOCTLS_H
|
||||
|
||||
/*
|
||||
* This structure is used to pass the values needed for the identification
|
||||
* of the resume swap area from a user space to the kernel via the
|
||||
* SNAPSHOT_SET_SWAP_AREA ioctl
|
||||
*/
|
||||
struct resume_swap_area {
|
||||
loff_t offset;
|
||||
u_int32_t dev;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SNAPSHOT_IOC_MAGIC '3'
|
||||
#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
|
||||
#define SNAPSHOT_UNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
|
||||
#define SNAPSHOT_ATOMIC_RESTORE _IO(SNAPSHOT_IOC_MAGIC, 4)
|
||||
#define SNAPSHOT_FREE _IO(SNAPSHOT_IOC_MAGIC, 5)
|
||||
#define SNAPSHOT_FREE_SWAP_PAGES _IO(SNAPSHOT_IOC_MAGIC, 9)
|
||||
#define SNAPSHOT_S2RAM _IO(SNAPSHOT_IOC_MAGIC, 11)
|
||||
#define SNAPSHOT_SET_SWAP_AREA _IOW(SNAPSHOT_IOC_MAGIC, 13, \
|
||||
struct resume_swap_area)
|
||||
#define SNAPSHOT_GET_IMAGE_SIZE _IOR(SNAPSHOT_IOC_MAGIC, 14, loff_t)
|
||||
#define SNAPSHOT_PLATFORM_SUPPORT _IO(SNAPSHOT_IOC_MAGIC, 15)
|
||||
#define SNAPSHOT_POWER_OFF _IO(SNAPSHOT_IOC_MAGIC, 16)
|
||||
#define SNAPSHOT_CREATE_IMAGE _IOW(SNAPSHOT_IOC_MAGIC, 17, int)
|
||||
#define SNAPSHOT_PREF_IMAGE_SIZE _IO(SNAPSHOT_IOC_MAGIC, 18)
|
||||
#define SNAPSHOT_AVAIL_SWAP_SIZE _IOR(SNAPSHOT_IOC_MAGIC, 19, loff_t)
|
||||
#define SNAPSHOT_ALLOC_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 20, loff_t)
|
||||
#define SNAPSHOT_IOC_MAXNR 20
|
||||
|
||||
#endif /* _LINUX_SUSPEND_IOCTLS_H */
|
|
@ -44,9 +44,30 @@ config PM_VERBOSE
|
|||
---help---
|
||||
This option enables verbose messages from the Power Management code.
|
||||
|
||||
config CAN_PM_TRACE
|
||||
def_bool y
|
||||
depends on PM_DEBUG && PM_SLEEP && EXPERIMENTAL
|
||||
|
||||
config PM_TRACE
|
||||
bool
|
||||
help
|
||||
This enables code to save the last PM event point across
|
||||
reboot. The architecture needs to support this, x86 for
|
||||
example does by saving things in the RTC, see below.
|
||||
|
||||
The architecture specific code must provide the extern
|
||||
functions from <linux/resume-trace.h> as well as the
|
||||
<asm/resume-trace.h> header with a TRACE_RESUME() macro.
|
||||
|
||||
The way the information is presented is architecture-
|
||||
dependent, x86 will print the information during a
|
||||
late_initcall.
|
||||
|
||||
config PM_TRACE_RTC
|
||||
bool "Suspend/resume event tracing"
|
||||
depends on PM_DEBUG && X86 && PM_SLEEP && EXPERIMENTAL
|
||||
depends on CAN_PM_TRACE
|
||||
depends on X86
|
||||
select PM_TRACE
|
||||
default n
|
||||
---help---
|
||||
This enables some cheesy code to save the last PM event point in the
|
||||
|
@ -63,7 +84,8 @@ config PM_TRACE
|
|||
|
||||
config PM_SLEEP_SMP
|
||||
bool
|
||||
depends on SUSPEND_SMP_POSSIBLE || HIBERNATION_SMP_POSSIBLE
|
||||
depends on SMP
|
||||
depends on ARCH_SUSPEND_POSSIBLE || ARCH_HIBERNATION_POSSIBLE
|
||||
depends on PM_SLEEP
|
||||
select HOTPLUG_CPU
|
||||
default y
|
||||
|
@ -73,46 +95,29 @@ config PM_SLEEP
|
|||
depends on SUSPEND || HIBERNATION
|
||||
default y
|
||||
|
||||
config SUSPEND_UP_POSSIBLE
|
||||
bool
|
||||
depends on (X86 && !X86_VOYAGER) || PPC || ARM || BLACKFIN || MIPS \
|
||||
|| SUPERH || FRV
|
||||
depends on !SMP
|
||||
default y
|
||||
|
||||
config SUSPEND_SMP_POSSIBLE
|
||||
bool
|
||||
depends on (X86 && !X86_VOYAGER) \
|
||||
|| (PPC && (PPC_PSERIES || PPC_PMAC)) || ARM
|
||||
depends on SMP
|
||||
default y
|
||||
|
||||
config SUSPEND
|
||||
bool "Suspend to RAM and standby"
|
||||
depends on PM
|
||||
depends on SUSPEND_UP_POSSIBLE || SUSPEND_SMP_POSSIBLE
|
||||
depends on PM && ARCH_SUSPEND_POSSIBLE
|
||||
default y
|
||||
---help---
|
||||
Allow the system to enter sleep states in which main memory is
|
||||
powered and thus its contents are preserved, such as the
|
||||
suspend-to-RAM state (i.e. the ACPI S3 state).
|
||||
suspend-to-RAM state (e.g. the ACPI S3 state).
|
||||
|
||||
config HIBERNATION_UP_POSSIBLE
|
||||
bool
|
||||
depends on X86 || PPC64_SWSUSP || PPC32
|
||||
depends on !SMP
|
||||
config SUSPEND_FREEZER
|
||||
bool "Enable freezer for suspend to RAM/standby" \
|
||||
if ARCH_WANTS_FREEZER_CONTROL || BROKEN
|
||||
depends on SUSPEND
|
||||
default y
|
||||
help
|
||||
This allows you to turn off the freezer for suspend. If this is
|
||||
done, no tasks are frozen for suspend to RAM/standby.
|
||||
|
||||
config HIBERNATION_SMP_POSSIBLE
|
||||
bool
|
||||
depends on (X86 && !X86_VOYAGER) || PPC64_SWSUSP
|
||||
depends on SMP
|
||||
default y
|
||||
Turning OFF this setting is NOT recommended! If in doubt, say Y.
|
||||
|
||||
config HIBERNATION
|
||||
bool "Hibernation (aka 'suspend to disk')"
|
||||
depends on PM && SWAP
|
||||
depends on HIBERNATION_UP_POSSIBLE || HIBERNATION_SMP_POSSIBLE
|
||||
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
|
||||
---help---
|
||||
Enable the suspend to disk (STD) functionality, which is usually
|
||||
called "hibernation" in user interfaces. STD checkpoints the
|
||||
|
|
|
@ -54,8 +54,8 @@ static struct platform_hibernation_ops *hibernation_ops;
|
|||
|
||||
void hibernation_set_ops(struct platform_hibernation_ops *ops)
|
||||
{
|
||||
if (ops && !(ops->start && ops->pre_snapshot && ops->finish
|
||||
&& ops->prepare && ops->enter && ops->pre_restore
|
||||
if (ops && !(ops->begin && ops->end && ops->pre_snapshot
|
||||
&& ops->prepare && ops->finish && ops->enter && ops->pre_restore
|
||||
&& ops->restore_cleanup)) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
|
@ -70,15 +70,55 @@ void hibernation_set_ops(struct platform_hibernation_ops *ops)
|
|||
mutex_unlock(&pm_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEBUG
|
||||
static void hibernation_debug_sleep(void)
|
||||
{
|
||||
printk(KERN_INFO "hibernation debug: Waiting for 5 seconds.\n");
|
||||
mdelay(5000);
|
||||
}
|
||||
|
||||
static int hibernation_testmode(int mode)
|
||||
{
|
||||
if (hibernation_mode == mode) {
|
||||
hibernation_debug_sleep();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hibernation_test(int level)
|
||||
{
|
||||
if (pm_test_level == level) {
|
||||
hibernation_debug_sleep();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else /* !CONFIG_PM_DEBUG */
|
||||
static int hibernation_testmode(int mode) { return 0; }
|
||||
static int hibernation_test(int level) { return 0; }
|
||||
#endif /* !CONFIG_PM_DEBUG */
|
||||
|
||||
/**
|
||||
* platform_start - tell the platform driver that we're starting
|
||||
* platform_begin - tell the platform driver that we're starting
|
||||
* hibernation
|
||||
*/
|
||||
|
||||
static int platform_start(int platform_mode)
|
||||
static int platform_begin(int platform_mode)
|
||||
{
|
||||
return (platform_mode && hibernation_ops) ?
|
||||
hibernation_ops->start() : 0;
|
||||
hibernation_ops->begin() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* platform_end - tell the platform driver that we've entered the
|
||||
* working state
|
||||
*/
|
||||
|
||||
static void platform_end(int platform_mode)
|
||||
{
|
||||
if (platform_mode && hibernation_ops)
|
||||
hibernation_ops->end();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,19 +202,25 @@ int create_image(int platform_mode)
|
|||
*/
|
||||
error = device_power_down(PMSG_FREEZE);
|
||||
if (error) {
|
||||
printk(KERN_ERR "Some devices failed to power down, "
|
||||
KERN_ERR "aborting suspend\n");
|
||||
printk(KERN_ERR "PM: Some devices failed to power down, "
|
||||
"aborting hibernation\n");
|
||||
goto Enable_irqs;
|
||||
}
|
||||
|
||||
if (hibernation_test(TEST_CORE))
|
||||
goto Power_up;
|
||||
|
||||
in_suspend = 1;
|
||||
save_processor_state();
|
||||
error = swsusp_arch_suspend();
|
||||
if (error)
|
||||
printk(KERN_ERR "Error %d while creating the image\n", error);
|
||||
printk(KERN_ERR "PM: Error %d creating hibernation image\n",
|
||||
error);
|
||||
/* Restore control flow magically appears here */
|
||||
restore_processor_state();
|
||||
if (!in_suspend)
|
||||
platform_leave(platform_mode);
|
||||
Power_up:
|
||||
/* NOTE: device_power_up() is just a resume() for devices
|
||||
* that suspended with irqs off ... no overall powerup.
|
||||
*/
|
||||
|
@ -202,36 +248,90 @@ int hibernation_snapshot(int platform_mode)
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = platform_start(platform_mode);
|
||||
error = platform_begin(platform_mode);
|
||||
if (error)
|
||||
return error;
|
||||
goto Close;
|
||||
|
||||
suspend_console();
|
||||
error = device_suspend(PMSG_FREEZE);
|
||||
if (error)
|
||||
goto Resume_console;
|
||||
|
||||
error = platform_pre_snapshot(platform_mode);
|
||||
if (error)
|
||||
if (hibernation_test(TEST_DEVICES))
|
||||
goto Resume_devices;
|
||||
|
||||
error = platform_pre_snapshot(platform_mode);
|
||||
if (error || hibernation_test(TEST_PLATFORM))
|
||||
goto Finish;
|
||||
|
||||
error = disable_nonboot_cpus();
|
||||
if (!error) {
|
||||
if (hibernation_mode != HIBERNATION_TEST) {
|
||||
in_suspend = 1;
|
||||
if (hibernation_test(TEST_CPUS))
|
||||
goto Enable_cpus;
|
||||
|
||||
if (hibernation_testmode(HIBERNATION_TEST))
|
||||
goto Enable_cpus;
|
||||
|
||||
error = create_image(platform_mode);
|
||||
/* Control returns here after successful restore */
|
||||
} else {
|
||||
printk("swsusp debug: Waiting for 5 seconds.\n");
|
||||
mdelay(5000);
|
||||
}
|
||||
}
|
||||
Enable_cpus:
|
||||
enable_nonboot_cpus();
|
||||
Resume_devices:
|
||||
Finish:
|
||||
platform_finish(platform_mode);
|
||||
Resume_devices:
|
||||
device_resume();
|
||||
Resume_console:
|
||||
resume_console();
|
||||
Close:
|
||||
platform_end(platform_mode);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* resume_target_kernel - prepare devices that need to be suspended with
|
||||
* interrupts off, restore the contents of highmem that have not been
|
||||
* restored yet from the image and run the low level code that will restore
|
||||
* the remaining contents of memory and switch to the just restored target
|
||||
* kernel.
|
||||
*/
|
||||
|
||||
static int resume_target_kernel(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
local_irq_disable();
|
||||
error = device_power_down(PMSG_PRETHAW);
|
||||
if (error) {
|
||||
printk(KERN_ERR "PM: Some devices failed to power down, "
|
||||
"aborting resume\n");
|
||||
goto Enable_irqs;
|
||||
}
|
||||
/* We'll ignore saved state, but this gets preempt count (etc) right */
|
||||
save_processor_state();
|
||||
error = restore_highmem();
|
||||
if (!error) {
|
||||
error = swsusp_arch_resume();
|
||||
/*
|
||||
* The code below is only ever reached in case of a failure.
|
||||
* Otherwise execution continues at place where
|
||||
* swsusp_arch_suspend() was called
|
||||
*/
|
||||
BUG_ON(!error);
|
||||
/* This call to restore_highmem() undos the previous one */
|
||||
restore_highmem();
|
||||
}
|
||||
/*
|
||||
* The only reason why swsusp_arch_resume() can fail is memory being
|
||||
* very tight, so we have to free it as soon as we can to avoid
|
||||
* subsequent failures
|
||||
*/
|
||||
swsusp_free();
|
||||
restore_processor_state();
|
||||
touch_softlockup_watchdog();
|
||||
device_power_up();
|
||||
Enable_irqs:
|
||||
local_irq_enable();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -258,7 +358,7 @@ int hibernation_restore(int platform_mode)
|
|||
if (!error) {
|
||||
error = disable_nonboot_cpus();
|
||||
if (!error)
|
||||
error = swsusp_resume();
|
||||
error = resume_target_kernel();
|
||||
enable_nonboot_cpus();
|
||||
}
|
||||
platform_restore_cleanup(platform_mode);
|
||||
|
@ -286,9 +386,9 @@ int hibernation_platform_enter(void)
|
|||
* hibernation_ops->finish() before saving the image, so we should let
|
||||
* the firmware know that we're going to enter the sleep state after all
|
||||
*/
|
||||
error = hibernation_ops->start();
|
||||
error = hibernation_ops->begin();
|
||||
if (error)
|
||||
return error;
|
||||
goto Close;
|
||||
|
||||
suspend_console();
|
||||
error = device_suspend(PMSG_SUSPEND);
|
||||
|
@ -322,6 +422,8 @@ int hibernation_platform_enter(void)
|
|||
device_resume();
|
||||
Resume_console:
|
||||
resume_console();
|
||||
Close:
|
||||
hibernation_ops->end();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -352,24 +454,17 @@ static void power_down(void)
|
|||
* Valid image is on the disk, if we continue we risk serious data
|
||||
* corruption after resume.
|
||||
*/
|
||||
printk(KERN_CRIT "Please power me down manually\n");
|
||||
printk(KERN_CRIT "PM: Please power down manually\n");
|
||||
while(1);
|
||||
}
|
||||
|
||||
static void unprepare_processes(void)
|
||||
{
|
||||
thaw_processes();
|
||||
pm_restore_console();
|
||||
}
|
||||
|
||||
static int prepare_processes(void)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
pm_prepare_console();
|
||||
if (freeze_processes()) {
|
||||
error = -EBUSY;
|
||||
unprepare_processes();
|
||||
thaw_processes();
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
@ -389,6 +484,7 @@ int hibernate(void)
|
|||
goto Unlock;
|
||||
}
|
||||
|
||||
pm_prepare_console();
|
||||
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
|
||||
if (error)
|
||||
goto Exit;
|
||||
|
@ -398,7 +494,7 @@ int hibernate(void)
|
|||
if (error)
|
||||
goto Exit;
|
||||
|
||||
printk("Syncing filesystems ... ");
|
||||
printk(KERN_INFO "PM: Syncing filesystems ... ");
|
||||
sys_sync();
|
||||
printk("done.\n");
|
||||
|
||||
|
@ -406,11 +502,12 @@ int hibernate(void)
|
|||
if (error)
|
||||
goto Finish;
|
||||
|
||||
if (hibernation_mode == HIBERNATION_TESTPROC) {
|
||||
printk("swsusp debug: Waiting for 5 seconds.\n");
|
||||
mdelay(5000);
|
||||
if (hibernation_test(TEST_FREEZER))
|
||||
goto Thaw;
|
||||
}
|
||||
|
||||
if (hibernation_testmode(HIBERNATION_TESTPROC))
|
||||
goto Thaw;
|
||||
|
||||
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
|
||||
if (in_suspend && !error) {
|
||||
unsigned int flags = 0;
|
||||
|
@ -427,11 +524,12 @@ int hibernate(void)
|
|||
swsusp_free();
|
||||
}
|
||||
Thaw:
|
||||
unprepare_processes();
|
||||
thaw_processes();
|
||||
Finish:
|
||||
free_basic_memory_bitmaps();
|
||||
Exit:
|
||||
pm_notifier_call_chain(PM_POST_HIBERNATION);
|
||||
pm_restore_console();
|
||||
atomic_inc(&snapshot_device_available);
|
||||
Unlock:
|
||||
mutex_unlock(&pm_mutex);
|
||||
|
@ -473,22 +571,23 @@ static int software_resume(void)
|
|||
return -ENOENT;
|
||||
}
|
||||
swsusp_resume_device = name_to_dev_t(resume_file);
|
||||
pr_debug("swsusp: Resume From Partition %s\n", resume_file);
|
||||
pr_debug("PM: Resume from partition %s\n", resume_file);
|
||||
} else {
|
||||
pr_debug("swsusp: Resume From Partition %d:%d\n",
|
||||
MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device));
|
||||
pr_debug("PM: Resume from partition %d:%d\n",
|
||||
MAJOR(swsusp_resume_device),
|
||||
MINOR(swsusp_resume_device));
|
||||
}
|
||||
|
||||
if (noresume) {
|
||||
/**
|
||||
* FIXME: If noresume is specified, we need to find the partition
|
||||
* and reset it back to normal swap space.
|
||||
* FIXME: If noresume is specified, we need to find the
|
||||
* partition and reset it back to normal swap space.
|
||||
*/
|
||||
mutex_unlock(&pm_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("PM: Checking swsusp image.\n");
|
||||
pr_debug("PM: Checking hibernation image.\n");
|
||||
error = swsusp_check();
|
||||
if (error)
|
||||
goto Unlock;
|
||||
|
@ -499,6 +598,11 @@ static int software_resume(void)
|
|||
goto Unlock;
|
||||
}
|
||||
|
||||
pm_prepare_console();
|
||||
error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
|
||||
if (error)
|
||||
goto Finish;
|
||||
|
||||
error = create_basic_memory_bitmaps();
|
||||
if (error)
|
||||
goto Finish;
|
||||
|
@ -510,7 +614,7 @@ static int software_resume(void)
|
|||
goto Done;
|
||||
}
|
||||
|
||||
pr_debug("PM: Reading swsusp image.\n");
|
||||
pr_debug("PM: Reading hibernation image.\n");
|
||||
|
||||
error = swsusp_read(&flags);
|
||||
if (!error)
|
||||
|
@ -518,10 +622,12 @@ static int software_resume(void)
|
|||
|
||||
printk(KERN_ERR "PM: Restore failed, recovering.\n");
|
||||
swsusp_free();
|
||||
unprepare_processes();
|
||||
thaw_processes();
|
||||
Done:
|
||||
free_basic_memory_bitmaps();
|
||||
Finish:
|
||||
pm_notifier_call_chain(PM_POST_RESTORE);
|
||||
pm_restore_console();
|
||||
atomic_inc(&snapshot_device_available);
|
||||
/* For success case, the suspend path will release the lock */
|
||||
Unlock:
|
||||
|
@ -636,7 +742,7 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
error = -EINVAL;
|
||||
|
||||
if (!error)
|
||||
pr_debug("PM: suspend-to-disk mode set to '%s'\n",
|
||||
pr_debug("PM: Hibernation mode set to '%s'\n",
|
||||
hibernation_modes[mode]);
|
||||
mutex_unlock(&pm_mutex);
|
||||
return error ? error : n;
|
||||
|
@ -668,7 +774,7 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
mutex_lock(&pm_mutex);
|
||||
swsusp_resume_device = res;
|
||||
mutex_unlock(&pm_mutex);
|
||||
printk("Attempting manual resume\n");
|
||||
printk(KERN_INFO "PM: Starting manual resume from disk\n");
|
||||
noresume = 0;
|
||||
software_resume();
|
||||
ret = n;
|
||||
|
|
|
@ -24,13 +24,112 @@
|
|||
|
||||
#include "power.h"
|
||||
|
||||
BLOCKING_NOTIFIER_HEAD(pm_chain_head);
|
||||
|
||||
DEFINE_MUTEX(pm_mutex);
|
||||
|
||||
unsigned int pm_flags;
|
||||
EXPORT_SYMBOL(pm_flags);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/* Routines for PM-transition notifications */
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
|
||||
|
||||
int register_pm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&pm_chain_head, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_pm_notifier);
|
||||
|
||||
int unregister_pm_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&pm_chain_head, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_pm_notifier);
|
||||
|
||||
int pm_notifier_call_chain(unsigned long val)
|
||||
{
|
||||
return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
|
||||
== NOTIFY_BAD) ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEBUG
|
||||
int pm_test_level = TEST_NONE;
|
||||
|
||||
static int suspend_test(int level)
|
||||
{
|
||||
if (pm_test_level == level) {
|
||||
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
|
||||
mdelay(5000);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const pm_tests[__TEST_AFTER_LAST] = {
|
||||
[TEST_NONE] = "none",
|
||||
[TEST_CORE] = "core",
|
||||
[TEST_CPUS] = "processors",
|
||||
[TEST_PLATFORM] = "platform",
|
||||
[TEST_DEVICES] = "devices",
|
||||
[TEST_FREEZER] = "freezer",
|
||||
};
|
||||
|
||||
static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
char *s = buf;
|
||||
int level;
|
||||
|
||||
for (level = TEST_FIRST; level <= TEST_MAX; level++)
|
||||
if (pm_tests[level]) {
|
||||
if (level == pm_test_level)
|
||||
s += sprintf(s, "[%s] ", pm_tests[level]);
|
||||
else
|
||||
s += sprintf(s, "%s ", pm_tests[level]);
|
||||
}
|
||||
|
||||
if (s != buf)
|
||||
/* convert the last space to a newline */
|
||||
*(s-1) = '\n';
|
||||
|
||||
return (s - buf);
|
||||
}
|
||||
|
||||
static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
const char *buf, size_t n)
|
||||
{
|
||||
const char * const *s;
|
||||
int level;
|
||||
char *p;
|
||||
int len;
|
||||
int error = -EINVAL;
|
||||
|
||||
p = memchr(buf, '\n', n);
|
||||
len = p ? p - buf : n;
|
||||
|
||||
mutex_lock(&pm_mutex);
|
||||
|
||||
level = TEST_FIRST;
|
||||
for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
|
||||
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
|
||||
pm_test_level = level;
|
||||
error = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&pm_mutex);
|
||||
|
||||
return error ? error : n;
|
||||
}
|
||||
|
||||
power_attr(pm_test);
|
||||
#else /* !CONFIG_PM_DEBUG */
|
||||
static inline int suspend_test(int level) { return 0; }
|
||||
#endif /* !CONFIG_PM_DEBUG */
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
|
||||
/* This is just an arbitrary number */
|
||||
|
@ -76,13 +175,13 @@ static int suspend_prepare(void)
|
|||
if (!suspend_ops || !suspend_ops->enter)
|
||||
return -EPERM;
|
||||
|
||||
pm_prepare_console();
|
||||
|
||||
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
|
||||
if (error)
|
||||
goto Finish;
|
||||
|
||||
pm_prepare_console();
|
||||
|
||||
if (freeze_processes()) {
|
||||
if (suspend_freeze_processes()) {
|
||||
error = -EAGAIN;
|
||||
goto Thaw;
|
||||
}
|
||||
|
@ -100,10 +199,10 @@ static int suspend_prepare(void)
|
|||
return 0;
|
||||
|
||||
Thaw:
|
||||
thaw_processes();
|
||||
pm_restore_console();
|
||||
suspend_thaw_processes();
|
||||
Finish:
|
||||
pm_notifier_call_chain(PM_POST_SUSPEND);
|
||||
pm_restore_console();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -133,10 +232,13 @@ static int suspend_enter(suspend_state_t state)
|
|||
BUG_ON(!irqs_disabled());
|
||||
|
||||
if ((error = device_power_down(PMSG_SUSPEND))) {
|
||||
printk(KERN_ERR "Some devices failed to power down\n");
|
||||
printk(KERN_ERR "PM: Some devices failed to power down\n");
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (!suspend_test(TEST_CORE))
|
||||
error = suspend_ops->enter(state);
|
||||
|
||||
device_power_up();
|
||||
Done:
|
||||
arch_suspend_enable_irqs();
|
||||
|
@ -145,8 +247,8 @@ static int suspend_enter(suspend_state_t state)
|
|||
}
|
||||
|
||||
/**
|
||||
* suspend_devices_and_enter - suspend devices and enter the desired system sleep
|
||||
* state.
|
||||
* suspend_devices_and_enter - suspend devices and enter the desired system
|
||||
* sleep state.
|
||||
* @state: state to enter
|
||||
*/
|
||||
int suspend_devices_and_enter(suspend_state_t state)
|
||||
|
@ -156,33 +258,45 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|||
if (!suspend_ops)
|
||||
return -ENOSYS;
|
||||
|
||||
if (suspend_ops->set_target) {
|
||||
error = suspend_ops->set_target(state);
|
||||
if (suspend_ops->begin) {
|
||||
error = suspend_ops->begin(state);
|
||||
if (error)
|
||||
return error;
|
||||
goto Close;
|
||||
}
|
||||
suspend_console();
|
||||
error = device_suspend(PMSG_SUSPEND);
|
||||
if (error) {
|
||||
printk(KERN_ERR "Some devices failed to suspend\n");
|
||||
printk(KERN_ERR "PM: Some devices failed to suspend\n");
|
||||
goto Resume_console;
|
||||
}
|
||||
|
||||
if (suspend_test(TEST_DEVICES))
|
||||
goto Resume_devices;
|
||||
|
||||
if (suspend_ops->prepare) {
|
||||
error = suspend_ops->prepare();
|
||||
if (error)
|
||||
goto Resume_devices;
|
||||
}
|
||||
|
||||
if (suspend_test(TEST_PLATFORM))
|
||||
goto Finish;
|
||||
|
||||
error = disable_nonboot_cpus();
|
||||
if (!error)
|
||||
if (!error && !suspend_test(TEST_CPUS))
|
||||
suspend_enter(state);
|
||||
|
||||
enable_nonboot_cpus();
|
||||
Finish:
|
||||
if (suspend_ops->finish)
|
||||
suspend_ops->finish();
|
||||
Resume_devices:
|
||||
device_resume();
|
||||
Resume_console:
|
||||
resume_console();
|
||||
Close:
|
||||
if (suspend_ops->end)
|
||||
suspend_ops->end();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -194,9 +308,9 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|||
*/
|
||||
static void suspend_finish(void)
|
||||
{
|
||||
thaw_processes();
|
||||
pm_restore_console();
|
||||
suspend_thaw_processes();
|
||||
pm_notifier_call_chain(PM_POST_SUSPEND);
|
||||
pm_restore_console();
|
||||
}
|
||||
|
||||
|
||||
|
@ -238,17 +352,22 @@ static int enter_state(suspend_state_t state)
|
|||
if (!mutex_trylock(&pm_mutex))
|
||||
return -EBUSY;
|
||||
|
||||
printk("Syncing filesystems ... ");
|
||||
printk(KERN_INFO "PM: Syncing filesystems ... ");
|
||||
sys_sync();
|
||||
printk("done.\n");
|
||||
|
||||
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
|
||||
if ((error = suspend_prepare()))
|
||||
error = suspend_prepare();
|
||||
if (error)
|
||||
goto Unlock;
|
||||
|
||||
if (suspend_test(TEST_FREEZER))
|
||||
goto Finish;
|
||||
|
||||
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
|
||||
error = suspend_devices_and_enter(state);
|
||||
|
||||
Finish:
|
||||
pr_debug("PM: Finishing wakeup.\n");
|
||||
suspend_finish();
|
||||
Unlock:
|
||||
|
@ -369,18 +488,18 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
}
|
||||
|
||||
power_attr(pm_trace);
|
||||
#endif /* CONFIG_PM_TRACE */
|
||||
|
||||
static struct attribute * g[] = {
|
||||
&state_attr.attr,
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
&pm_trace_attr.attr,
|
||||
#endif
|
||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)
|
||||
&pm_test_attr.attr,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
#else
|
||||
static struct attribute * g[] = {
|
||||
&state_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
#endif /* CONFIG_PM_TRACE */
|
||||
|
||||
static struct attribute_group attr_group = {
|
||||
.attrs = g,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <linux/suspend.h>
|
||||
#include <linux/suspend_ioctls.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
struct swsusp_info {
|
||||
struct new_utsname uts;
|
||||
|
@ -128,42 +130,12 @@ struct snapshot_handle {
|
|||
#define data_of(handle) ((handle).buffer + (handle).buf_offset)
|
||||
|
||||
extern unsigned int snapshot_additional_pages(struct zone *zone);
|
||||
extern unsigned long snapshot_get_image_size(void);
|
||||
extern int snapshot_read_next(struct snapshot_handle *handle, size_t count);
|
||||
extern int snapshot_write_next(struct snapshot_handle *handle, size_t count);
|
||||
extern void snapshot_write_finalize(struct snapshot_handle *handle);
|
||||
extern int snapshot_image_loaded(struct snapshot_handle *handle);
|
||||
|
||||
/*
|
||||
* This structure is used to pass the values needed for the identification
|
||||
* of the resume swap area from a user space to the kernel via the
|
||||
* SNAPSHOT_SET_SWAP_AREA ioctl
|
||||
*/
|
||||
struct resume_swap_area {
|
||||
loff_t offset;
|
||||
u_int32_t dev;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SNAPSHOT_IOC_MAGIC '3'
|
||||
#define SNAPSHOT_FREEZE _IO(SNAPSHOT_IOC_MAGIC, 1)
|
||||
#define SNAPSHOT_UNFREEZE _IO(SNAPSHOT_IOC_MAGIC, 2)
|
||||
#define SNAPSHOT_ATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
|
||||
#define SNAPSHOT_ATOMIC_RESTORE _IO(SNAPSHOT_IOC_MAGIC, 4)
|
||||
#define SNAPSHOT_FREE _IO(SNAPSHOT_IOC_MAGIC, 5)
|
||||
#define SNAPSHOT_SET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
|
||||
#define SNAPSHOT_AVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
|
||||
#define SNAPSHOT_GET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
|
||||
#define SNAPSHOT_FREE_SWAP_PAGES _IO(SNAPSHOT_IOC_MAGIC, 9)
|
||||
#define SNAPSHOT_SET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
|
||||
#define SNAPSHOT_S2RAM _IO(SNAPSHOT_IOC_MAGIC, 11)
|
||||
#define SNAPSHOT_PMOPS _IOW(SNAPSHOT_IOC_MAGIC, 12, unsigned int)
|
||||
#define SNAPSHOT_SET_SWAP_AREA _IOW(SNAPSHOT_IOC_MAGIC, 13, \
|
||||
struct resume_swap_area)
|
||||
#define SNAPSHOT_IOC_MAXNR 13
|
||||
|
||||
#define PMOPS_PREPARE 1
|
||||
#define PMOPS_ENTER 2
|
||||
#define PMOPS_FINISH 3
|
||||
|
||||
/* If unset, the snapshot device cannot be open. */
|
||||
extern atomic_t snapshot_device_available;
|
||||
|
||||
|
@ -181,7 +153,6 @@ extern int swsusp_swap_in_use(void);
|
|||
extern int swsusp_check(void);
|
||||
extern int swsusp_shrink_memory(void);
|
||||
extern void swsusp_free(void);
|
||||
extern int swsusp_resume(void);
|
||||
extern int swsusp_read(unsigned int *flags_p);
|
||||
extern int swsusp_write(unsigned int flags);
|
||||
extern void swsusp_close(void);
|
||||
|
@ -201,11 +172,56 @@ static inline int suspend_devices_and_enter(suspend_state_t state)
|
|||
}
|
||||
#endif /* !CONFIG_SUSPEND */
|
||||
|
||||
/* kernel/power/common.c */
|
||||
extern struct blocking_notifier_head pm_chain_head;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/* kernel/power/main.c */
|
||||
extern int pm_notifier_call_chain(unsigned long val);
|
||||
#endif
|
||||
|
||||
static inline int pm_notifier_call_chain(unsigned long val)
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
unsigned int count_highmem_pages(void);
|
||||
int restore_highmem(void);
|
||||
#else
|
||||
static inline unsigned int count_highmem_pages(void) { return 0; }
|
||||
static inline int restore_highmem(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Suspend test levels
|
||||
*/
|
||||
enum {
|
||||
/* keep first */
|
||||
TEST_NONE,
|
||||
TEST_CORE,
|
||||
TEST_CPUS,
|
||||
TEST_PLATFORM,
|
||||
TEST_DEVICES,
|
||||
TEST_FREEZER,
|
||||
/* keep last */
|
||||
__TEST_AFTER_LAST
|
||||
};
|
||||
|
||||
#define TEST_FIRST TEST_NONE
|
||||
#define TEST_MAX (__TEST_AFTER_LAST - 1)
|
||||
|
||||
extern int pm_test_level;
|
||||
|
||||
#ifdef CONFIG_SUSPEND_FREEZER
|
||||
static inline int suspend_freeze_processes(void)
|
||||
{
|
||||
return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
|
||||
== NOTIFY_BAD) ? -EINVAL : 0;
|
||||
return freeze_processes();
|
||||
}
|
||||
|
||||
static inline void suspend_thaw_processes(void)
|
||||
{
|
||||
thaw_processes();
|
||||
}
|
||||
#else
|
||||
static inline int suspend_freeze_processes(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void suspend_thaw_processes(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -635,7 +635,7 @@ __register_nosave_region(unsigned long start_pfn, unsigned long end_pfn,
|
|||
region->end_pfn = end_pfn;
|
||||
list_add_tail(®ion->list, &nosave_regions);
|
||||
Report:
|
||||
printk("swsusp: Registered nosave memory region: %016lx - %016lx\n",
|
||||
printk(KERN_INFO "PM: Registered nosave memory: %016lx - %016lx\n",
|
||||
start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT);
|
||||
}
|
||||
|
||||
|
@ -704,7 +704,7 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
|
|||
list_for_each_entry(region, &nosave_regions, list) {
|
||||
unsigned long pfn;
|
||||
|
||||
printk("swsusp: Marking nosave pages: %016lx - %016lx\n",
|
||||
pr_debug("PM: Marking nosave pages: %016lx - %016lx\n",
|
||||
region->start_pfn << PAGE_SHIFT,
|
||||
region->end_pfn << PAGE_SHIFT);
|
||||
|
||||
|
@ -749,7 +749,7 @@ int create_basic_memory_bitmaps(void)
|
|||
free_pages_map = bm2;
|
||||
mark_nosave_pages(forbidden_pages_map);
|
||||
|
||||
printk("swsusp: Basic memory bitmaps created\n");
|
||||
pr_debug("PM: Basic memory bitmaps created\n");
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -784,7 +784,7 @@ void free_basic_memory_bitmaps(void)
|
|||
memory_bm_free(bm2, PG_UNSAFE_CLEAR);
|
||||
kfree(bm2);
|
||||
|
||||
printk("swsusp: Basic memory bitmaps freed\n");
|
||||
pr_debug("PM: Basic memory bitmaps freed\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -872,7 +872,6 @@ unsigned int count_highmem_pages(void)
|
|||
}
|
||||
#else
|
||||
static inline void *saveable_highmem_page(unsigned long pfn) { return NULL; }
|
||||
static inline unsigned int count_highmem_pages(void) { return 0; }
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
/**
|
||||
|
@ -1089,7 +1088,7 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
|
|||
}
|
||||
|
||||
nr_pages += count_pages_for_highmem(nr_highmem);
|
||||
pr_debug("swsusp: Normal pages needed: %u + %u + %u, available pages: %u\n",
|
||||
pr_debug("PM: Normal pages needed: %u + %u + %u, available pages: %u\n",
|
||||
nr_pages, PAGES_FOR_IO, meta, free);
|
||||
|
||||
return free > nr_pages + PAGES_FOR_IO + meta;
|
||||
|
@ -1202,20 +1201,20 @@ asmlinkage int swsusp_save(void)
|
|||
{
|
||||
unsigned int nr_pages, nr_highmem;
|
||||
|
||||
printk("swsusp: critical section: \n");
|
||||
printk(KERN_INFO "PM: Creating hibernation image: \n");
|
||||
|
||||
drain_local_pages();
|
||||
nr_pages = count_data_pages();
|
||||
nr_highmem = count_highmem_pages();
|
||||
printk("swsusp: Need to copy %u pages\n", nr_pages + nr_highmem);
|
||||
printk(KERN_INFO "PM: Need to copy %u pages\n", nr_pages + nr_highmem);
|
||||
|
||||
if (!enough_free_mem(nr_pages, nr_highmem)) {
|
||||
printk(KERN_ERR "swsusp: Not enough free memory\n");
|
||||
printk(KERN_ERR "PM: Not enough free memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (swsusp_alloc(&orig_bm, ©_bm, nr_pages, nr_highmem)) {
|
||||
printk(KERN_ERR "swsusp: Memory allocation failed\n");
|
||||
printk(KERN_ERR "PM: Memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -1235,7 +1234,8 @@ asmlinkage int swsusp_save(void)
|
|||
nr_copy_pages = nr_pages;
|
||||
nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE);
|
||||
|
||||
printk("swsusp: critical section: done (%d pages copied)\n", nr_pages);
|
||||
printk(KERN_INFO "PM: Hibernation image created (%d pages copied)\n",
|
||||
nr_pages);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1264,12 +1264,17 @@ static char *check_image_kernel(struct swsusp_info *info)
|
|||
}
|
||||
#endif /* CONFIG_ARCH_HIBERNATION_HEADER */
|
||||
|
||||
unsigned long snapshot_get_image_size(void)
|
||||
{
|
||||
return nr_copy_pages + nr_meta_pages + 1;
|
||||
}
|
||||
|
||||
static int init_header(struct swsusp_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(struct swsusp_info));
|
||||
info->num_physpages = num_physpages;
|
||||
info->image_pages = nr_copy_pages;
|
||||
info->pages = nr_copy_pages + nr_meta_pages + 1;
|
||||
info->pages = snapshot_get_image_size();
|
||||
info->size = info->pages;
|
||||
info->size <<= PAGE_SHIFT;
|
||||
return init_header_complete(info);
|
||||
|
@ -1429,7 +1434,7 @@ static int check_header(struct swsusp_info *info)
|
|||
if (!reason && info->num_physpages != num_physpages)
|
||||
reason = "memory size";
|
||||
if (reason) {
|
||||
printk(KERN_ERR "swsusp: Resume mismatch: %s\n", reason);
|
||||
printk(KERN_ERR "PM: Image mismatch: %s\n", reason);
|
||||
return -EPERM;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
|
||||
#include "power.h"
|
||||
|
||||
extern char resume_file[];
|
||||
|
||||
#define SWSUSP_SIG "S1SUSPEND"
|
||||
|
||||
struct swsusp_header {
|
||||
|
@ -73,7 +71,8 @@ static int submit(int rw, pgoff_t page_off, struct page *page,
|
|||
bio->bi_end_io = end_swap_bio_read;
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
|
||||
printk("swsusp: ERROR: adding page to bio at %ld\n", page_off);
|
||||
printk(KERN_ERR "PM: Adding page to bio failed at %ld\n",
|
||||
page_off);
|
||||
bio_put(bio);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
@ -153,7 +152,7 @@ static int mark_swapfiles(sector_t start, unsigned int flags)
|
|||
error = bio_write_page(swsusp_resume_block,
|
||||
swsusp_header, NULL);
|
||||
} else {
|
||||
printk(KERN_ERR "swsusp: Swap header not found!\n");
|
||||
printk(KERN_ERR "PM: Swap header not found!\n");
|
||||
error = -ENODEV;
|
||||
}
|
||||
return error;
|
||||
|
@ -325,7 +324,8 @@ static int save_image(struct swap_map_handle *handle,
|
|||
struct timeval start;
|
||||
struct timeval stop;
|
||||
|
||||
printk("Saving image data pages (%u pages) ... ", nr_to_write);
|
||||
printk(KERN_INFO "PM: Saving image data pages (%u pages) ... ",
|
||||
nr_to_write);
|
||||
m = nr_to_write / 100;
|
||||
if (!m)
|
||||
m = 1;
|
||||
|
@ -365,7 +365,7 @@ static int enough_swap(unsigned int nr_pages)
|
|||
{
|
||||
unsigned int free_swap = count_swap_pages(root_swap, 1);
|
||||
|
||||
pr_debug("swsusp: free swap pages: %u\n", free_swap);
|
||||
pr_debug("PM: Free swap pages: %u\n", free_swap);
|
||||
return free_swap > nr_pages + PAGES_FOR_IO;
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ int swsusp_write(unsigned int flags)
|
|||
|
||||
error = swsusp_swap_check();
|
||||
if (error) {
|
||||
printk(KERN_ERR "swsusp: Cannot find swap device, try "
|
||||
printk(KERN_ERR "PM: Cannot find swap device, try "
|
||||
"swapon -a.\n");
|
||||
return error;
|
||||
}
|
||||
|
@ -402,7 +402,7 @@ int swsusp_write(unsigned int flags)
|
|||
}
|
||||
header = (struct swsusp_info *)data_of(snapshot);
|
||||
if (!enough_swap(header->pages)) {
|
||||
printk(KERN_ERR "swsusp: Not enough free swap\n");
|
||||
printk(KERN_ERR "PM: Not enough free swap\n");
|
||||
error = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
@ -417,7 +417,7 @@ int swsusp_write(unsigned int flags)
|
|||
|
||||
if (!error) {
|
||||
flush_swap_writer(&handle);
|
||||
printk("S");
|
||||
printk(KERN_INFO "PM: S");
|
||||
error = mark_swapfiles(start, flags);
|
||||
printk("|\n");
|
||||
}
|
||||
|
@ -507,7 +507,8 @@ static int load_image(struct swap_map_handle *handle,
|
|||
int err2;
|
||||
unsigned nr_pages;
|
||||
|
||||
printk("Loading image data pages (%u pages) ... ", nr_to_read);
|
||||
printk(KERN_INFO "PM: Loading image data pages (%u pages) ... ",
|
||||
nr_to_read);
|
||||
m = nr_to_read / 100;
|
||||
if (!m)
|
||||
m = 1;
|
||||
|
@ -558,7 +559,7 @@ int swsusp_read(unsigned int *flags_p)
|
|||
|
||||
*flags_p = swsusp_header->flags;
|
||||
if (IS_ERR(resume_bdev)) {
|
||||
pr_debug("swsusp: block device not initialised\n");
|
||||
pr_debug("PM: Image device not initialised\n");
|
||||
return PTR_ERR(resume_bdev);
|
||||
}
|
||||
|
||||
|
@ -577,9 +578,9 @@ int swsusp_read(unsigned int *flags_p)
|
|||
blkdev_put(resume_bdev);
|
||||
|
||||
if (!error)
|
||||
pr_debug("swsusp: Reading resume file was successful\n");
|
||||
pr_debug("PM: Image successfully loaded\n");
|
||||
else
|
||||
pr_debug("swsusp: Error %d resuming\n", error);
|
||||
pr_debug("PM: Error %d resuming\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -611,13 +612,13 @@ int swsusp_check(void)
|
|||
if (error)
|
||||
blkdev_put(resume_bdev);
|
||||
else
|
||||
pr_debug("swsusp: Signature found, resuming\n");
|
||||
pr_debug("PM: Signature found, resuming\n");
|
||||
} else {
|
||||
error = PTR_ERR(resume_bdev);
|
||||
}
|
||||
|
||||
if (error)
|
||||
pr_debug("swsusp: Error %d check for resume file\n", error);
|
||||
pr_debug("PM: Error %d checking image file\n", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -629,7 +630,7 @@ int swsusp_check(void)
|
|||
void swsusp_close(void)
|
||||
{
|
||||
if (IS_ERR(resume_bdev)) {
|
||||
pr_debug("swsusp: block device not initialised\n");
|
||||
pr_debug("PM: Image device not initialised\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,14 +64,6 @@ unsigned long image_size = 500 * 1024 * 1024;
|
|||
|
||||
int in_suspend __nosavedata = 0;
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
unsigned int count_highmem_pages(void);
|
||||
int restore_highmem(void);
|
||||
#else
|
||||
static inline int restore_highmem(void) { return 0; }
|
||||
static inline unsigned int count_highmem_pages(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The following functions are used for tracing the allocated
|
||||
* swap pages, so that they can be freed in case of an error.
|
||||
|
@ -196,7 +188,8 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
|
|||
centisecs = 1; /* avoid div-by-zero */
|
||||
k = nr_pages * (PAGE_SIZE / 1024);
|
||||
kps = (k * 100) / centisecs;
|
||||
printk("%s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", msg, k,
|
||||
printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n",
|
||||
msg, k,
|
||||
centisecs / 100, centisecs % 100,
|
||||
kps / 1000, (kps % 1000) / 10);
|
||||
}
|
||||
|
@ -227,7 +220,7 @@ int swsusp_shrink_memory(void)
|
|||
char *p = "-\\|/";
|
||||
struct timeval start, stop;
|
||||
|
||||
printk("Shrinking memory... ");
|
||||
printk(KERN_INFO "PM: Shrinking memory... ");
|
||||
do_gettimeofday(&start);
|
||||
do {
|
||||
long size, highmem_size;
|
||||
|
@ -269,38 +262,3 @@ int swsusp_shrink_memory(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int swsusp_resume(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
local_irq_disable();
|
||||
/* NOTE: device_power_down() is just a suspend() with irqs off;
|
||||
* it has no special "power things down" semantics
|
||||
*/
|
||||
if (device_power_down(PMSG_PRETHAW))
|
||||
printk(KERN_ERR "Some devices failed to power down, very bad\n");
|
||||
/* We'll ignore saved state, but this gets preempt count (etc) right */
|
||||
save_processor_state();
|
||||
error = restore_highmem();
|
||||
if (!error) {
|
||||
error = swsusp_arch_resume();
|
||||
/* The code below is only ever reached in case of a failure.
|
||||
* Otherwise execution continues at place where
|
||||
* swsusp_arch_suspend() was called
|
||||
*/
|
||||
BUG_ON(!error);
|
||||
/* This call to restore_highmem() undos the previous one */
|
||||
restore_highmem();
|
||||
}
|
||||
/* The only reason why swsusp_arch_resume() can fail is memory being
|
||||
* very tight, so we have to free it as soon as we can to avoid
|
||||
* subsequent failures
|
||||
*/
|
||||
swsusp_free();
|
||||
restore_processor_state();
|
||||
touch_softlockup_watchdog();
|
||||
device_power_up();
|
||||
local_irq_enable();
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,29 @@
|
|||
|
||||
#include "power.h"
|
||||
|
||||
/*
|
||||
* NOTE: The SNAPSHOT_SET_SWAP_FILE and SNAPSHOT_PMOPS ioctls are obsolete and
|
||||
* will be removed in the future. They are only preserved here for
|
||||
* compatibility with existing userland utilities.
|
||||
*/
|
||||
#define SNAPSHOT_SET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int)
|
||||
#define SNAPSHOT_PMOPS _IOW(SNAPSHOT_IOC_MAGIC, 12, unsigned int)
|
||||
|
||||
#define PMOPS_PREPARE 1
|
||||
#define PMOPS_ENTER 2
|
||||
#define PMOPS_FINISH 3
|
||||
|
||||
/*
|
||||
* NOTE: The following ioctl definitions are wrong and have been replaced with
|
||||
* correct ones. They are only preserved here for compatibility with existing
|
||||
* userland utilities and will be removed in the future.
|
||||
*/
|
||||
#define SNAPSHOT_ATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *)
|
||||
#define SNAPSHOT_SET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long)
|
||||
#define SNAPSHOT_AVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *)
|
||||
#define SNAPSHOT_GET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *)
|
||||
|
||||
|
||||
#define SNAPSHOT_MINOR 231
|
||||
|
||||
static struct snapshot_data {
|
||||
|
@ -36,7 +59,7 @@ static struct snapshot_data {
|
|||
int mode;
|
||||
char frozen;
|
||||
char ready;
|
||||
char platform_suspend;
|
||||
char platform_support;
|
||||
} snapshot_state;
|
||||
|
||||
atomic_t snapshot_device_available = ATOMIC_INIT(1);
|
||||
|
@ -44,6 +67,7 @@ atomic_t snapshot_device_available = ATOMIC_INIT(1);
|
|||
static int snapshot_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct snapshot_data *data;
|
||||
int error;
|
||||
|
||||
if (!atomic_add_unless(&snapshot_device_available, -1, 0))
|
||||
return -EBUSY;
|
||||
|
@ -64,13 +88,23 @@ static int snapshot_open(struct inode *inode, struct file *filp)
|
|||
data->swap = swsusp_resume_device ?
|
||||
swap_type_of(swsusp_resume_device, 0, NULL) : -1;
|
||||
data->mode = O_RDONLY;
|
||||
error = pm_notifier_call_chain(PM_RESTORE_PREPARE);
|
||||
if (error)
|
||||
pm_notifier_call_chain(PM_POST_RESTORE);
|
||||
} else {
|
||||
data->swap = -1;
|
||||
data->mode = O_WRONLY;
|
||||
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
|
||||
if (error)
|
||||
pm_notifier_call_chain(PM_POST_HIBERNATION);
|
||||
}
|
||||
if (error) {
|
||||
atomic_inc(&snapshot_device_available);
|
||||
return error;
|
||||
}
|
||||
data->frozen = 0;
|
||||
data->ready = 0;
|
||||
data->platform_suspend = 0;
|
||||
data->platform_support = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -88,6 +122,8 @@ static int snapshot_release(struct inode *inode, struct file *filp)
|
|||
thaw_processes();
|
||||
mutex_unlock(&pm_mutex);
|
||||
}
|
||||
pm_notifier_call_chain(data->mode == O_WRONLY ?
|
||||
PM_POST_HIBERNATION : PM_POST_RESTORE);
|
||||
atomic_inc(&snapshot_device_available);
|
||||
return 0;
|
||||
}
|
||||
|
@ -133,7 +169,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
{
|
||||
int error = 0;
|
||||
struct snapshot_data *data;
|
||||
loff_t avail;
|
||||
loff_t size;
|
||||
sector_t offset;
|
||||
|
||||
if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
|
||||
|
@ -151,8 +187,6 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
if (data->frozen)
|
||||
break;
|
||||
mutex_lock(&pm_mutex);
|
||||
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);
|
||||
if (!error) {
|
||||
printk("Syncing filesystems ... ");
|
||||
sys_sync();
|
||||
printk("done.\n");
|
||||
|
@ -160,9 +194,6 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
error = freeze_processes();
|
||||
if (error)
|
||||
thaw_processes();
|
||||
}
|
||||
if (error)
|
||||
pm_notifier_call_chain(PM_POST_HIBERNATION);
|
||||
mutex_unlock(&pm_mutex);
|
||||
if (!error)
|
||||
data->frozen = 1;
|
||||
|
@ -173,19 +204,19 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
break;
|
||||
mutex_lock(&pm_mutex);
|
||||
thaw_processes();
|
||||
pm_notifier_call_chain(PM_POST_HIBERNATION);
|
||||
mutex_unlock(&pm_mutex);
|
||||
data->frozen = 0;
|
||||
break;
|
||||
|
||||
case SNAPSHOT_CREATE_IMAGE:
|
||||
case SNAPSHOT_ATOMIC_SNAPSHOT:
|
||||
if (data->mode != O_RDONLY || !data->frozen || data->ready) {
|
||||
error = -EPERM;
|
||||
break;
|
||||
}
|
||||
error = hibernation_snapshot(data->platform_suspend);
|
||||
error = hibernation_snapshot(data->platform_support);
|
||||
if (!error)
|
||||
error = put_user(in_suspend, (unsigned int __user *)arg);
|
||||
error = put_user(in_suspend, (int __user *)arg);
|
||||
if (!error)
|
||||
data->ready = 1;
|
||||
break;
|
||||
|
@ -197,7 +228,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
error = -EPERM;
|
||||
break;
|
||||
}
|
||||
error = hibernation_restore(data->platform_suspend);
|
||||
error = hibernation_restore(data->platform_support);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_FREE:
|
||||
|
@ -206,16 +237,29 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
data->ready = 0;
|
||||
break;
|
||||
|
||||
case SNAPSHOT_PREF_IMAGE_SIZE:
|
||||
case SNAPSHOT_SET_IMAGE_SIZE:
|
||||
image_size = arg;
|
||||
break;
|
||||
|
||||
case SNAPSHOT_AVAIL_SWAP:
|
||||
avail = count_swap_pages(data->swap, 1);
|
||||
avail <<= PAGE_SHIFT;
|
||||
error = put_user(avail, (loff_t __user *)arg);
|
||||
case SNAPSHOT_GET_IMAGE_SIZE:
|
||||
if (!data->ready) {
|
||||
error = -ENODATA;
|
||||
break;
|
||||
}
|
||||
size = snapshot_get_image_size();
|
||||
size <<= PAGE_SHIFT;
|
||||
error = put_user(size, (loff_t __user *)arg);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_AVAIL_SWAP_SIZE:
|
||||
case SNAPSHOT_AVAIL_SWAP:
|
||||
size = count_swap_pages(data->swap, 1);
|
||||
size <<= PAGE_SHIFT;
|
||||
error = put_user(size, (loff_t __user *)arg);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_ALLOC_SWAP_PAGE:
|
||||
case SNAPSHOT_GET_SWAP_PAGE:
|
||||
if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
|
||||
error = -ENODEV;
|
||||
|
@ -224,7 +268,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
offset = alloc_swapdev_block(data->swap);
|
||||
if (offset) {
|
||||
offset <<= PAGE_SHIFT;
|
||||
error = put_user(offset, (sector_t __user *)arg);
|
||||
error = put_user(offset, (loff_t __user *)arg);
|
||||
} else {
|
||||
error = -ENOSPC;
|
||||
}
|
||||
|
@ -238,7 +282,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
free_all_swap_pages(data->swap);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_SET_SWAP_FILE:
|
||||
case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */
|
||||
if (!swsusp_swap_in_use()) {
|
||||
/*
|
||||
* User space encodes device types as two-byte values,
|
||||
|
@ -275,26 +319,33 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||
mutex_unlock(&pm_mutex);
|
||||
break;
|
||||
|
||||
case SNAPSHOT_PMOPS:
|
||||
case SNAPSHOT_PLATFORM_SUPPORT:
|
||||
data->platform_support = !!arg;
|
||||
break;
|
||||
|
||||
case SNAPSHOT_POWER_OFF:
|
||||
if (data->platform_support)
|
||||
error = hibernation_platform_enter();
|
||||
break;
|
||||
|
||||
case SNAPSHOT_PMOPS: /* This ioctl is deprecated */
|
||||
error = -EINVAL;
|
||||
|
||||
switch (arg) {
|
||||
|
||||
case PMOPS_PREPARE:
|
||||
data->platform_suspend = 1;
|
||||
data->platform_support = 1;
|
||||
error = 0;
|
||||
break;
|
||||
|
||||
case PMOPS_ENTER:
|
||||
if (data->platform_suspend)
|
||||
if (data->platform_support)
|
||||
error = hibernation_platform_enter();
|
||||
|
||||
break;
|
||||
|
||||
case PMOPS_FINISH:
|
||||
if (data->platform_suspend)
|
||||
if (data->platform_support)
|
||||
error = 0;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue