Skip to content

Commit bca84a7

Browse files
committed
PM: sleep: Use DPM_FLAG_SMART_SUSPEND conditionally
A recent discussion has revealed that using DPM_FLAG_SMART_SUSPEND unconditionally is generally problematic because it may lead to situations in which the device's runtime PM information is internally inconsistent or does not reflect its real state [1]. For this reason, change the handling of DPM_FLAG_SMART_SUSPEND so that it is only taken into account if it is consistently set by the drivers of all devices having any PM callbacks throughout dependency graphs in accordance with the following rules: - The "smart suspend" feature is only enabled for devices whose drivers ask for it (that is, set DPM_FLAG_SMART_SUSPEND) and for devices without PM callbacks unless they have never had runtime PM enabled. - The "smart suspend" feature is not enabled for a device if it has not been enabled for the device's parent unless the parent does not take children into account or it has never had runtime PM enabled. - The "smart suspend" feature is not enabled for a device if it has not been enabled for one of the device's suppliers taking runtime PM into account unless that supplier has never had runtime PM enabled. Namely, introduce a new device PM flag called smart_suspend that is only set if the above conditions are met and update all DPM_FLAG_SMART_SUSPEND users to check power.smart_suspend instead of directly checking the latter. At the same time, drop the power.set_active flage introduced recently in commit 3775fc5 ("PM: sleep: core: Synchronize runtime PM status of parents and children") because it is now sufficient to check power.smart_suspend along with the dev_pm_skip_resume() return value to decide whether or not pm_runtime_set_active() needs to be called for the device. Link: https://lore.kernel.org/linux-pm/CAPDyKFroyU3YDSfw_Y6k3giVfajg3NQGwNWeteJWqpW29BojhQ@mail.gmail.com/ [1] Fixes: 7585946 ("PM: sleep: core: Restrict power.set_active propagation") Signed-off-by: Rafael J. Wysocki <[email protected]> Reviewed-by: Ulf Hansson <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> # drivers/pci Link: https://patch.msgid.link/[email protected]
1 parent 758cc55 commit bca84a7

File tree

6 files changed

+64
-22
lines changed

6 files changed

+64
-22
lines changed

