forked from Minki/linux
Merge branch 'pm-sleep'
* pm-sleep: PM / Freezer: Remove references to TIF_FREEZE in comments PM / Sleep: Add more wakeup source initialization routines PM / Hibernate: Enable usermodehelpers in hibernate() error path PM / Sleep: Make __pm_stay_awake() delete wakeup source timers PM / Sleep: Fix race conditions related to wakeup source timer function PM / Sleep: Fix possible infinite loop during wakeup source destruction PM / Hibernate: print physical addresses consistently with other parts of kernel PM: Add comment describing relationships between PM callbacks to pm.h PM / Sleep: Drop suspend_stats_update() PM / Sleep: Make enter_state() in kernel/power/suspend.c static PM / Sleep: Unify kerneldoc comments in kernel/power/suspend.c PM / Sleep: Remove unnecessary label from suspend_freeze_processes() PM / Sleep: Do not check wakeup too often in try_to_freeze_tasks() PM / Sleep: Initialize wakeup source locks in wakeup_source_add() PM / Hibernate: Refactor and simplify freezer_test_done PM / Hibernate: Thaw kernel threads in hibernation_snapshot() in error/test path PM / Freezer / Docs: Document the beauty of freeze/thaw semantics PM / Suspend: Avoid code duplication in suspend statistics update PM / Sleep: Introduce generic callbacks for new device PM phases PM / Sleep: Introduce "late suspend" and "early resume" of devices
This commit is contained in:
commit
643161ace2
@ -96,6 +96,12 @@ struct dev_pm_ops {
|
||||
int (*thaw)(struct device *dev);
|
||||
int (*poweroff)(struct device *dev);
|
||||
int (*restore)(struct device *dev);
|
||||
int (*suspend_late)(struct device *dev);
|
||||
int (*resume_early)(struct device *dev);
|
||||
int (*freeze_late)(struct device *dev);
|
||||
int (*thaw_early)(struct device *dev);
|
||||
int (*poweroff_late)(struct device *dev);
|
||||
int (*restore_early)(struct device *dev);
|
||||
int (*suspend_noirq)(struct device *dev);
|
||||
int (*resume_noirq)(struct device *dev);
|
||||
int (*freeze_noirq)(struct device *dev);
|
||||
@ -305,7 +311,7 @@ Entering System Suspend
|
||||
-----------------------
|
||||
When the system goes into the standby or memory sleep state, the phases are:
|
||||
|
||||
prepare, suspend, suspend_noirq.
|
||||
prepare, suspend, suspend_late, suspend_noirq.
|
||||
|
||||
1. The prepare phase is meant to prevent races by preventing new devices
|
||||
from being registered; the PM core would never know that all the
|
||||
@ -324,7 +330,12 @@ When the system goes into the standby or memory sleep state, the phases are:
|
||||
appropriate low-power state, depending on the bus type the device is on,
|
||||
and they may enable wakeup events.
|
||||
|
||||
3. The suspend_noirq phase occurs after IRQ handlers have been disabled,
|
||||
3 For a number of devices it is convenient to split suspend into the
|
||||
"quiesce device" and "save device state" phases, in which cases
|
||||
suspend_late is meant to do the latter. It is always executed after
|
||||
runtime power management has been disabled for all devices.
|
||||
|
||||
4. The suspend_noirq phase occurs after IRQ handlers have been disabled,
|
||||
which means that the driver's interrupt handler will not be called while
|
||||
the callback method is running. The methods should save the values of
|
||||
the device's registers that weren't saved previously and finally put the
|
||||
@ -359,7 +370,7 @@ Leaving System Suspend
|
||||
----------------------
|
||||
When resuming from standby or memory sleep, the phases are:
|
||||
|
||||
resume_noirq, resume, complete.
|
||||
resume_noirq, resume_early, resume, complete.
|
||||
|
||||
1. The resume_noirq callback methods should perform any actions needed
|
||||
before the driver's interrupt handlers are invoked. This generally
|
||||
@ -375,14 +386,18 @@ When resuming from standby or memory sleep, the phases are:
|
||||
device driver's ->pm.resume_noirq() method to perform device-specific
|
||||
actions.
|
||||
|
||||
2. The resume methods should bring the the device back to its operating
|
||||
2. The resume_early methods should prepare devices for the execution of
|
||||
the resume methods. This generally involves undoing the actions of the
|
||||
preceding suspend_late phase.
|
||||
|
||||
3 The resume methods should bring the the device back to its operating
|
||||
state, so that it can perform normal I/O. This generally involves
|
||||
undoing the actions of the suspend phase.
|
||||
|
||||
3. The complete phase uses only a bus callback. The method should undo the
|
||||
actions of the prepare phase. Note, however, that new children may be
|
||||
registered below the device as soon as the resume callbacks occur; it's
|
||||
not necessary to wait until the complete phase.
|
||||
4. The complete phase should undo the actions of the prepare phase. Note,
|
||||
however, that new children may be registered below the device as soon as
|
||||
the resume callbacks occur; it's not necessary to wait until the
|
||||
complete phase.
|
||||
|
||||
At the end of these phases, drivers should be as functional as they were before
|
||||
suspending: I/O can be performed using DMA and IRQs, and the relevant clocks are
|
||||
@ -429,8 +444,8 @@ an image of the system memory while everything is stable, reactivate all
|
||||
devices (thaw), write the image to permanent storage, and finally shut down the
|
||||
system (poweroff). The phases used to accomplish this are:
|
||||
|
||||
prepare, freeze, freeze_noirq, thaw_noirq, thaw, complete,
|
||||
prepare, poweroff, poweroff_noirq
|
||||
prepare, freeze, freeze_late, freeze_noirq, thaw_noirq, thaw_early,
|
||||
thaw, complete, prepare, poweroff, poweroff_late, poweroff_noirq
|
||||
|
||||
1. The prepare phase is discussed in the "Entering System Suspend" section
|
||||
above.
|
||||
@ -441,7 +456,11 @@ system (poweroff). The phases used to accomplish this are:
|
||||
save time it's best not to do so. Also, the device should not be
|
||||
prepared to generate wakeup events.
|
||||
|
||||
3. The freeze_noirq phase is analogous to the suspend_noirq phase discussed
|
||||
3. The freeze_late phase is analogous to the suspend_late phase described
|
||||
above, except that the device should not be put in a low-power state and
|
||||
should not be allowed to generate wakeup events by it.
|
||||
|
||||
4. The freeze_noirq phase is analogous to the suspend_noirq phase discussed
|
||||
above, except again that the device should not be put in a low-power
|
||||
state and should not be allowed to generate wakeup events.
|
||||
|
||||
@ -449,15 +468,19 @@ At this point the system image is created. All devices should be inactive and
|
||||
the contents of memory should remain undisturbed while this happens, so that the
|
||||
image forms an atomic snapshot of the system state.
|
||||
|
||||
4. The thaw_noirq phase is analogous to the resume_noirq phase discussed
|
||||
5. The thaw_noirq phase is analogous to the resume_noirq phase discussed
|
||||
above. The main difference is that its methods can assume the device is
|
||||
in the same state as at the end of the freeze_noirq phase.
|
||||
|
||||
5. The thaw phase is analogous to the resume phase discussed above. Its
|
||||
6. The thaw_early phase is analogous to the resume_early phase described
|
||||
above. Its methods should undo the actions of the preceding
|
||||
freeze_late, if necessary.
|
||||
|
||||
7. The thaw phase is analogous to the resume phase discussed above. Its
|
||||
methods should bring the device back to an operating state, so that it
|
||||
can be used for saving the image if necessary.
|
||||
|
||||
6. The complete phase is discussed in the "Leaving System Suspend" section
|
||||
8. The complete phase is discussed in the "Leaving System Suspend" section
|
||||
above.
|
||||
|
||||
At this point the system image is saved, and the devices then need to be
|
||||
@ -465,16 +488,19 @@ prepared for the upcoming system shutdown. This is much like suspending them
|
||||
before putting the system into the standby or memory sleep state, and the phases
|
||||
are similar.
|
||||
|
||||
7. The prepare phase is discussed above.
|
||||
9. The prepare phase is discussed above.
|
||||
|
||||
8. The poweroff phase is analogous to the suspend phase.
|
||||
10. The poweroff phase is analogous to the suspend phase.
|
||||
|
||||
9. The poweroff_noirq phase is analogous to the suspend_noirq phase.
|
||||
11. The poweroff_late phase is analogous to the suspend_late phase.
|
||||
|
||||
The poweroff and poweroff_noirq callbacks should do essentially the same things
|
||||
as the suspend and suspend_noirq callbacks. The only notable difference is that
|
||||
they need not store the device register values, because the registers should
|
||||
already have been stored during the freeze or freeze_noirq phases.
|
||||
12. The poweroff_noirq phase is analogous to the suspend_noirq phase.
|
||||
|
||||
The poweroff, poweroff_late and poweroff_noirq callbacks should do essentially
|
||||
the same things as the suspend, suspend_late and suspend_noirq callbacks,
|
||||
respectively. The only notable difference is that they need not store the
|
||||
device register values, because the registers should already have been stored
|
||||
during the freeze, freeze_late or freeze_noirq phases.
|
||||
|
||||
|
||||
Leaving Hibernation
|
||||
@ -518,22 +544,25 @@ To achieve this, the image kernel must restore the devices' pre-hibernation
|
||||
functionality. The operation is much like waking up from the memory sleep
|
||||
state, although it involves different phases:
|
||||
|
||||
restore_noirq, restore, complete
|
||||
restore_noirq, restore_early, restore, complete
|
||||
|
||||
1. The restore_noirq phase is analogous to the resume_noirq phase.
|
||||
|
||||
2. The restore phase is analogous to the resume phase.
|
||||
2. The restore_early phase is analogous to the resume_early phase.
|
||||
|
||||
3. The complete phase is discussed above.
|
||||
3. The restore phase is analogous to the resume phase.
|
||||
|
||||
The main difference from resume[_noirq] is that restore[_noirq] must assume the
|
||||
device has been accessed and reconfigured by the boot loader or the boot kernel.
|
||||
Consequently the state of the device may be different from the state remembered
|
||||
from the freeze and freeze_noirq phases. The device may even need to be reset
|
||||
and completely re-initialized. In many cases this difference doesn't matter, so
|
||||
the resume[_noirq] and restore[_norq] method pointers can be set to the same
|
||||
routines. Nevertheless, different callback pointers are used in case there is a
|
||||
situation where it actually matters.
|
||||
4. The complete phase is discussed above.
|
||||
|
||||
The main difference from resume[_early|_noirq] is that restore[_early|_noirq]
|
||||
must assume the device has been accessed and reconfigured by the boot loader or
|
||||
the boot kernel. Consequently the state of the device may be different from the
|
||||
state remembered from the freeze, freeze_late and freeze_noirq phases. The
|
||||
device may even need to be reset and completely re-initialized. In many cases
|
||||
this difference doesn't matter, so the resume[_early|_noirq] and
|
||||
restore[_early|_norq] method pointers can be set to the same routines.
|
||||
Nevertheless, different callback pointers are used in case there is a situation
|
||||
where it actually does matter.
|
||||
|
||||
|
||||
Device Power Management Domains
|
||||
|
@ -63,6 +63,27 @@ devices have been reinitialized, the function thaw_processes() is called in
|
||||
order to clear the PF_FROZEN flag for each frozen task. Then, the tasks that
|
||||
have been frozen leave __refrigerator() and continue running.
|
||||
|
||||
|
||||
Rationale behind the functions dealing with freezing and thawing of tasks:
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
freeze_processes():
|
||||
- freezes only userspace tasks
|
||||
|
||||
freeze_kernel_threads():
|
||||
- freezes all tasks (including kernel threads) because we can't freeze
|
||||
kernel threads without freezing userspace tasks
|
||||
|
||||
thaw_kernel_threads():
|
||||
- thaws only kernel threads; this is particularly useful if we need to do
|
||||
anything special in between thawing of kernel threads and thawing of
|
||||
userspace tasks, or if we want to postpone the thawing of userspace tasks
|
||||
|
||||
thaw_processes():
|
||||
- thaws all tasks (including kernel threads) because we can't thaw userspace
|
||||
tasks without thawing kernel threads
|
||||
|
||||
|
||||
III. Which kernel threads are freezable?
|
||||
|
||||
Kernel threads are not freezable by default. However, a kernel thread may clear
|
||||
|
@ -1234,8 +1234,7 @@ static int suspend(int vetoable)
|
||||
struct apm_user *as;
|
||||
|
||||
dpm_suspend_start(PMSG_SUSPEND);
|
||||
|
||||
dpm_suspend_noirq(PMSG_SUSPEND);
|
||||
dpm_suspend_end(PMSG_SUSPEND);
|
||||
|
||||
local_irq_disable();
|
||||
syscore_suspend();
|
||||
@ -1259,9 +1258,9 @@ static int suspend(int vetoable)
|
||||
syscore_resume();
|
||||
local_irq_enable();
|
||||
|
||||
dpm_resume_noirq(PMSG_RESUME);
|
||||
|
||||
dpm_resume_start(PMSG_RESUME);
|
||||
dpm_resume_end(PMSG_RESUME);
|
||||
|
||||
queue_event(APM_NORMAL_RESUME, NULL);
|
||||
spin_lock(&user_list_lock);
|
||||
for (as = user_list; as != NULL; as = as->next) {
|
||||
@ -1277,7 +1276,7 @@ static void standby(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
dpm_suspend_noirq(PMSG_SUSPEND);
|
||||
dpm_suspend_end(PMSG_SUSPEND);
|
||||
|
||||
local_irq_disable();
|
||||
syscore_suspend();
|
||||
@ -1291,7 +1290,7 @@ static void standby(void)
|
||||
syscore_resume();
|
||||
local_irq_enable();
|
||||
|
||||
dpm_resume_noirq(PMSG_RESUME);
|
||||
dpm_resume_start(PMSG_RESUME);
|
||||
}
|
||||
|
||||
static apm_event_t get_event(void)
|
||||
|
@ -91,68 +91,39 @@ int pm_generic_prepare(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
|
||||
* @dev: Device to handle.
|
||||
* @event: PM transition of the system under way.
|
||||
* @bool: Whether or not this is the "noirq" stage.
|
||||
*
|
||||
* Execute the PM callback corresponding to @event provided by the driver of
|
||||
* @dev, if defined, and return its error code. Return 0 if the callback is
|
||||
* not present.
|
||||
*/
|
||||
static int __pm_generic_call(struct device *dev, int event, bool noirq)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
int (*callback)(struct device *);
|
||||
|
||||
if (!pm)
|
||||
return 0;
|
||||
|
||||
switch (event) {
|
||||
case PM_EVENT_SUSPEND:
|
||||
callback = noirq ? pm->suspend_noirq : pm->suspend;
|
||||
break;
|
||||
case PM_EVENT_FREEZE:
|
||||
callback = noirq ? pm->freeze_noirq : pm->freeze;
|
||||
break;
|
||||
case PM_EVENT_HIBERNATE:
|
||||
callback = noirq ? pm->poweroff_noirq : pm->poweroff;
|
||||
break;
|
||||
case PM_EVENT_RESUME:
|
||||
callback = noirq ? pm->resume_noirq : pm->resume;
|
||||
break;
|
||||
case PM_EVENT_THAW:
|
||||
callback = noirq ? pm->thaw_noirq : pm->thaw;
|
||||
break;
|
||||
case PM_EVENT_RESTORE:
|
||||
callback = noirq ? pm->restore_noirq : pm->restore;
|
||||
break;
|
||||
default:
|
||||
callback = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return callback ? callback(dev) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
|
||||
* @dev: Device to suspend.
|
||||
*/
|
||||
int pm_generic_suspend_noirq(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_suspend_late - Generic suspend_late callback for subsystems.
|
||||
* @dev: Device to suspend.
|
||||
*/
|
||||
int pm_generic_suspend_late(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->suspend_late ? pm->suspend_late(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
|
||||
|
||||
/**
|
||||
* pm_generic_suspend - Generic suspend callback for subsystems.
|
||||
* @dev: Device to suspend.
|
||||
*/
|
||||
int pm_generic_suspend(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->suspend ? pm->suspend(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_suspend);
|
||||
|
||||
@ -162,17 +133,33 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend);
|
||||
*/
|
||||
int pm_generic_freeze_noirq(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_freeze_late - Generic freeze_late callback for subsystems.
|
||||
* @dev: Device to freeze.
|
||||
*/
|
||||
int pm_generic_freeze_late(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->freeze_late ? pm->freeze_late(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_freeze_late);
|
||||
|
||||
/**
|
||||
* pm_generic_freeze - Generic freeze callback for subsystems.
|
||||
* @dev: Device to freeze.
|
||||
*/
|
||||
int pm_generic_freeze(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->freeze ? pm->freeze(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_freeze);
|
||||
|
||||
@ -182,17 +169,33 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze);
|
||||
*/
|
||||
int pm_generic_poweroff_noirq(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_poweroff_late - Generic poweroff_late callback for subsystems.
|
||||
* @dev: Device to handle.
|
||||
*/
|
||||
int pm_generic_poweroff_late(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
|
||||
|
||||
/**
|
||||
* pm_generic_poweroff - Generic poweroff callback for subsystems.
|
||||
* @dev: Device to handle.
|
||||
*/
|
||||
int pm_generic_poweroff(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->poweroff ? pm->poweroff(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_poweroff);
|
||||
|
||||
@ -202,17 +205,33 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff);
|
||||
*/
|
||||
int pm_generic_thaw_noirq(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_THAW, true);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_thaw_early - Generic thaw_early callback for subsystems.
|
||||
* @dev: Device to thaw.
|
||||
*/
|
||||
int pm_generic_thaw_early(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->thaw_early ? pm->thaw_early(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_thaw_early);
|
||||
|
||||
/**
|
||||
* pm_generic_thaw - Generic thaw callback for subsystems.
|
||||
* @dev: Device to thaw.
|
||||
*/
|
||||
int pm_generic_thaw(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_THAW, false);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->thaw ? pm->thaw(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_thaw);
|
||||
|
||||
@ -222,17 +241,33 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
|
||||
*/
|
||||
int pm_generic_resume_noirq(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_RESUME, true);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_resume_early - Generic resume_early callback for subsystems.
|
||||
* @dev: Device to resume.
|
||||
*/
|
||||
int pm_generic_resume_early(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->resume_early ? pm->resume_early(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_resume_early);
|
||||
|
||||
/**
|
||||
* pm_generic_resume - Generic resume callback for subsystems.
|
||||
* @dev: Device to resume.
|
||||
*/
|
||||
int pm_generic_resume(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_RESUME, false);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->resume ? pm->resume(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_resume);
|
||||
|
||||
@ -242,17 +277,33 @@ EXPORT_SYMBOL_GPL(pm_generic_resume);
|
||||
*/
|
||||
int pm_generic_restore_noirq(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_RESTORE, true);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
|
||||
|
||||
/**
|
||||
* pm_generic_restore_early - Generic restore_early callback for subsystems.
|
||||
* @dev: Device to resume.
|
||||
*/
|
||||
int pm_generic_restore_early(struct device *dev)
|
||||
{
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->restore_early ? pm->restore_early(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_restore_early);
|
||||
|
||||
/**
|
||||
* pm_generic_restore - Generic restore callback for subsystems.
|
||||
* @dev: Device to restore.
|
||||
*/
|
||||
int pm_generic_restore(struct device *dev)
|
||||
{
|
||||
return __pm_generic_call(dev, PM_EVENT_RESTORE, false);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
return pm && pm->restore ? pm->restore(dev) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_generic_restore);
|
||||
|
||||
|
@ -47,6 +47,7 @@ typedef int (*pm_callback_t)(struct device *);
|
||||
LIST_HEAD(dpm_list);
|
||||
LIST_HEAD(dpm_prepared_list);
|
||||
LIST_HEAD(dpm_suspended_list);
|
||||
LIST_HEAD(dpm_late_early_list);
|
||||
LIST_HEAD(dpm_noirq_list);
|
||||
|
||||
struct suspend_stats suspend_stats;
|
||||
@ -245,6 +246,40 @@ static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_late_early_op - Return the PM operation appropriate for given PM event.
|
||||
* @ops: PM operations to choose from.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Runtime PM is disabled for @dev while this function is being executed.
|
||||
*/
|
||||
static pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops,
|
||||
pm_message_t state)
|
||||
{
|
||||
switch (state.event) {
|
||||
#ifdef CONFIG_SUSPEND
|
||||
case PM_EVENT_SUSPEND:
|
||||
return ops->suspend_late;
|
||||
case PM_EVENT_RESUME:
|
||||
return ops->resume_early;
|
||||
#endif /* CONFIG_SUSPEND */
|
||||
#ifdef CONFIG_HIBERNATE_CALLBACKS
|
||||
case PM_EVENT_FREEZE:
|
||||
case PM_EVENT_QUIESCE:
|
||||
return ops->freeze_late;
|
||||
case PM_EVENT_HIBERNATE:
|
||||
return ops->poweroff_late;
|
||||
case PM_EVENT_THAW:
|
||||
case PM_EVENT_RECOVER:
|
||||
return ops->thaw_early;
|
||||
case PM_EVENT_RESTORE:
|
||||
return ops->restore_early;
|
||||
#endif /* CONFIG_HIBERNATE_CALLBACKS */
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_noirq_op - Return the PM operation appropriate for given PM event.
|
||||
* @ops: PM operations to choose from.
|
||||
@ -374,21 +409,21 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
|
||||
TRACE_RESUME(0);
|
||||
|
||||
if (dev->pm_domain) {
|
||||
info = "EARLY power domain ";
|
||||
info = "noirq power domain ";
|
||||
callback = pm_noirq_op(&dev->pm_domain->ops, state);
|
||||
} else if (dev->type && dev->type->pm) {
|
||||
info = "EARLY type ";
|
||||
info = "noirq type ";
|
||||
callback = pm_noirq_op(dev->type->pm, state);
|
||||
} else if (dev->class && dev->class->pm) {
|
||||
info = "EARLY class ";
|
||||
info = "noirq class ";
|
||||
callback = pm_noirq_op(dev->class->pm, state);
|
||||
} else if (dev->bus && dev->bus->pm) {
|
||||
info = "EARLY bus ";
|
||||
info = "noirq bus ";
|
||||
callback = pm_noirq_op(dev->bus->pm, state);
|
||||
}
|
||||
|
||||
if (!callback && dev->driver && dev->driver->pm) {
|
||||
info = "EARLY driver ";
|
||||
info = "noirq driver ";
|
||||
callback = pm_noirq_op(dev->driver->pm, state);
|
||||
}
|
||||
|
||||
@ -399,13 +434,13 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_resume_noirq - Execute "early resume" callbacks for non-sysdev devices.
|
||||
* dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Call the "noirq" resume handlers for all devices marked as DPM_OFF_IRQ and
|
||||
* Call the "noirq" resume handlers for all devices in dpm_noirq_list and
|
||||
* enable device drivers to receive interrupts.
|
||||
*/
|
||||
void dpm_resume_noirq(pm_message_t state)
|
||||
static void dpm_resume_noirq(pm_message_t state)
|
||||
{
|
||||
ktime_t starttime = ktime_get();
|
||||
|
||||
@ -415,7 +450,7 @@ void dpm_resume_noirq(pm_message_t state)
|
||||
int error;
|
||||
|
||||
get_device(dev);
|
||||
list_move_tail(&dev->power.entry, &dpm_suspended_list);
|
||||
list_move_tail(&dev->power.entry, &dpm_late_early_list);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
error = device_resume_noirq(dev, state);
|
||||
@ -423,6 +458,80 @@ void dpm_resume_noirq(pm_message_t state)
|
||||
suspend_stats.failed_resume_noirq++;
|
||||
dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
|
||||
dpm_save_failed_dev(dev_name(dev));
|
||||
pm_dev_err(dev, state, " noirq", error);
|
||||
}
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
put_device(dev);
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
dpm_show_time(starttime, state, "noirq");
|
||||
resume_device_irqs();
|
||||
}
|
||||
|
||||
/**
|
||||
* device_resume_early - Execute an "early resume" callback for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Runtime PM is disabled for @dev while this function is being executed.
|
||||
*/
|
||||
static int device_resume_early(struct device *dev, pm_message_t state)
|
||||
{
|
||||
pm_callback_t callback = NULL;
|
||||
char *info = NULL;
|
||||
int error = 0;
|
||||
|
||||
TRACE_DEVICE(dev);
|
||||
TRACE_RESUME(0);
|
||||
|
||||
if (dev->pm_domain) {
|
||||
info = "early power domain ";
|
||||
callback = pm_late_early_op(&dev->pm_domain->ops, state);
|
||||
} else if (dev->type && dev->type->pm) {
|
||||
info = "early type ";
|
||||
callback = pm_late_early_op(dev->type->pm, state);
|
||||
} else if (dev->class && dev->class->pm) {
|
||||
info = "early class ";
|
||||
callback = pm_late_early_op(dev->class->pm, state);
|
||||
} else if (dev->bus && dev->bus->pm) {
|
||||
info = "early bus ";
|
||||
callback = pm_late_early_op(dev->bus->pm, state);
|
||||
}
|
||||
|
||||
if (!callback && dev->driver && dev->driver->pm) {
|
||||
info = "early driver ";
|
||||
callback = pm_late_early_op(dev->driver->pm, state);
|
||||
}
|
||||
|
||||
error = dpm_run_callback(callback, dev, state, info);
|
||||
|
||||
TRACE_RESUME(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_resume_early - Execute "early resume" callbacks for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static void dpm_resume_early(pm_message_t state)
|
||||
{
|
||||
ktime_t starttime = ktime_get();
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
while (!list_empty(&dpm_late_early_list)) {
|
||||
struct device *dev = to_device(dpm_late_early_list.next);
|
||||
int error;
|
||||
|
||||
get_device(dev);
|
||||
list_move_tail(&dev->power.entry, &dpm_suspended_list);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
error = device_resume_early(dev, state);
|
||||
if (error) {
|
||||
suspend_stats.failed_resume_early++;
|
||||
dpm_save_failed_step(SUSPEND_RESUME_EARLY);
|
||||
dpm_save_failed_dev(dev_name(dev));
|
||||
pm_dev_err(dev, state, " early", error);
|
||||
}
|
||||
|
||||
@ -431,9 +540,18 @@ void dpm_resume_noirq(pm_message_t state)
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
dpm_show_time(starttime, state, "early");
|
||||
resume_device_irqs();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpm_resume_noirq);
|
||||
|
||||
/**
|
||||
* dpm_resume_start - Execute "noirq" and "early" device callbacks.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
void dpm_resume_start(pm_message_t state)
|
||||
{
|
||||
dpm_resume_noirq(state);
|
||||
dpm_resume_early(state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpm_resume_start);
|
||||
|
||||
/**
|
||||
* device_resume - Execute "resume" callbacks for given device.
|
||||
@ -716,21 +834,21 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
|
||||
char *info = NULL;
|
||||
|
||||
if (dev->pm_domain) {
|
||||
info = "LATE power domain ";
|
||||
info = "noirq power domain ";
|
||||
callback = pm_noirq_op(&dev->pm_domain->ops, state);
|
||||
} else if (dev->type && dev->type->pm) {
|
||||
info = "LATE type ";
|
||||
info = "noirq type ";
|
||||
callback = pm_noirq_op(dev->type->pm, state);
|
||||
} else if (dev->class && dev->class->pm) {
|
||||
info = "LATE class ";
|
||||
info = "noirq class ";
|
||||
callback = pm_noirq_op(dev->class->pm, state);
|
||||
} else if (dev->bus && dev->bus->pm) {
|
||||
info = "LATE bus ";
|
||||
info = "noirq bus ";
|
||||
callback = pm_noirq_op(dev->bus->pm, state);
|
||||
}
|
||||
|
||||
if (!callback && dev->driver && dev->driver->pm) {
|
||||
info = "LATE driver ";
|
||||
info = "noirq driver ";
|
||||
callback = pm_noirq_op(dev->driver->pm, state);
|
||||
}
|
||||
|
||||
@ -738,21 +856,21 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices.
|
||||
* dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Prevent device drivers from receiving interrupts and call the "noirq" suspend
|
||||
* handlers for all non-sysdev devices.
|
||||
*/
|
||||
int dpm_suspend_noirq(pm_message_t state)
|
||||
static int dpm_suspend_noirq(pm_message_t state)
|
||||
{
|
||||
ktime_t starttime = ktime_get();
|
||||
int error = 0;
|
||||
|
||||
suspend_device_irqs();
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
while (!list_empty(&dpm_suspended_list)) {
|
||||
struct device *dev = to_device(dpm_suspended_list.prev);
|
||||
while (!list_empty(&dpm_late_early_list)) {
|
||||
struct device *dev = to_device(dpm_late_early_list.prev);
|
||||
|
||||
get_device(dev);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
@ -761,7 +879,7 @@ int dpm_suspend_noirq(pm_message_t state)
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
if (error) {
|
||||
pm_dev_err(dev, state, " late", error);
|
||||
pm_dev_err(dev, state, " noirq", error);
|
||||
suspend_stats.failed_suspend_noirq++;
|
||||
dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
|
||||
dpm_save_failed_dev(dev_name(dev));
|
||||
@ -776,10 +894,95 @@ int dpm_suspend_noirq(pm_message_t state)
|
||||
if (error)
|
||||
dpm_resume_noirq(resume_event(state));
|
||||
else
|
||||
dpm_show_time(starttime, state, "late");
|
||||
dpm_show_time(starttime, state, "noirq");
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpm_suspend_noirq);
|
||||
|
||||
/**
|
||||
* device_suspend_late - Execute a "late suspend" callback for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Runtime PM is disabled for @dev while this function is being executed.
|
||||
*/
|
||||
static int device_suspend_late(struct device *dev, pm_message_t state)
|
||||
{
|
||||
pm_callback_t callback = NULL;
|
||||
char *info = NULL;
|
||||
|
||||
if (dev->pm_domain) {
|
||||
info = "late power domain ";
|
||||
callback = pm_late_early_op(&dev->pm_domain->ops, state);
|
||||
} else if (dev->type && dev->type->pm) {
|
||||
info = "late type ";
|
||||
callback = pm_late_early_op(dev->type->pm, state);
|
||||
} else if (dev->class && dev->class->pm) {
|
||||
info = "late class ";
|
||||
callback = pm_late_early_op(dev->class->pm, state);
|
||||
} else if (dev->bus && dev->bus->pm) {
|
||||
info = "late bus ";
|
||||
callback = pm_late_early_op(dev->bus->pm, state);
|
||||
}
|
||||
|
||||
if (!callback && dev->driver && dev->driver->pm) {
|
||||
info = "late driver ";
|
||||
callback = pm_late_early_op(dev->driver->pm, state);
|
||||
}
|
||||
|
||||
return dpm_run_callback(callback, dev, state, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_suspend_late - Execute "late suspend" callbacks for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static int dpm_suspend_late(pm_message_t state)
|
||||
{
|
||||
ktime_t starttime = ktime_get();
|
||||
int error = 0;
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
while (!list_empty(&dpm_suspended_list)) {
|
||||
struct device *dev = to_device(dpm_suspended_list.prev);
|
||||
|
||||
get_device(dev);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
error = device_suspend_late(dev, state);
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
if (error) {
|
||||
pm_dev_err(dev, state, " late", error);
|
||||
suspend_stats.failed_suspend_late++;
|
||||
dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
|
||||
dpm_save_failed_dev(dev_name(dev));
|
||||
put_device(dev);
|
||||
break;
|
||||
}
|
||||
if (!list_empty(&dev->power.entry))
|
||||
list_move(&dev->power.entry, &dpm_late_early_list);
|
||||
put_device(dev);
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
if (error)
|
||||
dpm_resume_early(resume_event(state));
|
||||
else
|
||||
dpm_show_time(starttime, state, "late");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_suspend_end - Execute "late" and "noirq" device suspend callbacks.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
int dpm_suspend_end(pm_message_t state)
|
||||
{
|
||||
int error = dpm_suspend_late(state);
|
||||
|
||||
return error ? : dpm_suspend_noirq(state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpm_suspend_end);
|
||||
|
||||
/**
|
||||
* legacy_suspend - Execute a legacy (bus or class) suspend callback for device.
|
||||
|
@ -52,6 +52,23 @@ static void pm_wakeup_timer_fn(unsigned long data);
|
||||
|
||||
static LIST_HEAD(wakeup_sources);
|
||||
|
||||
/**
|
||||
* wakeup_source_prepare - Prepare a new wakeup source for initialization.
|
||||
* @ws: Wakeup source to prepare.
|
||||
* @name: Pointer to the name of the new wakeup source.
|
||||
*
|
||||
* Callers must ensure that the @name string won't be freed when @ws is still in
|
||||
* use.
|
||||
*/
|
||||
void wakeup_source_prepare(struct wakeup_source *ws, const char *name)
|
||||
{
|
||||
if (ws) {
|
||||
memset(ws, 0, sizeof(*ws));
|
||||
ws->name = name;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_prepare);
|
||||
|
||||
/**
|
||||
* wakeup_source_create - Create a struct wakeup_source object.
|
||||
* @name: Name of the new wakeup source.
|
||||
@ -60,37 +77,44 @@ struct wakeup_source *wakeup_source_create(const char *name)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
|
||||
ws = kzalloc(sizeof(*ws), GFP_KERNEL);
|
||||
ws = kmalloc(sizeof(*ws), GFP_KERNEL);
|
||||
if (!ws)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&ws->lock);
|
||||
if (name)
|
||||
ws->name = kstrdup(name, GFP_KERNEL);
|
||||
|
||||
wakeup_source_prepare(ws, name ? kstrdup(name, GFP_KERNEL) : NULL);
|
||||
return ws;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_create);
|
||||
|
||||
/**
|
||||
* wakeup_source_drop - Prepare a struct wakeup_source object for destruction.
|
||||
* @ws: Wakeup source to prepare for destruction.
|
||||
*
|
||||
* Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never
|
||||
* be run in parallel with this function for the same wakeup source object.
|
||||
*/
|
||||
void wakeup_source_drop(struct wakeup_source *ws)
|
||||
{
|
||||
if (!ws)
|
||||
return;
|
||||
|
||||
del_timer_sync(&ws->timer);
|
||||
__pm_relax(ws);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_drop);
|
||||
|
||||
/**
|
||||
* wakeup_source_destroy - Destroy a struct wakeup_source object.
|
||||
* @ws: Wakeup source to destroy.
|
||||
*
|
||||
* Use only for wakeup source objects created with wakeup_source_create().
|
||||
*/
|
||||
void wakeup_source_destroy(struct wakeup_source *ws)
|
||||
{
|
||||
if (!ws)
|
||||
return;
|
||||
|
||||
spin_lock_irq(&ws->lock);
|
||||
while (ws->active) {
|
||||
spin_unlock_irq(&ws->lock);
|
||||
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT));
|
||||
|
||||
spin_lock_irq(&ws->lock);
|
||||
}
|
||||
spin_unlock_irq(&ws->lock);
|
||||
|
||||
wakeup_source_drop(ws);
|
||||
kfree(ws->name);
|
||||
kfree(ws);
|
||||
}
|
||||
@ -105,6 +129,7 @@ void wakeup_source_add(struct wakeup_source *ws)
|
||||
if (WARN_ON(!ws))
|
||||
return;
|
||||
|
||||
spin_lock_init(&ws->lock);
|
||||
setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
|
||||
ws->active = false;
|
||||
|
||||
@ -152,8 +177,10 @@ EXPORT_SYMBOL_GPL(wakeup_source_register);
|
||||
*/
|
||||
void wakeup_source_unregister(struct wakeup_source *ws)
|
||||
{
|
||||
wakeup_source_remove(ws);
|
||||
wakeup_source_destroy(ws);
|
||||
if (ws) {
|
||||
wakeup_source_remove(ws);
|
||||
wakeup_source_destroy(ws);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_unregister);
|
||||
|
||||
@ -349,7 +376,6 @@ static void wakeup_source_activate(struct wakeup_source *ws)
|
||||
{
|
||||
ws->active = true;
|
||||
ws->active_count++;
|
||||
ws->timer_expires = jiffies;
|
||||
ws->last_time = ktime_get();
|
||||
|
||||
/* Increment the counter of events in progress. */
|
||||
@ -370,9 +396,14 @@ void __pm_stay_awake(struct wakeup_source *ws)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
ws->event_count++;
|
||||
if (!ws->active)
|
||||
wakeup_source_activate(ws);
|
||||
|
||||
del_timer(&ws->timer);
|
||||
ws->timer_expires = 0;
|
||||
|
||||
spin_unlock_irqrestore(&ws->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__pm_stay_awake);
|
||||
@ -438,6 +469,7 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
|
||||
ws->max_time = duration;
|
||||
|
||||
del_timer(&ws->timer);
|
||||
ws->timer_expires = 0;
|
||||
|
||||
/*
|
||||
* Increment the counter of registered wakeup events and decrement the
|
||||
@ -492,11 +524,22 @@ EXPORT_SYMBOL_GPL(pm_relax);
|
||||
* pm_wakeup_timer_fn - Delayed finalization of a wakeup event.
|
||||
* @data: Address of the wakeup source object associated with the event source.
|
||||
*
|
||||
* Call __pm_relax() for the wakeup source whose address is stored in @data.
|
||||
* Call wakeup_source_deactivate() for the wakeup source whose address is stored
|
||||
* in @data if it is currently active and its timer has not been canceled and
|
||||
* the expiration time of the timer is not in future.
|
||||
*/
|
||||
static void pm_wakeup_timer_fn(unsigned long data)
|
||||
{
|
||||
__pm_relax((struct wakeup_source *)data);
|
||||
struct wakeup_source *ws = (struct wakeup_source *)data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
if (ws->active && ws->timer_expires
|
||||
&& time_after_eq(jiffies, ws->timer_expires))
|
||||
wakeup_source_deactivate(ws);
|
||||
|
||||
spin_unlock_irqrestore(&ws->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -534,7 +577,7 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
|
||||
if (!expires)
|
||||
expires = 1;
|
||||
|
||||
if (time_after(expires, ws->timer_expires)) {
|
||||
if (!ws->timer_expires || time_after(expires, ws->timer_expires)) {
|
||||
mod_timer(&ws->timer, expires);
|
||||
ws->timer_expires = expires;
|
||||
}
|
||||
|
@ -129,9 +129,9 @@ static void do_suspend(void)
|
||||
printk(KERN_DEBUG "suspending xenstore...\n");
|
||||
xs_suspend();
|
||||
|
||||
err = dpm_suspend_noirq(PMSG_FREEZE);
|
||||
err = dpm_suspend_end(PMSG_FREEZE);
|
||||
if (err) {
|
||||
printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
|
||||
printk(KERN_ERR "dpm_suspend_end failed: %d\n", err);
|
||||
goto out_resume;
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ static void do_suspend(void)
|
||||
|
||||
err = stop_machine(xen_suspend, &si, cpumask_of(0));
|
||||
|
||||
dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
|
||||
dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
|
||||
|
@ -110,6 +110,10 @@ typedef struct pm_message {
|
||||
* Subsystem-level @suspend() is executed for all devices after invoking
|
||||
* subsystem-level @prepare() for all of them.
|
||||
*
|
||||
* @suspend_late: Continue operations started by @suspend(). For a number of
|
||||
* devices @suspend_late() may point to the same callback routine as the
|
||||
* runtime suspend callback.
|
||||
*
|
||||
* @resume: Executed after waking the system up from a sleep state in which the
|
||||
* contents of main memory were preserved. The exact action to perform
|
||||
* depends on the device's subsystem, but generally the driver is expected
|
||||
@ -122,6 +126,10 @@ typedef struct pm_message {
|
||||
* Subsystem-level @resume() is executed for all devices after invoking
|
||||
* subsystem-level @resume_noirq() for all of them.
|
||||
*
|
||||
* @resume_early: Prepare to execute @resume(). For a number of devices
|
||||
* @resume_early() may point to the same callback routine as the runtime
|
||||
* resume callback.
|
||||
*
|
||||
* @freeze: Hibernation-specific, executed before creating a hibernation image.
|
||||
* Analogous to @suspend(), but it should not enable the device to signal
|
||||
* wakeup events or change its power state. The majority of subsystems
|
||||
@ -131,6 +139,10 @@ typedef struct pm_message {
|
||||
* Subsystem-level @freeze() is executed for all devices after invoking
|
||||
* subsystem-level @prepare() for all of them.
|
||||
*
|
||||
* @freeze_late: Continue operations started by @freeze(). Analogous to
|
||||
* @suspend_late(), but it should not enable the device to signal wakeup
|
||||
* events or change its power state.
|
||||
*
|
||||
* @thaw: Hibernation-specific, executed after creating a hibernation image OR
|
||||
* if the creation of an image has failed. Also executed after a failing
|
||||
* attempt to restore the contents of main memory from such an image.
|
||||
@ -140,15 +152,23 @@ typedef struct pm_message {
|
||||
* subsystem-level @thaw_noirq() for all of them. It also may be executed
|
||||
* directly after @freeze() in case of a transition error.
|
||||
*
|
||||
* @thaw_early: Prepare to execute @thaw(). Undo the changes made by the
|
||||
* preceding @freeze_late().
|
||||
*
|
||||
* @poweroff: Hibernation-specific, executed after saving a hibernation image.
|
||||
* Analogous to @suspend(), but it need not save the device's settings in
|
||||
* memory.
|
||||
* Subsystem-level @poweroff() is executed for all devices after invoking
|
||||
* subsystem-level @prepare() for all of them.
|
||||
*
|
||||
* @poweroff_late: Continue operations started by @poweroff(). Analogous to
|
||||
* @suspend_late(), but it need not save the device's settings in memory.
|
||||
*
|
||||
* @restore: Hibernation-specific, executed after restoring the contents of main
|
||||
* memory from a hibernation image, analogous to @resume().
|
||||
*
|
||||
* @restore_early: Prepare to execute @restore(), analogous to @resume_early().
|
||||
*
|
||||
* @suspend_noirq: Complete the actions started by @suspend(). Carry out any
|
||||
* additional operations required for suspending the device that might be
|
||||
* racing with its driver's interrupt handler, which is guaranteed not to
|
||||
@ -158,9 +178,10 @@ typedef struct pm_message {
|
||||
* @suspend_noirq() has returned successfully. If the device can generate
|
||||
* system wakeup signals and is enabled to wake up the system, it should be
|
||||
* configured to do so at that time. However, depending on the platform
|
||||
* and device's subsystem, @suspend() may be allowed to put the device into
|
||||
* the low-power state and configure it to generate wakeup signals, in
|
||||
* which case it generally is not necessary to define @suspend_noirq().
|
||||
* and device's subsystem, @suspend() or @suspend_late() may be allowed to
|
||||
* put the device into the low-power state and configure it to generate
|
||||
* wakeup signals, in which case it generally is not necessary to define
|
||||
* @suspend_noirq().
|
||||
*
|
||||
* @resume_noirq: Prepare for the execution of @resume() by carrying out any
|
||||
* operations required for resuming the device that might be racing with
|
||||
@ -171,9 +192,9 @@ typedef struct pm_message {
|
||||
* additional operations required for freezing the device that might be
|
||||
* racing with its driver's interrupt handler, which is guaranteed not to
|
||||
* run while @freeze_noirq() is being executed.
|
||||
* The power state of the device should not be changed by either @freeze()
|
||||
* or @freeze_noirq() and it should not be configured to signal system
|
||||
* wakeup by any of these callbacks.
|
||||
* The power state of the device should not be changed by either @freeze(),
|
||||
* or @freeze_late(), or @freeze_noirq() and it should not be configured to
|
||||
* signal system wakeup by any of these callbacks.
|
||||
*
|
||||
* @thaw_noirq: Prepare for the execution of @thaw() by carrying out any
|
||||
* operations required for thawing the device that might be racing with its
|
||||
@ -249,6 +270,12 @@ struct dev_pm_ops {
|
||||
int (*thaw)(struct device *dev);
|
||||
int (*poweroff)(struct device *dev);
|
||||
int (*restore)(struct device *dev);
|
||||
int (*suspend_late)(struct device *dev);
|
||||
int (*resume_early)(struct device *dev);
|
||||
int (*freeze_late)(struct device *dev);
|
||||
int (*thaw_early)(struct device *dev);
|
||||
int (*poweroff_late)(struct device *dev);
|
||||
int (*restore_early)(struct device *dev);
|
||||
int (*suspend_noirq)(struct device *dev);
|
||||
int (*resume_noirq)(struct device *dev);
|
||||
int (*freeze_noirq)(struct device *dev);
|
||||
@ -293,6 +320,15 @@ const struct dev_pm_ops name = { \
|
||||
/*
|
||||
* Use this for defining a set of PM operations to be used in all situations
|
||||
* (sustem suspend, hibernation or runtime PM).
|
||||
* NOTE: In general, system suspend callbacks, .suspend() and .resume(), should
|
||||
* be different from the corresponding runtime PM callbacks, .runtime_suspend(),
|
||||
* and .runtime_resume(), because .runtime_suspend() always works on an already
|
||||
* quiescent device, while .suspend() should assume that the device may be doing
|
||||
* something when it is called (it should ensure that the device will be
|
||||
* quiescent after it has returned). Therefore it's better to point the "late"
|
||||
* suspend and "early" resume callback pointers, .suspend_late() and
|
||||
* .resume_early(), to the same routines as .runtime_suspend() and
|
||||
* .runtime_resume(), respectively (and analogously for hibernation).
|
||||
*/
|
||||
#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \
|
||||
const struct dev_pm_ops name = { \
|
||||
@ -584,13 +620,13 @@ struct dev_pm_domain {
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern void device_pm_lock(void);
|
||||
extern void dpm_resume_noirq(pm_message_t state);
|
||||
extern void dpm_resume_start(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_end(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);
|
||||
@ -605,17 +641,23 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
|
||||
extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
|
||||
|
||||
extern int pm_generic_prepare(struct device *dev);
|
||||
extern int pm_generic_suspend_late(struct device *dev);
|
||||
extern int pm_generic_suspend_noirq(struct device *dev);
|
||||
extern int pm_generic_suspend(struct device *dev);
|
||||
extern int pm_generic_resume_early(struct device *dev);
|
||||
extern int pm_generic_resume_noirq(struct device *dev);
|
||||
extern int pm_generic_resume(struct device *dev);
|
||||
extern int pm_generic_freeze_noirq(struct device *dev);
|
||||
extern int pm_generic_freeze_late(struct device *dev);
|
||||
extern int pm_generic_freeze(struct device *dev);
|
||||
extern int pm_generic_thaw_noirq(struct device *dev);
|
||||
extern int pm_generic_thaw_early(struct device *dev);
|
||||
extern int pm_generic_thaw(struct device *dev);
|
||||
extern int pm_generic_restore_noirq(struct device *dev);
|
||||
extern int pm_generic_restore_early(struct device *dev);
|
||||
extern int pm_generic_restore(struct device *dev);
|
||||
extern int pm_generic_poweroff_noirq(struct device *dev);
|
||||
extern int pm_generic_poweroff_late(struct device *dev);
|
||||
extern int pm_generic_poweroff(struct device *dev);
|
||||
extern void pm_generic_complete(struct device *dev);
|
||||
|
||||
|
@ -41,7 +41,7 @@
|
||||
* @active: Status of the wakeup source.
|
||||
*/
|
||||
struct wakeup_source {
|
||||
char *name;
|
||||
const char *name;
|
||||
struct list_head entry;
|
||||
spinlock_t lock;
|
||||
struct timer_list timer;
|
||||
@ -73,7 +73,9 @@ static inline bool device_may_wakeup(struct device *dev)
|
||||
}
|
||||
|
||||
/* drivers/base/power/wakeup.c */
|
||||
extern void wakeup_source_prepare(struct wakeup_source *ws, const char *name);
|
||||
extern struct wakeup_source *wakeup_source_create(const char *name);
|
||||
extern void wakeup_source_drop(struct wakeup_source *ws);
|
||||
extern void wakeup_source_destroy(struct wakeup_source *ws);
|
||||
extern void wakeup_source_add(struct wakeup_source *ws);
|
||||
extern void wakeup_source_remove(struct wakeup_source *ws);
|
||||
@ -103,11 +105,16 @@ static inline bool device_can_wakeup(struct device *dev)
|
||||
return dev->power.can_wakeup;
|
||||
}
|
||||
|
||||
static inline void wakeup_source_prepare(struct wakeup_source *ws,
|
||||
const char *name) {}
|
||||
|
||||
static inline struct wakeup_source *wakeup_source_create(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void wakeup_source_drop(struct wakeup_source *ws) {}
|
||||
|
||||
static inline void wakeup_source_destroy(struct wakeup_source *ws) {}
|
||||
|
||||
static inline void wakeup_source_add(struct wakeup_source *ws) {}
|
||||
@ -165,4 +172,17 @@ static inline void pm_wakeup_event(struct device *dev, unsigned int msec) {}
|
||||
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
|
||||
static inline void wakeup_source_init(struct wakeup_source *ws,
|
||||
const char *name)
|
||||
{
|
||||
wakeup_source_prepare(ws, name);
|
||||
wakeup_source_add(ws);
|
||||
}
|
||||
|
||||
static inline void wakeup_source_trash(struct wakeup_source *ws)
|
||||
{
|
||||
wakeup_source_remove(ws);
|
||||
wakeup_source_drop(ws);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_PM_WAKEUP_H */
|
||||
|
@ -42,8 +42,10 @@ enum suspend_stat_step {
|
||||
SUSPEND_FREEZE = 1,
|
||||
SUSPEND_PREPARE,
|
||||
SUSPEND_SUSPEND,
|
||||
SUSPEND_SUSPEND_LATE,
|
||||
SUSPEND_SUSPEND_NOIRQ,
|
||||
SUSPEND_RESUME_NOIRQ,
|
||||
SUSPEND_RESUME_EARLY,
|
||||
SUSPEND_RESUME
|
||||
};
|
||||
|
||||
@ -53,8 +55,10 @@ struct suspend_stats {
|
||||
int failed_freeze;
|
||||
int failed_prepare;
|
||||
int failed_suspend;
|
||||
int failed_suspend_late;
|
||||
int failed_suspend_noirq;
|
||||
int failed_resume;
|
||||
int failed_resume_early;
|
||||
int failed_resume_noirq;
|
||||
#define REC_FAILED_NUM 2
|
||||
int last_failed_dev;
|
||||
|
@ -424,7 +424,7 @@ void daemonize(const char *name, ...)
|
||||
*/
|
||||
exit_mm(current);
|
||||
/*
|
||||
* We don't want to have TIF_FREEZE set if the system-wide hibernation
|
||||
* We don't want to get frozen, in case system-wide hibernation
|
||||
* or suspend transition begins right now.
|
||||
*/
|
||||
current->flags |= (PF_NOFREEZE | PF_KTHREAD);
|
||||
|
@ -99,9 +99,9 @@ static void fake_signal_wake_up(struct task_struct *p)
|
||||
* freeze_task - send a freeze request to given task
|
||||
* @p: task to send the request to
|
||||
*
|
||||
* If @p is freezing, the freeze request is sent by setting %TIF_FREEZE
|
||||
* flag and either sending a fake signal to it or waking it up, depending
|
||||
* on whether it has %PF_FREEZER_NOSIG set.
|
||||
* If @p is freezing, the freeze request is sent either by sending a fake
|
||||
* signal (if it's not a kernel thread) or waking it up (if it's a kernel
|
||||
* thread).
|
||||
*
|
||||
* RETURNS:
|
||||
* %false, if @p is not freezing or already frozen; %true, otherwise
|
||||
|
@ -1546,13 +1546,13 @@ int kernel_kexec(void)
|
||||
if (error)
|
||||
goto Resume_console;
|
||||
/* At this point, dpm_suspend_start() has been called,
|
||||
* but *not* dpm_suspend_noirq(). We *must* call
|
||||
* dpm_suspend_noirq() now. Otherwise, drivers for
|
||||
* but *not* dpm_suspend_end(). We *must* call
|
||||
* dpm_suspend_end() now. Otherwise, drivers for
|
||||
* some devices (e.g. interrupt controllers) become
|
||||
* desynchronized with the actual state of the
|
||||
* hardware at resume time, and evil weirdness ensues.
|
||||
*/
|
||||
error = dpm_suspend_noirq(PMSG_FREEZE);
|
||||
error = dpm_suspend_end(PMSG_FREEZE);
|
||||
if (error)
|
||||
goto Resume_devices;
|
||||
error = disable_nonboot_cpus();
|
||||
@ -1579,7 +1579,7 @@ int kernel_kexec(void)
|
||||
local_irq_enable();
|
||||
Enable_cpus:
|
||||
enable_nonboot_cpus();
|
||||
dpm_resume_noirq(PMSG_RESTORE);
|
||||
dpm_resume_start(PMSG_RESTORE);
|
||||
Resume_devices:
|
||||
dpm_resume_end(PMSG_RESTORE);
|
||||
Resume_console:
|
||||
|
@ -245,8 +245,8 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
|
||||
* create_image - Create a hibernation image.
|
||||
* @platform_mode: Whether or not to use the platform driver.
|
||||
*
|
||||
* Execute device drivers' .freeze_noirq() callbacks, create a hibernation image
|
||||
* and execute the drivers' .thaw_noirq() callbacks.
|
||||
* Execute device drivers' "late" and "noirq" freeze callbacks, create a
|
||||
* hibernation image and run the drivers' "noirq" and "early" thaw callbacks.
|
||||
*
|
||||
* Control reappears in this routine after the subsequent restore.
|
||||
*/
|
||||
@ -254,7 +254,7 @@ static int create_image(int platform_mode)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = dpm_suspend_noirq(PMSG_FREEZE);
|
||||
error = dpm_suspend_end(PMSG_FREEZE);
|
||||
if (error) {
|
||||
printk(KERN_ERR "PM: Some devices failed to power down, "
|
||||
"aborting hibernation\n");
|
||||
@ -306,7 +306,7 @@ static int create_image(int platform_mode)
|
||||
Platform_finish:
|
||||
platform_finish(platform_mode);
|
||||
|
||||
dpm_resume_noirq(in_suspend ?
|
||||
dpm_resume_start(in_suspend ?
|
||||
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
|
||||
|
||||
return error;
|
||||
@ -343,13 +343,13 @@ int hibernation_snapshot(int platform_mode)
|
||||
* successful freezer test.
|
||||
*/
|
||||
freezer_test_done = true;
|
||||
goto Cleanup;
|
||||
goto Thaw;
|
||||
}
|
||||
|
||||
error = dpm_prepare(PMSG_FREEZE);
|
||||
if (error) {
|
||||
dpm_complete(PMSG_RECOVER);
|
||||
goto Cleanup;
|
||||
goto Thaw;
|
||||
}
|
||||
|
||||
suspend_console();
|
||||
@ -385,6 +385,8 @@ int hibernation_snapshot(int platform_mode)
|
||||
platform_end(platform_mode);
|
||||
return error;
|
||||
|
||||
Thaw:
|
||||
thaw_kernel_threads();
|
||||
Cleanup:
|
||||
swsusp_free();
|
||||
goto Close;
|
||||
@ -394,16 +396,16 @@ int hibernation_snapshot(int platform_mode)
|
||||
* resume_target_kernel - Restore system state from a hibernation image.
|
||||
* @platform_mode: Whether or not to use the platform driver.
|
||||
*
|
||||
* Execute device drivers' .freeze_noirq() callbacks, 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.
|
||||
* Execute device drivers' "noirq" and "late" freeze callbacks, 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(bool platform_mode)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = dpm_suspend_noirq(PMSG_QUIESCE);
|
||||
error = dpm_suspend_end(PMSG_QUIESCE);
|
||||
if (error) {
|
||||
printk(KERN_ERR "PM: Some devices failed to power down, "
|
||||
"aborting resume\n");
|
||||
@ -460,7 +462,7 @@ static int resume_target_kernel(bool platform_mode)
|
||||
Cleanup:
|
||||
platform_restore_cleanup(platform_mode);
|
||||
|
||||
dpm_resume_noirq(PMSG_RECOVER);
|
||||
dpm_resume_start(PMSG_RECOVER);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -518,7 +520,7 @@ int hibernation_platform_enter(void)
|
||||
goto Resume_devices;
|
||||
}
|
||||
|
||||
error = dpm_suspend_noirq(PMSG_HIBERNATE);
|
||||
error = dpm_suspend_end(PMSG_HIBERNATE);
|
||||
if (error)
|
||||
goto Resume_devices;
|
||||
|
||||
@ -549,7 +551,7 @@ int hibernation_platform_enter(void)
|
||||
Platform_finish:
|
||||
hibernation_ops->finish();
|
||||
|
||||
dpm_resume_noirq(PMSG_RESTORE);
|
||||
dpm_resume_start(PMSG_RESTORE);
|
||||
|
||||
Resume_devices:
|
||||
entering_platform_hibernation = false;
|
||||
@ -616,7 +618,7 @@ int hibernate(void)
|
||||
/* Allocate memory management structures */
|
||||
error = create_basic_memory_bitmaps();
|
||||
if (error)
|
||||
goto Exit;
|
||||
goto Enable_umh;
|
||||
|
||||
printk(KERN_INFO "PM: Syncing filesystems ... ");
|
||||
sys_sync();
|
||||
@ -624,15 +626,11 @@ int hibernate(void)
|
||||
|
||||
error = freeze_processes();
|
||||
if (error)
|
||||
goto Finish;
|
||||
goto Free_bitmaps;
|
||||
|
||||
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
|
||||
if (error)
|
||||
if (error || freezer_test_done)
|
||||
goto Thaw;
|
||||
if (freezer_test_done) {
|
||||
freezer_test_done = false;
|
||||
goto Thaw;
|
||||
}
|
||||
|
||||
if (in_suspend) {
|
||||
unsigned int flags = 0;
|
||||
@ -657,8 +655,13 @@ int hibernate(void)
|
||||
|
||||
Thaw:
|
||||
thaw_processes();
|
||||
Finish:
|
||||
|
||||
/* Don't bother checking whether freezer_test_done is true */
|
||||
freezer_test_done = false;
|
||||
|
||||
Free_bitmaps:
|
||||
free_basic_memory_bitmaps();
|
||||
Enable_umh:
|
||||
usermodehelper_enable();
|
||||
Exit:
|
||||
pm_notifier_call_chain(PM_POST_HIBERNATION);
|
||||
|
@ -165,16 +165,20 @@ static int suspend_stats_show(struct seq_file *s, void *unused)
|
||||
last_errno %= REC_FAILED_NUM;
|
||||
last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
|
||||
last_step %= REC_FAILED_NUM;
|
||||
seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
|
||||
"%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
|
||||
seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n"
|
||||
"%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
|
||||
"success", suspend_stats.success,
|
||||
"fail", suspend_stats.fail,
|
||||
"failed_freeze", suspend_stats.failed_freeze,
|
||||
"failed_prepare", suspend_stats.failed_prepare,
|
||||
"failed_suspend", suspend_stats.failed_suspend,
|
||||
"failed_suspend_late",
|
||||
suspend_stats.failed_suspend_late,
|
||||
"failed_suspend_noirq",
|
||||
suspend_stats.failed_suspend_noirq,
|
||||
"failed_resume", suspend_stats.failed_resume,
|
||||
"failed_resume_early",
|
||||
suspend_stats.failed_resume_early,
|
||||
"failed_resume_noirq",
|
||||
suspend_stats.failed_resume_noirq);
|
||||
seq_printf(s, "failures:\n last_failed_dev:\t%-s\n",
|
||||
@ -287,16 +291,10 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
|
||||
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
|
||||
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
|
||||
error = pm_suspend(state);
|
||||
break;
|
||||
}
|
||||
if (state < PM_SUSPEND_MAX && *s) {
|
||||
error = enter_state(state);
|
||||
if (error) {
|
||||
suspend_stats.fail++;
|
||||
dpm_save_failed_errno(error);
|
||||
} else
|
||||
suspend_stats.success++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -177,13 +177,11 @@ extern const char *const pm_states[];
|
||||
|
||||
extern bool valid_state(suspend_state_t state);
|
||||
extern int suspend_devices_and_enter(suspend_state_t state);
|
||||
extern int enter_state(suspend_state_t state);
|
||||
#else /* !CONFIG_SUSPEND */
|
||||
static inline int suspend_devices_and_enter(suspend_state_t state)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline int enter_state(suspend_state_t state) { return -ENOSYS; }
|
||||
static inline bool valid_state(suspend_state_t state) { return false; }
|
||||
#endif /* !CONFIG_SUSPEND */
|
||||
|
||||
@ -234,16 +232,14 @@ static inline int suspend_freeze_processes(void)
|
||||
int error;
|
||||
|
||||
error = freeze_processes();
|
||||
|
||||
/*
|
||||
* freeze_processes() automatically thaws every task if freezing
|
||||
* fails. So we need not do anything extra upon error.
|
||||
*/
|
||||
if (error)
|
||||
goto Finish;
|
||||
return error;
|
||||
|
||||
error = freeze_kernel_threads();
|
||||
|
||||
/*
|
||||
* freeze_kernel_threads() thaws only kernel threads upon freezing
|
||||
* failure. So we have to thaw the userspace tasks ourselves.
|
||||
@ -251,7 +247,6 @@ static inline int suspend_freeze_processes(void)
|
||||
if (error)
|
||||
thaw_processes();
|
||||
|
||||
Finish:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -53,11 +53,9 @@ static int try_to_freeze_tasks(bool user_only)
|
||||
* It is "frozen enough". If the task does wake
|
||||
* up, it will immediately call try_to_freeze.
|
||||
*
|
||||
* Because freeze_task() goes through p's
|
||||
* scheduler lock after setting TIF_FREEZE, it's
|
||||
* guaranteed that either we see TASK_RUNNING or
|
||||
* try_to_stop() after schedule() in ptrace/signal
|
||||
* stop sees TIF_FREEZE.
|
||||
* Because freeze_task() goes through p's scheduler lock, it's
|
||||
* guaranteed that TASK_STOPPED/TRACED -> TASK_RUNNING
|
||||
* transition can't race with task state testing here.
|
||||
*/
|
||||
if (!task_is_stopped_or_traced(p) &&
|
||||
!freezer_should_skip(p))
|
||||
@ -98,13 +96,15 @@ static int try_to_freeze_tasks(bool user_only)
|
||||
elapsed_csecs / 100, elapsed_csecs % 100,
|
||||
todo - wq_busy, wq_busy);
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
do_each_thread(g, p) {
|
||||
if (!wakeup && !freezer_should_skip(p) &&
|
||||
p != current && freezing(p) && !frozen(p))
|
||||
sched_show_task(p);
|
||||
} while_each_thread(g, p);
|
||||
read_unlock(&tasklist_lock);
|
||||
if (!wakeup) {
|
||||
read_lock(&tasklist_lock);
|
||||
do_each_thread(g, p) {
|
||||
if (p != current && !freezer_should_skip(p)
|
||||
&& freezing(p) && !frozen(p))
|
||||
sched_show_task(p);
|
||||
} while_each_thread(g, p);
|
||||
read_unlock(&tasklist_lock);
|
||||
}
|
||||
} else {
|
||||
printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
|
||||
elapsed_csecs % 100);
|
||||
|
@ -711,9 +711,10 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
|
||||
list_for_each_entry(region, &nosave_regions, list) {
|
||||
unsigned long pfn;
|
||||
|
||||
pr_debug("PM: Marking nosave pages: %016lx - %016lx\n",
|
||||
region->start_pfn << PAGE_SHIFT,
|
||||
region->end_pfn << PAGE_SHIFT);
|
||||
pr_debug("PM: Marking nosave pages: [mem %#010llx-%#010llx]\n",
|
||||
(unsigned long long) region->start_pfn << PAGE_SHIFT,
|
||||
((unsigned long long) region->end_pfn << PAGE_SHIFT)
|
||||
- 1);
|
||||
|
||||
for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++)
|
||||
if (pfn_valid(pfn)) {
|
||||
|
@ -37,8 +37,8 @@ const char *const pm_states[PM_SUSPEND_MAX] = {
|
||||
static const struct platform_suspend_ops *suspend_ops;
|
||||
|
||||
/**
|
||||
* suspend_set_ops - Set the global suspend method table.
|
||||
* @ops: Pointer to ops structure.
|
||||
* suspend_set_ops - Set the global suspend method table.
|
||||
* @ops: Suspend operations to use.
|
||||
*/
|
||||
void suspend_set_ops(const struct platform_suspend_ops *ops)
|
||||
{
|
||||
@ -58,11 +58,11 @@ bool valid_state(suspend_state_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* suspend_valid_only_mem - generic memory-only valid callback
|
||||
* suspend_valid_only_mem - Generic memory-only valid callback.
|
||||
*
|
||||
* Platform drivers that implement mem suspend only and only need
|
||||
* to check for that in their .valid callback can use this instead
|
||||
* of rolling their own .valid callback.
|
||||
* Platform drivers that implement mem suspend only and only need to check for
|
||||
* that in their .valid() callback can use this instead of rolling their own
|
||||
* .valid() callback.
|
||||
*/
|
||||
int suspend_valid_only_mem(suspend_state_t state)
|
||||
{
|
||||
@ -83,10 +83,11 @@ static int suspend_test(int level)
|
||||
}
|
||||
|
||||
/**
|
||||
* suspend_prepare - Do prep work before entering low-power state.
|
||||
* suspend_prepare - Prepare for entering system sleep state.
|
||||
*
|
||||
* This is common code that is called for each state that we're entering.
|
||||
* Run suspend notifiers, allocate a console and stop all processes.
|
||||
* Common code run for every system sleep state that can be entered (except for
|
||||
* hibernation). Run suspend notifiers, allocate the "suspend" console and
|
||||
* freeze processes.
|
||||
*/
|
||||
static int suspend_prepare(void)
|
||||
{
|
||||
@ -131,9 +132,9 @@ void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* suspend_enter - enter the desired system sleep state.
|
||||
* @state: State to enter
|
||||
* @wakeup: Returns information that suspend should not be entered again.
|
||||
* suspend_enter - Make the system enter the given sleep state.
|
||||
* @state: System sleep state to enter.
|
||||
* @wakeup: Returns information that the sleep state should not be re-entered.
|
||||
*
|
||||
* This function should be called after devices have been suspended.
|
||||
*/
|
||||
@ -147,7 +148,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
||||
goto Platform_finish;
|
||||
}
|
||||
|
||||
error = dpm_suspend_noirq(PMSG_SUSPEND);
|
||||
error = dpm_suspend_end(PMSG_SUSPEND);
|
||||
if (error) {
|
||||
printk(KERN_ERR "PM: Some devices failed to power down\n");
|
||||
goto Platform_finish;
|
||||
@ -189,7 +190,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
||||
if (suspend_ops->wake)
|
||||
suspend_ops->wake();
|
||||
|
||||
dpm_resume_noirq(PMSG_RESUME);
|
||||
dpm_resume_start(PMSG_RESUME);
|
||||
|
||||
Platform_finish:
|
||||
if (suspend_ops->finish)
|
||||
@ -199,9 +200,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
||||
}
|
||||
|
||||
/**
|
||||
* suspend_devices_and_enter - suspend devices and enter the desired system
|
||||
* sleep state.
|
||||
* @state: state to enter
|
||||
* suspend_devices_and_enter - Suspend devices and enter system sleep state.
|
||||
* @state: System sleep state to enter.
|
||||
*/
|
||||
int suspend_devices_and_enter(suspend_state_t state)
|
||||
{
|
||||
@ -251,10 +251,10 @@ int suspend_devices_and_enter(suspend_state_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* suspend_finish - Do final work before exiting suspend sequence.
|
||||
* suspend_finish - Clean up before finishing the suspend sequence.
|
||||
*
|
||||
* Call platform code to clean up, restart processes, and free the
|
||||
* console that we've allocated. This is not called for suspend-to-disk.
|
||||
* Call platform code to clean up, restart processes, and free the console that
|
||||
* we've allocated. This routine is not called for hibernation.
|
||||
*/
|
||||
static void suspend_finish(void)
|
||||
{
|
||||
@ -265,16 +265,14 @@ static void suspend_finish(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* enter_state - Do common work of entering low-power state.
|
||||
* @state: pm_state structure for state we're entering.
|
||||
* enter_state - Do common work needed to enter system sleep state.
|
||||
* @state: System sleep state to enter.
|
||||
*
|
||||
* Make sure we're the only ones trying to enter a sleep state. Fail
|
||||
* if someone has beat us to it, since we don't want anything weird to
|
||||
* happen when we wake up.
|
||||
* Then, do the setup for suspend, enter the state, and cleaup (after
|
||||
* we've woken up).
|
||||
* Make sure that no one else is trying to put the system into a sleep state.
|
||||
* Fail if that's not the case. Otherwise, prepare for system suspend, make the
|
||||
* system enter the given sleep state and clean up after wakeup.
|
||||
*/
|
||||
int enter_state(suspend_state_t state)
|
||||
static int enter_state(suspend_state_t state)
|
||||
{
|
||||
int error;
|
||||
|
||||
@ -310,24 +308,26 @@ int enter_state(suspend_state_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_suspend - Externally visible function for suspending system.
|
||||
* @state: Enumerated value of state to enter.
|
||||
* pm_suspend - Externally visible function for suspending the system.
|
||||
* @state: System sleep state to enter.
|
||||
*
|
||||
* Determine whether or not value is within range, get state
|
||||
* structure, and enter (above).
|
||||
* Check if the value of @state represents one of the supported states,
|
||||
* execute enter_state() and update system suspend statistics.
|
||||
*/
|
||||
int pm_suspend(suspend_state_t state)
|
||||
{
|
||||
int ret;
|
||||
if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) {
|
||||
ret = enter_state(state);
|
||||
if (ret) {
|
||||
suspend_stats.fail++;
|
||||
dpm_save_failed_errno(ret);
|
||||
} else
|
||||
suspend_stats.success++;
|
||||
return ret;
|
||||
int error;
|
||||
|
||||
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
error = enter_state(state);
|
||||
if (error) {
|
||||
suspend_stats.fail++;
|
||||
dpm_save_failed_errno(error);
|
||||
} else {
|
||||
suspend_stats.success++;
|
||||
}
|
||||
return -EINVAL;
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(pm_suspend);
|
||||
|
@ -249,16 +249,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
||||
}
|
||||
pm_restore_gfp_mask();
|
||||
error = hibernation_snapshot(data->platform_support);
|
||||
if (error) {
|
||||
thaw_kernel_threads();
|
||||
} else {
|
||||
if (!error) {
|
||||
error = put_user(in_suspend, (int __user *)arg);
|
||||
if (!error && !freezer_test_done)
|
||||
data->ready = 1;
|
||||
if (freezer_test_done) {
|
||||
freezer_test_done = false;
|
||||
thaw_kernel_threads();
|
||||
}
|
||||
data->ready = !freezer_test_done && !error;
|
||||
freezer_test_done = false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user