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:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
Reference in New Issue
Block a user