]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PM: sleep: core: Synchronize runtime PM status of parents and children
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 28 Jan 2025 19:24:41 +0000 (20:24 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 29 Jan 2025 10:50:33 +0000 (11:50 +0100)
Commit 6e176bf8d461 ("PM: sleep: core: Do not skip callbacks in the
resume phase") overlooked the case in which the parent of a device with
DPM_FLAG_SMART_SUSPEND set did not use that flag and could be runtime-
suspended before a transition into a system-wide sleep state.  In that
case, if the child is resumed during the subsequent transition from
that state into the working state, its runtime PM status will be set to
RPM_ACTIVE, but the runtime PM status of the parent will not be updated
accordingly, even though the parent will be resumed too, because of the
dev_pm_skip_suspend() check in device_resume_noirq().

Address this problem by tracking the need to set the runtime PM status
to RPM_ACTIVE during system-wide resume transitions for devices with
DPM_FLAG_SMART_SUSPEND set and all of the devices depended on by them.

Fixes: 6e176bf8d461 ("PM: sleep: core: Do not skip callbacks in the resume phase")
Closes: https://lore.kernel.org/linux-pm/Z30p2Etwf3F2AUvD@hovoldconsulting.com/
Reported-by: Johan Hovold <johan@kernel.org>
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Johan Hovold <johan+linaro@kernel.org>
Tested-by: Johan Hovold <johan+linaro@kernel.org>
Link: https://patch.msgid.link/12619233.O9o76ZdvQC@rjwysocki.net
drivers/base/power/main.c
include/linux/pm.h

index cbc9a7a75def7625b5f1f6c95664d95b92c30037..d497d448e4b2af0aa1b49960af4e6542e8eaa34e 100644 (file)
@@ -656,13 +656,15 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
         * so change its status accordingly.
         *
         * Otherwise, the device is going to be resumed, so set its PM-runtime
-        * status to "active", but do that only if DPM_FLAG_SMART_SUSPEND is set
-        * to avoid confusing drivers that don't use it.
+        * status to "active" unless its power.set_active flag is clear, in
+        * which case it is not necessary to update its PM-runtime status.
         */
-       if (skip_resume)
+       if (skip_resume) {
                pm_runtime_set_suspended(dev);
-       else if (dev_pm_skip_suspend(dev))
+       } else if (dev->power.set_active) {
                pm_runtime_set_active(dev);
+               dev->power.set_active = false;
+       }
 
        if (dev->pm_domain) {
                info = "noirq power domain ";
@@ -1189,18 +1191,24 @@ static pm_message_t resume_event(pm_message_t sleep_state)
        return PMSG_ON;
 }
 
-static void dpm_superior_set_must_resume(struct device *dev)
+static void dpm_superior_set_must_resume(struct device *dev, bool set_active)
 {
        struct device_link *link;
        int idx;
 
-       if (dev->parent)
+       if (dev->parent) {
                dev->parent->power.must_resume = true;
+               if (set_active)
+                       dev->parent->power.set_active = true;
+       }
 
        idx = device_links_read_lock();
 
-       list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node)
+       list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) {
                link->supplier->power.must_resume = true;
+               if (set_active)
+                       link->supplier->power.set_active = true;
+       }
 
        device_links_read_unlock(idx);
 }
@@ -1278,8 +1286,11 @@ Skip:
              dev->power.may_skip_resume))
                dev->power.must_resume = true;
 
-       if (dev->power.must_resume)
-               dpm_superior_set_must_resume(dev);
+       if (dev->power.must_resume) {
+               dev->power.set_active = dev->power.set_active ||
+                       dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND);
+               dpm_superior_set_must_resume(dev, dev->power.set_active);
+       }
 
 Complete:
        complete_all(&dev->power.completion);
index 0627a795892be056669d33375d6a6cfb972eb091..0d2597a76dfcc922faf47c83b1390fda53e0eed8 100644 (file)
@@ -679,6 +679,7 @@ struct dev_pm_info {
        bool                    no_pm_callbacks:1;      /* Owned by the PM core */
        bool                    async_in_progress:1;    /* Owned by the PM core */
        bool                    must_resume:1;          /* Owned by the PM core */
+       bool                    set_active:1;           /* Owned by the PM core */
        bool                    may_skip_resume:1;      /* Set by subsystems */
 #else
        bool                    should_wakeup:1;