]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PM: sleep: core: Fix runtime PM enabling in device_resume_early()
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 18 Nov 2025 14:16:04 +0000 (15:16 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 18 Nov 2025 14:47:55 +0000 (15:47 +0100)
Runtime PM should only be enabled in device_resume_early() if it has
been disabled for the given device by device_suspend_late().  Otherwise,
it may cause runtime PM callbacks to run prematurely in some cases
which leads to further functional issues.

Make two changes to address this problem.

First, reorder device_suspend_late() to only disable runtime PM for a
device when it is going to look for the device's callback or if the
device is a "syscore" one.  In all of the other cases, disabling runtime
PM for the device is not in fact necessary.  However, if the device's
callback returns an error and the power.is_late_suspended flag is not
going to be set, enable runtime PM so it only remains disabled when
power.is_late_suspended is set.

Second, make device_resume_early() only enable runtime PM for the
devices with the power.is_late_suspended flag set.

Fixes: 443046d1ad66 ("PM: sleep: Make suspend of devices more asynchronous")
Reported-by: Rose Wu <ya-jou.wu@mediatek.com>
Closes: https://lore.kernel.org/linux-pm/70b25dca6f8c2756d78f076f4a7dee7edaaffc33.camel@mediatek.com/
Cc: 6.16+ <stable@vger.kernel.org> # 6.16+
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Link: https://patch.msgid.link/12784270.O9o76ZdvQC@rafael.j.wysocki
drivers/base/power/main.c

index e83503bdc1fdb83de20192745ca7e416fd556e04..1de1cd72b616da3cc108ec55a5e3606ad3f561c1 100644 (file)
@@ -888,12 +888,15 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
        TRACE_DEVICE(dev);
        TRACE_RESUME(0);
 
-       if (dev->power.syscore || dev->power.direct_complete)
+       if (dev->power.direct_complete)
                goto Out;
 
        if (!dev->power.is_late_suspended)
                goto Out;
 
+       if (dev->power.syscore)
+               goto Skip;
+
        if (!dpm_wait_for_superior(dev, async))
                goto Out;
 
@@ -926,11 +929,11 @@ Run:
 
 Skip:
        dev->power.is_late_suspended = false;
+       pm_runtime_enable(dev);
 
 Out:
        TRACE_RESUME(error);
 
-       pm_runtime_enable(dev);
        complete_all(&dev->power.completion);
 
        if (error) {
@@ -1615,12 +1618,6 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
        TRACE_DEVICE(dev);
        TRACE_SUSPEND(0);
 
-       /*
-        * Disable runtime PM for the device without checking if there is a
-        * pending resume request for it.
-        */
-       __pm_runtime_disable(dev, false);
-
        dpm_wait_for_subordinate(dev, async);
 
        if (READ_ONCE(async_error))
@@ -1631,9 +1628,18 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
                goto Complete;
        }
 
-       if (dev->power.syscore || dev->power.direct_complete)
+       if (dev->power.direct_complete)
                goto Complete;
 
+       /*
+        * Disable runtime PM for the device without checking if there is a
+        * pending resume request for it.
+        */
+       __pm_runtime_disable(dev, false);
+
+       if (dev->power.syscore)
+               goto Skip;
+
        if (dev->pm_domain) {
                info = "late power domain ";
                callback = pm_late_early_op(&dev->pm_domain->ops, state);
@@ -1664,6 +1670,7 @@ Run:
                WRITE_ONCE(async_error, error);
                dpm_save_failed_dev(dev_name(dev));
                pm_dev_err(dev, state, async ? " async late" : " late", error);
+               pm_runtime_enable(dev);
                goto Complete;
        }
        dpm_propagate_wakeup_to_parent(dev);