Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 245374
b: refs/heads/master
c: 91e7c75
h: refs/heads/master
v: v3
  • Loading branch information
Rafael J. Wysocki committed May 17, 2011
1 parent 4024e75 commit 4bf6d33
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 44 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: c650da23d59d2c82307380414606774c6d49b8bd
refs/heads/master: 91e7c75ba93c48a82670d630b9daac92ff70095d
14 changes: 9 additions & 5 deletions trunk/Documentation/power/devices.txt
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,15 @@ When the system goes into the standby or memory sleep state, the phases are:
time.) Unlike the other suspend-related phases, during the prepare
phase the device tree is traversed top-down.

The prepare phase uses only a bus callback. After the callback method
returns, no new children may be registered below the device. The method
may also prepare the device or driver in some way for the upcoming
system power transition, but it should not put the device into a
low-power state.
In addition to that, if device drivers need to allocate additional
memory to be able to hadle device suspend correctly, that should be
done in the prepare phase.

After the prepare callback method returns, no new children may be
registered below the device. The method may also prepare the device or
driver in some way for the upcoming system power transition (for
example, by allocating additional memory required for this purpose), but
it should not put the device into a low-power state.

2. The suspend methods should quiesce the device to stop it from performing
I/O. They also may save the device registers and put it into the
Expand Down
51 changes: 23 additions & 28 deletions trunk/Documentation/power/notifiers.txt
Original file line number Diff line number Diff line change
@@ -1,46 +1,41 @@
Suspend notifiers
(C) 2007 Rafael J. Wysocki <rjw@sisk.pl>, GPL

There are some operations that device drivers may want to carry out in their
.suspend() routines, but shouldn't, because they can cause the hibernation or
suspend to fail. For example, a driver may want to allocate a substantial amount
of memory (like 50 MB) in .suspend(), but that shouldn't be done after the
swsusp's memory shrinker has run.

Also, there may be some operations, that subsystems want to carry out before a
hibernation/suspend or after a restore/resume, requiring the system to be fully
functional, so the drivers' .suspend() and .resume() routines are not suitable
for this purpose. For example, device drivers may want to upload firmware to
their devices after a restore from a hibernation image, but they cannot do it by
calling request_firmware() from their .resume() routines (user land processes
are frozen at this point). The solution may be to load the firmware into
memory before processes are frozen and upload it from there in the .resume()
routine. Of course, a hibernation notifier may be used for this purpose.

The subsystems that have such needs can register suspend notifiers that will be
called upon the following events by the suspend core:
(C) 2007-2011 Rafael J. Wysocki <rjw@sisk.pl>, GPL

There are some operations that subsystems or drivers may want to carry out
before hibernation/suspend or after restore/resume, but they require the system
to be fully functional, so the drivers' and subsystems' .suspend() and .resume()
or even .prepare() and .complete() callbacks are not suitable for this purpose.
For example, device drivers may want to upload firmware to their devices after
resume/restore, but they cannot do it by calling request_firmware() from their
.resume() or .complete() routines (user land processes are frozen at these
points). The solution may be to load the firmware into memory before processes
are frozen and upload it from there in the .resume() routine.
A suspend/hibernation notifier may be used for this purpose.

The subsystems or drivers having such needs can register suspend notifiers that
will be called upon the following events by the PM core:

PM_HIBERNATION_PREPARE The system is going to hibernate or suspend, tasks will
be frozen immediately.

PM_POST_HIBERNATION The system memory state has been restored from a
hibernation image or an error occurred during the
hibernation. Device drivers' .resume() callbacks have
hibernation image or an error occurred during
hibernation. Device drivers' restore 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
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
PM_POST_RESTORE An error occurred during restore from hibernation.
Device drivers' restore callbacks have been executed
and tasks have been thawed.

PM_SUSPEND_PREPARE The system is preparing for a suspend.
PM_SUSPEND_PREPARE The system is preparing for suspend.

PM_POST_SUSPEND The system has just resumed or an error occurred during
the suspend. Device drivers' .resume() callbacks have
been executed and tasks have been thawed.
suspend. Device drivers' resume callbacks have been
executed and tasks have been thawed.

