]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PM: sleep: Rearrange suspend/resume error handling in the core
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 16 Jul 2025 19:31:23 +0000 (21:31 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 17 Jul 2025 17:59:49 +0000 (19:59 +0200)
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
drivers/base/power/main.c

index 5d9b3dc9011dc5c644a8ad6811ad4c7e8cd56a9e..8aa06d59a2eef2d5567ff28ffb6af1b6e2ad5610 100644 (file)
@@ -767,7 +767,7 @@ 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);
        }
@@ -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 @@ 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);
        }
@@ -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 @@ 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_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 @@ 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_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);