1
0

Merge branch 'pm-sleep'

Merge updates related to system sleep for 6.17-rc1:

 - Extend the asynchronous suspend and resume of devices to handle
   suppliers like parents and consumers like children (Rafael Wysocki)

 - Make pm_runtime_force_resume() work for drivers that set the
   DPM_FLAG_SMART_SUSPEND flag and allow PCI drivers and drivers that
   collaborate with the general ACPI PM domain to set it (Rafael
   Wysocki)

 - Add kernel parameter to disable asynchronous suspend/resume of
   devices (Tudor Ambarus)

 - Drop redundant might_sleep() calls from some functions in the device
   suspend/resume core code (Zhongqiu Han)

 - Fix the handling of monitors connected right before waking up the
   system from sleep (tuhaowen)

 - Clean up MAINTAINERS entries for suspend and hibernation (Rafael
   Wysocki)

 - Fix error code path in the KEXEC_JUMP flow and drop a redundant
   pm_restore_gfp_mask() call from it (Rafael Wysocki)

 - Rearrange suspend/resume error handling in the core device suspend
   and resume code (Rafael Wysocki)

 - Fix up white space that does not follow coding style in the
   hibernation core code (Darshan Rathod)

* pm-sleep:
  PM: hibernate: Fix up white space that does not follow coding style
  PM: sleep: Rearrange suspend/resume error handling in the core
  kexec_core: Drop redundant pm_restore_gfp_mask() call
  kexec_core: Fix error code path in the KEXEC_JUMP flow
  PM: sleep: Clean up MAINTAINERS entries for suspend and hibernation
  PM: sleep: add kernel parameter to disable asynchronous suspend/resume
  PCI/PM: Set power.strict_midlayer in pci_pm_init()
  ACPI: PM: Set/clear power.strict_midlayer in prepare/complete
  PM: sleep: Add strict_midlayer flag to struct dev_pm_info
  PM: runtime: Introduce __rpm_get_driver_callback()
  PM: Check power.needs_force_resume in pm_runtime_force_suspend()
  PM: runtime: Clear power.needs_force_resume in pm_runtime_reinit()
  PM: Make pm_runtime_force_resume() work with DPM_FLAG_SMART_SUSPEND
  PM: Move two sleep-related functions under CONFIG_PM_SLEEP
  PM: Use true/false as power.needs_force_resume values
  PM: sleep: Make async suspend handle suppliers like parents
  PM: sleep: Make async resume handle consumers like children
  PM: sleep: Drop superfluous might_sleep() calls
  PM: sleep: console: Fix the black screen issue
This commit is contained in:
Rafael J. Wysocki
2025-07-22 17:49:34 +02:00
13 changed files with 268 additions and 118 deletions

View File

@@ -5000,6 +5000,18 @@
that number, otherwise (e.g., 'pmu_override=on'), MMCR1
remains 0.
pm_async= [PM]
Format: off
This parameter sets the initial value of the
/sys/power/pm_async sysfs knob at boot time.
If set to "off", disables asynchronous suspend and
resume of devices during system-wide power transitions.
This can be useful on platforms where device
dependencies are not well-defined, or for debugging
power management issues. Asynchronous operations are
enabled by default.
pm_debug_messages [SUSPEND,KNL]
Enable suspend/resume debug messages during boot up.

View File

@@ -9787,7 +9787,7 @@ F: fs/freevxfs/
FREEZER
M: "Rafael J. Wysocki" <rafael@kernel.org>
M: Pavel Machek <pavel@kernel.org>
R: Pavel Machek <pavel@kernel.org>
L: linux-pm@vger.kernel.org
S: Supported
F: Documentation/power/freezing-of-tasks.rst
@@ -10663,7 +10663,7 @@ F: drivers/video/fbdev/hgafb.c
HIBERNATION (aka Software Suspend, aka swsusp)
M: "Rafael J. Wysocki" <rafael@kernel.org>
M: Pavel Machek <pavel@kernel.org>
R: Pavel Machek <pavel@kernel.org>
L: linux-pm@vger.kernel.org
S: Supported
B: https://bugzilla.kernel.org
@@ -23938,8 +23938,8 @@ F: drivers/sh/
SUSPEND TO RAM
M: "Rafael J. Wysocki" <rafael@kernel.org>
M: Len Brown <len.brown@intel.com>
M: Pavel Machek <pavel@kernel.org>
R: Len Brown <lenb@kernel.org>
R: Pavel Machek <pavel@kernel.org>
L: linux-pm@vger.kernel.org
S: Supported
B: https://bugzilla.kernel.org

