mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
PM: sleep: Rearrange suspend/resume error handling in the core
Notice that device_suspend_noirq(), device_suspend_late() and device_suspend() all set async_error on errors, so they don't really need to return a value. Accordingly, make them all void and use async_error in their callers instead of their return values. Moreover, since async_error is updated concurrently without locking during asynchronous suspend and resume processing, use READ_ONCE() and WRITE_ONCE() for accessing it in those places to ensure that all of the accesses will be carried out as expected. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Saravana Kannan <saravanak@google.com> Link: https://patch.msgid.link/6198088.lOV4Wx5bFT@rjwysocki.net
This commit is contained in:
parent
7c1f7c22e6
commit
dbd4bccd96
|
|
@ -767,7 +767,7 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
|
|||
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);
|
||||
}
|
||||
|
|
@ -824,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);
|
||||
|
|
@ -910,7 +910,7 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
|
|||
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);
|
||||
}
|
||||
|
|
@ -971,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);
|
||||
|
|
@ -1086,7 +1086,7 @@ 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);
|
||||
}
|
||||
|
|
@ -1150,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();
|
||||
|
|
@ -1387,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;
|
||||
|
|
@ -1398,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)
|
||||
|
|
@ -1431,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;
|
||||
|
|
@ -1457,12 +1457,10 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy
|
|||
complete_all(&dev->power.completion);
|
||||
TRACE_SUSPEND(error);
|
||||
|
||||
if (error || async_error)
|
||||
return error;
|
||||
if (error || READ_ONCE(async_error))
|
||||
return;
|
||||
|
||||
dpm_async_suspend_superior(dev, async_suspend_noirq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void async_suspend_noirq(void *data, async_cookie_t cookie)
|
||||
|
|
@ -1477,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);
|
||||
|
||||
|
|
@ -1508,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
|
||||
|
|
@ -1528,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);
|
||||
|
||||
|
|
@ -1585,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;
|
||||
|
|
@ -1602,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;
|
||||
}
|
||||
|
||||
|
|
@ -1640,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;
|
||||
|
|
@ -1654,12 +1651,10 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn
|
|||
TRACE_SUSPEND(error);
|
||||
complete_all(&dev->power.completion);
|
||||
|
||||
if (error || async_error)
|
||||
return error;
|
||||
if (error || READ_ONCE(async_error))
|
||||
return;
|
||||
|
||||
dpm_async_suspend_superior(dev, async_suspend_late);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void async_suspend_late(void *data, async_cookie_t cookie)
|
||||
|
|
@ -1678,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);
|
||||
|
||||
|
|
@ -1711,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
|
||||
|
|
@ -1731,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));
|
||||
|
|
@ -1822,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;
|
||||
|
|
@ -1834,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;
|
||||
}
|
||||
|
|
@ -1854,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;
|
||||
}
|
||||
|
||||
|
|
@ -1938,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);
|
||||
}
|
||||
|
|
@ -1946,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_superior(dev, async_suspend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void async_suspend(void *data, async_cookie_t cookie)
|
||||
|
|
@ -1970,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();
|
||||
|
|
@ -2005,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
|
||||
|
|
@ -2025,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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user