It is generally assumed that whatever the notifiers do for
PM_HIBERNATION_PREPARE, should be undone for PM_POST_HIBERNATION. Analogously,
Expand Down
18 changes: 12 additions & 6 deletions trunk/drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -579,11 +579,13 @@ static bool is_async(struct device *dev)
* Execute the appropriate "resume" callback for all devices whose status
* indicates that they are suspended.
*/
static void dpm_resume(pm_message_t state)
void dpm_resume(pm_message_t state)
{
struct device *dev;
ktime_t starttime = ktime_get();

might_sleep();

mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
Expand Down Expand Up @@ -656,10 +658,12 @@ static void device_complete(struct device *dev, pm_message_t state)
* Execute the ->complete() callbacks for all devices whose PM status is not
* DPM_ON (this allows new devices to be registered).
*/
static void dpm_complete(pm_message_t state)
void dpm_complete(pm_message_t state)
{
struct list_head list;

might_sleep();

INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_prepared_list)) {
Expand Down Expand Up @@ -688,7 +692,6 @@ static void dpm_complete(pm_message_t state)
*/
void dpm_resume_end(pm_message_t state)
{
might_sleep();
dpm_resume(state);
dpm_complete(state);
}
Expand Down Expand Up @@ -912,11 +915,13 @@ static int device_suspend(struct device *dev)
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
* @state: PM transition of the system being carried out.
*/
static int dpm_suspend(pm_message_t state)
int dpm_suspend(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error = 0;

might_sleep();

mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
Expand Down Expand Up @@ -1003,10 +1008,12 @@ static int device_prepare(struct device *dev, pm_message_t state)
*
* Execute the ->prepare() callback(s) for all devices.
*/
static int dpm_prepare(pm_message_t state)
int dpm_prepare(pm_message_t state)
{
int error = 0;

might_sleep();

mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
Expand Down Expand Up @@ -1055,7 +1062,6 @@ int dpm_suspend_start(pm_message_t state)
{
int error;

might_sleep();
error = dpm_prepare(state);
if (!error)
error = dpm_suspend(state);
Expand Down
4 changes: 4 additions & 0 deletions trunk/include/linux/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,14 @@ struct dev_power_domain {
extern void device_pm_lock(void);
extern void dpm_resume_noirq(pm_message_t state);
extern void dpm_resume_end(pm_message_t state);
extern void dpm_resume(pm_message_t state);
extern void dpm_complete(pm_message_t state);

extern void device_pm_unlock(void);
extern int dpm_suspend_noirq(pm_message_t state);
extern int dpm_suspend_start(pm_message_t state);
extern int dpm_suspend(pm_message_t state);
extern int dpm_prepare(pm_message_t state);

extern void __suspend_report_result(const char *function, void *fn, int ret);

Expand Down
17 changes: 13 additions & 4 deletions trunk/kernel/power/hibernate.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,20 +327,25 @@ static int create_image(int platform_mode)

int hibernation_snapshot(int platform_mode)
{
pm_message_t msg = PMSG_RECOVER;
int error;

error = platform_begin(platform_mode);
if (error)
goto Close;

error = dpm_prepare(PMSG_FREEZE);
if (error)
goto Complete_devices;

/* Preallocate image memory before shutting down devices. */
error = hibernate_preallocate_memory();
if (error)
goto Close;
goto Complete_devices;

suspend_console();
pm_restrict_gfp_mask();
error = dpm_suspend_start(PMSG_FREEZE);
error = dpm_suspend(PMSG_FREEZE);
if (error)
goto Recover_platform;

Expand All @@ -358,13 +363,17 @@ int hibernation_snapshot(int platform_mode)
if (error || !in_suspend)
swsusp_free();

dpm_resume_end(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
msg = in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE;
dpm_resume(msg);

if (error || !in_suspend)
pm_restore_gfp_mask();

resume_console();

Complete_devices:
dpm_complete(msg);

Close:
platform_end(platform_mode);
return error;
Expand Down

0 comments on commit 4bf6d33

Please sign in to comment.