View File

@@ -1119,6 +1119,8 @@ int acpi_subsys_prepare(struct device *dev)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
dev_pm_set_strict_midlayer(dev, true);
if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) {
int ret = dev->driver->pm->prepare(dev);
@@ -1147,6 +1149,8 @@ void acpi_subsys_complete(struct device *dev)
*/
if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
pm_request_resume(dev);
dev_pm_set_strict_midlayer(dev, false);
}
EXPORT_SYMBOL_GPL(acpi_subsys_complete);

View File

@@ -647,14 +647,27 @@ static void dpm_async_resume_children(struct device *dev, async_func_t func)
/*
* Start processing "async" children of the device unless it's been
* started already for them.
*
* This could have been done for the device's "async" consumers too, but
* they either need to wait for their parents or the processing has
* already started for them after their parents were processed.
*/
device_for_each_child(dev, func, dpm_async_with_cleanup);
}
static void dpm_async_resume_subordinate(struct device *dev, async_func_t func)
{
struct device_link *link;
int idx;
dpm_async_resume_children(dev, func);
idx = device_links_read_lock();
/* Start processing the device's "async" consumers. */
list_for_each_entry_rcu(link, &dev->links.consumers, s_node)
if (READ_ONCE(link->status) != DL_STATE_DORMANT)
dpm_async_with_cleanup(link->consumer, func);
device_links_read_unlock(idx);
}
static void dpm_clear_async_state(struct device *dev)
{
reinit_completion(&dev->power.completion);
@@ -663,7 +676,14 @@ static void dpm_clear_async_state(struct device *dev)
static bool dpm_root_device(struct device *dev)
{
return !dev->parent;
lockdep_assert_held(&dpm_list_mtx);
/*
* Since this function is required to run under dpm_list_mtx, the
* list_empty() below will only return true if the device's list of
* consumers is actually empty before calling it.
*/
return !dev->parent && list_empty(&dev->links.suppliers);
}
static void async_resume_noirq(void *data, async_cookie_t cookie);
@@ -747,12 +767,12 @@ Out:
TRACE_RESUME(error);
if (error) {
async_error = error;
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
}
dpm_async_resume_children(dev, async_resume_noirq);
dpm_async_resume_subordinate(dev, async_resume_noirq);
}
static void async_resume_noirq(void *data, async_cookie_t cookie)
@@ -804,7 +824,7 @@ static void dpm_noirq_resume_devices(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, 0, "noirq");
if (async_error)
if (READ_ONCE(async_error))
dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
@@ -890,12 +910,12 @@ Out:
complete_all(&dev->power.completion);
if (error) {
async_error = error;
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async early" : " early", error);
}
dpm_async_resume_children(dev, async_resume_early);
dpm_async_resume_subordinate(dev, async_resume_early);
}
static void async_resume_early(void *data, async_cookie_t cookie)
@@ -951,7 +971,7 @@ void dpm_resume_early(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, 0, "early");
if (async_error)
if (READ_ONCE(async_error))
dpm_save_failed_step(SUSPEND_RESUME_EARLY);
trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
@@ -1066,12 +1086,12 @@ static void device_resume(struct device *dev, pm_message_t state, bool async)
TRACE_RESUME(error);
if (error) {
async_error = error;
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async" : "", error);
}
dpm_async_resume_children(dev, async_resume);
dpm_async_resume_subordinate(dev, async_resume);
}
static void async_resume(void *data, async_cookie_t cookie)
@@ -1095,7 +1115,6 @@ void dpm_resume(pm_message_t state)
ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume"), state.event, true);
might_sleep();
pm_transition = state;
async_error = 0;
@@ -1131,7 +1150,7 @@ void dpm_resume(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
dpm_show_time(starttime, state, 0, NULL);
if (async_error)
if (READ_ONCE(async_error))
dpm_save_failed_step(SUSPEND_RESUME);
cpufreq_resume();
@@ -1198,7 +1217,6 @@ void dpm_complete(pm_message_t state)
struct list_head list;
trace_suspend_resume(TPS("dpm_complete"), state.event, true);
might_sleep();
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
@@ -1258,10 +1276,15 @@ static bool dpm_leaf_device(struct device *dev)
return false;
}
return true;
/*
* Since this function is required to run under dpm_list_mtx, the
* list_empty() below will only return true if the device's list of
* consumers is actually empty before calling it.
*/
return list_empty(&dev->links.consumers);
}
static void dpm_async_suspend_parent(struct device *dev, async_func_t func)
static bool dpm_async_suspend_parent(struct device *dev, async_func_t func)
{
guard(mutex)(&dpm_list_mtx);
@@ -1273,11 +1296,31 @@ static void dpm_async_suspend_parent(struct device *dev, async_func_t func)
* deleted before it.
*/
if (!device_pm_initialized(dev))
return;
return false;
/* Start processing the device's parent if it is "async". */
if (dev->parent)
dpm_async_with_cleanup(dev->parent, func);
return true;
}
static void dpm_async_suspend_superior(struct device *dev, async_func_t func)
{
struct device_link *link;
int idx;
if (!dpm_async_suspend_parent(dev, func))
return;
idx = device_links_read_lock();
/* Start processing the device's "async" suppliers. */
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node)
if (READ_ONCE(link->status) != DL_STATE_DORMANT)
dpm_async_with_cleanup(link->supplier, func);
device_links_read_unlock(idx);
}
static void dpm_async_suspend_complete_all(struct list_head *device_list)
@@ -1344,7 +1387,7 @@ static void async_suspend_noirq(void *data, async_cookie_t cookie);
* The driver of @dev will not receive interrupts while this function is being
* executed.
*/
static int device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
static void device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -1355,7 +1398,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy
dpm_wait_for_subordinate(dev, async);
if (async_error)
if (READ_ONCE(async_error))
goto Complete;
if (dev->power.syscore || dev->power.direct_complete)
@@ -1388,7 +1431,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy
Run:
error = dpm_run_callback(callback, dev, state, info);
if (error) {
async_error = error;
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
goto Complete;
@@ -1414,12 +1457,10 @@ Complete:
complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
if (error || async_error)
return error;
if (error || READ_ONCE(async_error))
return;
dpm_async_suspend_parent(dev, async_suspend_noirq);
return 0;
dpm_async_suspend_superior(dev, async_suspend_noirq);
}
static void async_suspend_noirq(void *data, async_cookie_t cookie)
@@ -1434,7 +1475,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
{
ktime_t starttime = ktime_get();
struct device *dev;
int error = 0;
int error;
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
@@ -1465,13 +1506,13 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_suspend_noirq(dev, state, false);
device_suspend_noirq(dev, state, false);
put_device(dev);
mutex_lock(&dpm_list_mtx);
if (error || async_error) {
if (READ_ONCE(async_error)) {
dpm_async_suspend_complete_all(&dpm_late_early_list);
/*
* Move all devices to the target list to resume them
@@ -1485,9 +1526,8 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
error = READ_ONCE(async_error);
if (error)
dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
@@ -1542,7 +1582,7 @@ static void async_suspend_late(void *data, async_cookie_t cookie);
*
* Runtime PM is disabled for @dev while this function is being executed.
*/
static int device_suspend_late(struct device *dev, pm_message_t state, bool async)
static void device_suspend_late(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -1559,11 +1599,11 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn
dpm_wait_for_subordinate(dev, async);
if (async_error)
if (READ_ONCE(async_error))
goto Complete;
if (pm_wakeup_pending()) {
async_error = -EBUSY;
WRITE_ONCE(async_error, -EBUSY);
goto Complete;
}
@@ -1597,7 +1637,7 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn
Run:
error = dpm_run_callback(callback, dev, state, info);
if (error) {
async_error = error;
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async late" : " late", error);
goto Complete;
@@ -1611,12 +1651,10 @@ Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion);
if (error || async_error)
return error;
if (error || READ_ONCE(async_error))
return;
dpm_async_suspend_parent(dev, async_suspend_late);
return 0;
dpm_async_suspend_superior(dev, async_suspend_late);
}
static void async_suspend_late(void *data, async_cookie_t cookie)
@@ -1635,7 +1673,7 @@ int dpm_suspend_late(pm_message_t state)
{
ktime_t starttime = ktime_get();
struct device *dev;
int error = 0;
int error;
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
@@ -1668,13 +1706,13 @@ int dpm_suspend_late(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_suspend_late(dev, state, false);
device_suspend_late(dev, state, false);
put_device(dev);
mutex_lock(&dpm_list_mtx);
if (error || async_error) {
if (READ_ONCE(async_error)) {
dpm_async_suspend_complete_all(&dpm_suspended_list);
/*
* Move all devices to the target list to resume them
@@ -1688,9 +1726,8 @@ int dpm_suspend_late(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
error = READ_ONCE(async_error);
if (error) {
dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
dpm_resume_early(resume_event(state));
@@ -1779,7 +1816,7 @@ static void async_suspend(void *data, async_cookie_t cookie);
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
*/
static int device_suspend(struct device *dev, pm_message_t state, bool async)
static void device_suspend(struct device *dev, pm_message_t state, bool async)
{
pm_callback_t callback = NULL;
const char *info = NULL;
@@ -1791,7 +1828,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async)
dpm_wait_for_subordinate(dev, async);
if (async_error) {
if (READ_ONCE(async_error)) {
dev->power.direct_complete = false;
goto Complete;
}
@@ -1811,7 +1848,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async)
if (pm_wakeup_pending()) {
dev->power.direct_complete = false;
async_error = -EBUSY;
WRITE_ONCE(async_error, -EBUSY);
goto Complete;
}
@@ -1895,7 +1932,7 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async)
Complete:
if (error) {
async_error = error;
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async" : "", error);
}
@@ -1903,12 +1940,10 @@ static int device_suspend(struct device *dev, pm_message_t state, bool async)
complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
if (error || async_error)
return error;
if (error || READ_ONCE(async_error))
return;
dpm_async_suspend_parent(dev, async_suspend);
return 0;
dpm_async_suspend_superior(dev, async_suspend);
}
static void async_suspend(void *data, async_cookie_t cookie)
@@ -1927,7 +1962,7 @@ int dpm_suspend(pm_message_t state)
{
ktime_t starttime = ktime_get();
struct device *dev;
int error = 0;
int error;
trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
might_sleep();
@@ -1962,13 +1997,13 @@ int dpm_suspend(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
error = device_suspend(dev, state, false);
device_suspend(dev, state, false);
put_device(dev);
mutex_lock(&dpm_list_mtx);
if (error || async_error) {
if (READ_ONCE(async_error)) {
dpm_async_suspend_complete_all(&dpm_prepared_list);
/*
* Move all devices to the target list to resume them
@@ -1982,9 +2017,8 @@ int dpm_suspend(pm_message_t state)
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
error = READ_ONCE(async_error);
if (error)
dpm_save_failed_step(SUSPEND_SUSPEND);
@@ -2129,7 +2163,6 @@ int dpm_prepare(pm_message_t state)
int error = 0;
trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
might_sleep();
/*
* Give a chance for the known devices to complete their probes, before

View File

@@ -19,10 +19,24 @@
typedef int (*pm_callback_t)(struct device *);
static inline pm_callback_t get_callback_ptr(const void *start, size_t offset)
{
return *(pm_callback_t *)(start + offset);
}
static pm_callback_t __rpm_get_driver_callback(struct device *dev,
size_t cb_offset)
{
if (dev->driver && dev->driver->pm)
return get_callback_ptr(dev->driver->pm, cb_offset);
return NULL;
}
static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset)
{
pm_callback_t cb;
const struct dev_pm_ops *ops;
pm_callback_t cb = NULL;
if (dev->pm_domain)
ops = &dev->pm_domain->ops;
@@ -36,12 +50,10 @@ static pm_callback_t __rpm_get_callback(struct device *dev, size_t cb_offset)
ops = NULL;
if (ops)
cb = *(pm_callback_t *)((void *)ops + cb_offset);
else
cb = NULL;
cb = get_callback_ptr(ops, cb_offset);
if (!cb && dev->driver && dev->driver->pm)
cb = *(pm_callback_t *)((void *)dev->driver->pm + cb_offset);
if (!cb)
cb = __rpm_get_driver_callback(dev, cb_offset);
return cb;
}
@@ -1827,7 +1839,7 @@ void pm_runtime_init(struct device *dev)
dev->power.request_pending = false;
dev->power.request = RPM_REQ_NONE;
dev->power.deferred_resume = false;
dev->power.needs_force_resume = 0;
dev->power.needs_force_resume = false;
INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0;
@@ -1854,6 +1866,11 @@ void pm_runtime_reinit(struct device *dev)
pm_runtime_put(dev->parent);
}
}
/*
* Clear power.needs_force_resume in case it has been set by
* pm_runtime_force_suspend() invoked from a driver remove callback.
*/
dev->power.needs_force_resume = false;
}
/**
@@ -1941,13 +1958,23 @@ void pm_runtime_drop_link(struct device_link *link)
pm_request_idle(link->supplier);
}
bool pm_runtime_need_not_resume(struct device *dev)
static pm_callback_t get_callback(struct device *dev, size_t cb_offset)
{
return atomic_read(&dev->power.usage_count) <= 1 &&
(atomic_read(&dev->power.child_count) == 0 ||
dev->power.ignore_children);
/*
* Setting power.strict_midlayer means that the middle layer
* code does not want its runtime PM callbacks to be invoked via
* pm_runtime_force_suspend() and pm_runtime_force_resume(), so
* return a direct pointer to the driver callback in that case.
*/
if (dev_pm_strict_midlayer_is_set(dev))
return __rpm_get_driver_callback(dev, cb_offset);
return __rpm_get_callback(dev, cb_offset);
}
#define GET_CALLBACK(dev, callback) \
get_callback(dev, offsetof(struct dev_pm_ops, callback))
/**
* pm_runtime_force_suspend - Force a device into suspend state if needed.
* @dev: Device to suspend.
@@ -1964,10 +1991,6 @@ bool pm_runtime_need_not_resume(struct device *dev)
* sure the device is put into low power state and it should only be used during
* system-wide PM transitions to sleep states. It assumes that the analogous
* pm_runtime_force_resume() will be used to resume the device.
*
* Do not use with DPM_FLAG_SMART_SUSPEND as this can lead to an inconsistent
* state where this function has called the ->runtime_suspend callback but the
* PM core marks the driver as runtime active.
*/
int pm_runtime_force_suspend(struct device *dev)
{
@@ -1975,10 +1998,10 @@ int pm_runtime_force_suspend(struct device *dev)
int ret;
pm_runtime_disable(dev);
if (pm_runtime_status_suspended(dev))
if (pm_runtime_status_suspended(dev) || dev->power.needs_force_resume)
return 0;
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
callback = GET_CALLBACK(dev, runtime_suspend);
dev_pm_enable_wake_irq_check(dev, true);
ret = callback ? callback(dev) : 0;
@@ -1990,15 +2013,16 @@ int pm_runtime_force_suspend(struct device *dev)
/*
* If the device can stay in suspend after the system-wide transition
* to the working state that will follow, drop the children counter of
* its parent, but set its status to RPM_SUSPENDED anyway in case this
* function will be called again for it in the meantime.
* its parent and the usage counters of its suppliers. Otherwise, set
* power.needs_force_resume to let pm_runtime_force_resume() know that
* the device needs to be taken care of and to prevent this function
* from handling the device again in case the device is passed to it
* once more subsequently.
*/
if (pm_runtime_need_not_resume(dev)) {
if (pm_runtime_need_not_resume(dev))
pm_runtime_set_suspended(dev);
} else {
__update_runtime_status(dev, RPM_SUSPENDED);
dev->power.needs_force_resume = 1;
}
else
dev->power.needs_force_resume = true;
return 0;
@@ -2009,33 +2033,37 @@ err:
}
EXPORT_SYMBOL_GPL(pm_runtime_force_suspend);
#ifdef CONFIG_PM_SLEEP
/**
* pm_runtime_force_resume - Force a device into resume state if needed.
* @dev: Device to resume.
*
* Prior invoking this function we expect the user to have brought the device
* into low power state by a call to pm_runtime_force_suspend(). Here we reverse
* those actions and bring the device into full power, if it is expected to be
* used on system resume. In the other case, we defer the resume to be managed
* via runtime PM.
* This function expects that either pm_runtime_force_suspend() has put the
* device into a low-power state prior to calling it, or the device had been
* runtime-suspended before the preceding system-wide suspend transition and it
* was left in suspend during that transition.
*
* Typically this function may be invoked from a system resume callback.
* The actions carried out by pm_runtime_force_suspend(), or by a runtime
* suspend in general, are reversed and the device is brought back into full
* power if it is expected to be used on system resume, which is the case when
* its needs_force_resume flag is set or when its smart_suspend flag is set and
* its runtime PM status is "active".
*
* In other cases, the resume is deferred to be managed via runtime PM.
*
* Typically, this function may be invoked from a system resume callback.
*/
int pm_runtime_force_resume(struct device *dev)
{
int (*callback)(struct device *);
int ret = 0;
if (!dev->power.needs_force_resume)
if (!dev->power.needs_force_resume && (!dev_pm_smart_suspend(dev) ||
pm_runtime_status_suspended(dev)))
goto out;
/*
* The value of the parent's children counter is correct already, so
* just update the status of the device.
*/
__update_runtime_status(dev, RPM_ACTIVE);
callback = RPM_GET_CALLBACK(dev, runtime_resume);
callback = GET_CALLBACK(dev, runtime_resume);
dev_pm_disable_wake_irq_check(dev, false);
ret = callback ? callback(dev) : 0;
@@ -2046,9 +2074,30 @@ int pm_runtime_force_resume(struct device *dev)
}
pm_runtime_mark_last_busy(dev);
out:
dev->power.needs_force_resume = 0;
/*
* The smart_suspend flag can be cleared here because it is not going
* to be necessary until the next system-wide suspend transition that
* will update it again.
*/
dev->power.smart_suspend = false;
/*
* Also clear needs_force_resume to make this function skip devices that
* have been seen by it once.
*/
dev->power.needs_force_resume = false;
pm_runtime_enable(dev);
return ret;
}
EXPORT_SYMBOL_GPL(pm_runtime_force_resume);
bool pm_runtime_need_not_resume(struct device *dev)
{
return atomic_read(&dev->power.usage_count) <= 1 &&
(atomic_read(&dev->power.child_count) == 0 ||
dev->power.ignore_children);
}
#endif /* CONFIG_PM_SLEEP */

View File

@@ -708,6 +708,8 @@ static int pci_pm_prepare(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
dev_pm_set_strict_midlayer(dev, true);
if (pm && pm->prepare) {
int error = pm->prepare(dev);
if (error < 0)
@@ -749,6 +751,8 @@ static void pci_pm_complete(struct device *dev)
if (pci_dev->current_state < pre_sleep_state)
pm_request_resume(dev);
}
dev_pm_set_strict_midlayer(dev, false);
}
#else /* !CONFIG_PM_SLEEP */

View File

@@ -879,6 +879,33 @@ static inline bool dev_pm_smart_suspend(struct device *dev)
#endif
}
/*
* dev_pm_set_strict_midlayer - Update the device's power.strict_midlayer flag
* @dev: Target device.
* @val: New flag value.
*
* When set, power.strict_midlayer means that the middle layer power management
* code (typically, a bus type or a PM domain) does not expect its runtime PM
* suspend callback to be invoked at all during system-wide PM transitions and
* it does not expect its runtime PM resume callback to be invoked at any point
* when runtime PM is disabled for the device during system-wide PM transitions.
*/
static inline void dev_pm_set_strict_midlayer(struct device *dev, bool val)
{
#ifdef CONFIG_PM_SLEEP
dev->power.strict_midlayer = val;
#endif
}
static inline bool dev_pm_strict_midlayer_is_set(struct device *dev)
{
#ifdef CONFIG_PM_SLEEP
return dev->power.strict_midlayer;
#else
return false;
#endif
}
static inline void device_lock(struct device *dev)
{
mutex_lock(&dev->mutex);

View File

@@ -683,6 +683,7 @@ struct dev_pm_info {
bool smart_suspend:1; /* Owned by the PM core */
bool must_resume:1; /* Owned by the PM core */
bool may_skip_resume:1; /* Set by subsystems */
bool strict_midlayer:1;
#else
bool should_wakeup:1;
#endif

View File

@@ -66,9 +66,7 @@ static inline bool queue_pm_work(struct work_struct *work)
extern int pm_generic_runtime_suspend(struct device *dev);
extern int pm_generic_runtime_resume(struct device *dev);
extern bool pm_runtime_need_not_resume(struct device *dev);
extern int pm_runtime_force_suspend(struct device *dev);
extern int pm_runtime_force_resume(struct device *dev);
extern int __pm_runtime_idle(struct device *dev, int rpmflags);
extern int __pm_runtime_suspend(struct device *dev, int rpmflags);
@@ -257,9 +255,7 @@ static inline bool queue_pm_work(struct work_struct *work) { return false; }
static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
static inline bool pm_runtime_need_not_resume(struct device *dev) {return true; }
static inline int pm_runtime_force_suspend(struct device *dev) { return 0; }
static inline int pm_runtime_force_resume(struct device *dev) { return 0; }
static inline int __pm_runtime_idle(struct device *dev, int rpmflags)
{
@@ -330,6 +326,18 @@ static inline void pm_runtime_release_supplier(struct device_link *link) {}
#endif /* !CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
bool pm_runtime_need_not_resume(struct device *dev);
int pm_runtime_force_resume(struct device *dev);
#else /* !CONFIG_PM_SLEEP */
static inline bool pm_runtime_need_not_resume(struct device *dev) {return true; }
static inline int pm_runtime_force_resume(struct device *dev) { return -ENXIO; }
#endif /* CONFIG_PM_SLEEP */
/**
* pm_runtime_idle - Conditionally set up autosuspend of a device or suspend it.
* @dev: Target device.

View File

@@ -1080,7 +1080,7 @@ int kernel_kexec(void)
console_suspend_all();
error = dpm_suspend_start(PMSG_FREEZE);
if (error)
goto Resume_console;
goto Resume_devices;
/*
* dpm_suspend_end() must be called after dpm_suspend_start()
* to complete the transition, like in the hibernation flows
@@ -1135,8 +1135,6 @@ int kernel_kexec(void)
dpm_resume_start(PMSG_RESTORE);
Resume_devices:
dpm_resume_end(PMSG_RESTORE);
Resume_console:
pm_restore_gfp_mask();
console_resume_all();
thaw_processes();
Restore_console:

View File

@@ -16,6 +16,7 @@
#define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
static int orig_fgconsole, orig_kmsg;
static bool vt_switch_done;
static DEFINE_MUTEX(vt_switch_mutex);
@@ -136,17 +137,21 @@ void pm_prepare_console(void)
if (orig_fgconsole < 0)
return;
vt_switch_done = true;
orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
return;
}
void pm_restore_console(void)
{
if (!pm_vt_switch())
if (!pm_vt_switch() && !vt_switch_done)
return;
if (orig_fgconsole >= 0) {
vt_move_to_console(orig_fgconsole, 0);
vt_kmsg_redirect(orig_kmsg);
}
vt_switch_done = false;
}

View File

@@ -8,6 +8,7 @@
#include <linux/acpi.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/pm-trace.h>
@@ -112,6 +113,14 @@ int pm_notifier_call_chain(unsigned long val)
/* If set, devices may be suspended and resumed asynchronously. */
int pm_async_enabled = 1;
static int __init pm_async_setup(char *str)
{
if (!strcmp(str, "off"))
pm_async_enabled = 0;
return 1;
}
__setup("pm_async=", pm_async_setup);
static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{

View File

@@ -1536,7 +1536,7 @@ static unsigned long copy_data_pages(struct memory_bitmap *copy_bm,
memory_bm_position_reset(orig_bm);
memory_bm_position_reset(copy_bm);
copy_pfn = memory_bm_next_pfn(copy_bm);
for(;;) {
for (;;) {
pfn = memory_bm_next_pfn(orig_bm);
if (unlikely(pfn == BM_END_OF_MAP))
break;
@@ -2161,13 +2161,13 @@ static const char *check_image_kernel(struct swsusp_info *info)
{
if (info->version_code != LINUX_VERSION_CODE)
return "kernel version";
if (strcmp(info->uts.sysname,init_utsname()->sysname))
if (strcmp(info->uts.sysname, init_utsname()->sysname))
return "system type";
if (strcmp(info->uts.release,init_utsname()->release))
if (strcmp(info->uts.release, init_utsname()->release))
return "kernel release";
if (strcmp(info->uts.version,init_utsname()->version))
if (strcmp(info->uts.version, init_utsname()->version))
return "version";
if (strcmp(info->uts.machine,init_utsname()->machine))
if (strcmp(info->uts.machine, init_utsname()->machine))
return "machine";
return NULL;
}
@@ -2361,7 +2361,7 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm,
struct memory_bitmap *zero_bm)
{
unsigned long decoded_pfn;
bool zero;
bool zero;
int j;
for (j = 0; j < PAGE_SIZE / sizeof(long); j++) {