drivers/acpi/device_pm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_complete);
11611161
*/
11621162
int acpi_subsys_suspend(struct device *dev)
11631163
{
1164-
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
1164+
if (!dev_pm_smart_suspend(dev) ||
11651165
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
11661166
pm_runtime_resume(dev);
11671167

@@ -1320,7 +1320,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
13201320
*/
13211321
int acpi_subsys_poweroff(struct device *dev)
13221322
{
1323-
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
1323+
if (!dev_pm_smart_suspend(dev) ||
13241324
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
13251325
pm_runtime_resume(dev);
13261326

drivers/base/power/main.c

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -656,15 +656,13 @@ static void device_resume_noirq(struct device *dev, pm_message_t state, bool asy
656656
* so change its status accordingly.
657657
*
658658
* Otherwise, the device is going to be resumed, so set its PM-runtime
659-
* status to "active" unless its power.set_active flag is clear, in
659+
* status to "active" unless its power.smart_suspend flag is clear, in
660660
* which case it is not necessary to update its PM-runtime status.
661661
*/
662-
if (skip_resume) {
662+
if (skip_resume)
663663
pm_runtime_set_suspended(dev);
664-
} else if (dev->power.set_active) {
664+
else if (dev_pm_smart_suspend(dev))
665665
pm_runtime_set_active(dev);
666-
dev->power.set_active = false;
667-
}
668666

669667
if (dev->pm_domain) {
670668
info = "noirq power domain ";
@@ -1282,14 +1280,8 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy
12821280
dev->power.may_skip_resume))
12831281
dev->power.must_resume = true;
12841282

1285-
if (dev->power.must_resume) {
1286-
if (dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) {
1287-
dev->power.set_active = true;
1288-
if (dev->parent && !dev->parent->power.ignore_children)
1289-
dev->parent->power.set_active = true;
1290-
}
1283+
if (dev->power.must_resume)
12911284
dpm_superior_set_must_resume(dev);
1292-
}
12931285

12941286
Complete:
12951287
complete_all(&dev->power.completion);
@@ -1797,6 +1789,49 @@ int dpm_suspend(pm_message_t state)
17971789
return error;
17981790
}
17991791

1792+
static void device_prepare_smart_suspend(struct device *dev)
1793+
{
1794+
struct device_link *link;
1795+
int idx;
1796+
1797+
/*
1798+
* The "smart suspend" feature is enabled for devices whose drivers ask
1799+
* for it and for devices without PM callbacks unless runtime PM is
1800+
* disabled and enabling it is blocked for them.
1801+
*
1802+
* However, if "smart suspend" is not enabled for the device's parent
1803+
* or any of its suppliers that take runtime PM into account, it cannot
1804+
* be enabled for the device either.
1805+
*/
1806+
dev->power.smart_suspend = (dev->power.no_pm_callbacks ||
1807+
dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) &&
1808+
!pm_runtime_blocked(dev);
1809+
1810+
if (!dev_pm_smart_suspend(dev))
1811+
return;
1812+
1813+
if (dev->parent && !dev_pm_smart_suspend(dev->parent) &&
1814+
!dev->parent->power.ignore_children && !pm_runtime_blocked(dev->parent)) {
1815+
dev->power.smart_suspend = false;
1816+
return;
1817+
}
1818+
1819+
idx = device_links_read_lock();
1820+
1821+
list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) {
1822+
if (!(link->flags | DL_FLAG_PM_RUNTIME))
1823+
continue;
1824+
1825+
if (!dev_pm_smart_suspend(link->supplier) &&
1826+
!pm_runtime_blocked(link->supplier)) {
1827+
dev->power.smart_suspend = false;
1828+
break;
1829+
}
1830+
}
1831+
1832+
device_links_read_unlock(idx);
1833+
}
1834+
18001835
/**
18011836
* device_prepare - Prepare a device for system power transition.
18021837
* @dev: Device to handle.
@@ -1858,6 +1893,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
18581893
pm_runtime_put(dev);
18591894
return ret;
18601895
}
1896+
device_prepare_smart_suspend(dev);
18611897
/*
18621898
* A positive return value from ->prepare() means "this device appears
18631899
* to be runtime-suspended and its state is fine, so if it really is
@@ -2033,6 +2069,5 @@ void device_pm_check_callbacks(struct device *dev)
20332069

20342070
bool dev_pm_skip_suspend(struct device *dev)
20352071
{
2036-
return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
2037-
pm_runtime_status_suspended(dev);
2072+
return dev_pm_smart_suspend(dev) && pm_runtime_status_suspended(dev);
20382073
}

drivers/mfd/intel-lpss.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ EXPORT_SYMBOL_NS_GPL(intel_lpss_remove, "INTEL_LPSS");
480480

481481
static int resume_lpss_device(struct device *dev, void *data)
482482
{
483-
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
483+
if (!dev_pm_smart_suspend(dev))
484484
pm_runtime_resume(dev);
485485

486486
return 0;

drivers/pci/pci-driver.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -812,8 +812,7 @@ static int pci_pm_suspend(struct device *dev)
812812
* suspend callbacks can cope with runtime-suspended devices, it is
813813
* better to resume the device from runtime suspend here.
814814
*/
815-
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
816-
pci_dev_need_resume(pci_dev)) {
815+
if (!dev_pm_smart_suspend(dev) || pci_dev_need_resume(pci_dev)) {
817816
pm_runtime_resume(dev);
818817
pci_dev->state_saved = false;
819818
} else {
@@ -1151,8 +1150,7 @@ static int pci_pm_poweroff(struct device *dev)
11511150
}
11521151

11531152
/* The reason to do that is the same as in pci_pm_suspend(). */
1154-
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
1155-
pci_dev_need_resume(pci_dev)) {
1153+
if (!dev_pm_smart_suspend(dev) || pci_dev_need_resume(pci_dev)) {
11561154
pm_runtime_resume(dev);
11571155
pci_dev->state_saved = false;
11581156
} else {

include/linux/device.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,15 @@ static inline bool dev_pm_test_driver_flags(struct device *dev, u32 flags)
10251025
return !!(dev->power.driver_flags & flags);
10261026
}
10271027

1028+
static inline bool dev_pm_smart_suspend(struct device *dev)
1029+
{
1030+
#ifdef CONFIG_PM_SLEEP
1031+
return dev->power.smart_suspend;
1032+
#else
1033+
return false;
1034+
#endif
1035+
}
1036+
10281037
static inline void device_lock(struct device *dev)
10291038
{
10301039
mutex_lock(&dev->mutex);

include/linux/pm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,8 +680,8 @@ struct dev_pm_info {
680680
bool syscore:1;
681681
bool no_pm_callbacks:1; /* Owned by the PM core */
682682
bool async_in_progress:1; /* Owned by the PM core */
683+
bool smart_suspend:1; /* Owned by the PM core */
683684
bool must_resume:1; /* Owned by the PM core */
684-
bool set_active:1; /* Owned by the PM core */
685685
bool may_skip_resume:1; /* Set by subsystems */
686686
#else
687687
bool should_wakeup:1;

0 commit comments

Comments
 (0)