Skip to content

Commit ebd6884

Browse files
committed
PM: sleep: Update power.completion for all devices on errors
After commit aa7a927 ("PM: sleep: Suspend async parents after suspending children"), the following scenario is possible: 1. Device A is async and it depends on device B that is sync. 2. Async suspend is scheduled for A before the processing of B is started. 3. A is waiting for B. 4. In the meantime, an unrelated device fails to suspend and returns an error. 5. The processing of B doesn't start at all and its power.completion is not updated. 6. A is still waiting for B when async_synchronize_full() is called. 7. Deadlock ensues. To prevent this from happening, update power.completion for all devices on errors in all suspend phases, but do not do it directly for devices that are already being processed or are waiting for the processing to start because in those cases it may be necessary to wait for the processing to actually complete before updating power.completion for the device. Fixes: aa7a927 ("PM: sleep: Suspend async parents after suspending children") Fixes: 443046d ("PM: sleep: Make suspend of devices more asynchronous") Closes: https://lore.kernel.org/linux-pm/[email protected]/ Reported-and-tested-by: Tudor Ambarus <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]> Reviewed-by: Ulf Hansson <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 228b9de commit ebd6884

File tree

1 file changed

+19
-0
lines changed

1 file changed

+19
-0
lines changed

drivers/base/power/main.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,22 @@ static void dpm_async_suspend_parent(struct device *dev, async_func_t func)
12801280
dpm_async_with_cleanup(dev->parent, func);
12811281
}
12821282

1283+
static void dpm_async_suspend_complete_all(struct list_head *device_list)
1284+
{
1285+
struct device *dev;
1286+
1287+
guard(mutex)(&async_wip_mtx);
1288+
1289+
list_for_each_entry_reverse(dev, device_list, power.entry) {
1290+
/*
1291+
* In case the device is being waited for and async processing
1292+
* has not started for it yet, let the waiters make progress.
1293+
*/
1294+
if (!dev->power.work_in_progress)
1295+
complete_all(&dev->power.completion);
1296+
}
1297+
}
1298+
12831299
/**
12841300
* resume_event - Return a "resume" message for given "suspend" sleep state.
12851301
* @sleep_state: PM message representing a sleep state.
@@ -1456,6 +1472,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
14561472
mutex_lock(&dpm_list_mtx);
14571473

14581474
if (error || async_error) {
1475+
dpm_async_suspend_complete_all(&dpm_late_early_list);
14591476
/*
14601477
* Move all devices to the target list to resume them
14611478
* properly.
@@ -1658,6 +1675,7 @@ int dpm_suspend_late(pm_message_t state)
16581675
mutex_lock(&dpm_list_mtx);
16591676

16601677
if (error || async_error) {
1678+
dpm_async_suspend_complete_all(&dpm_suspended_list);
16611679
/*
16621680
* Move all devices to the target list to resume them
16631681
* properly.
@@ -1951,6 +1969,7 @@ int dpm_suspend(pm_message_t state)
19511969
mutex_lock(&dpm_list_mtx);
19521970

19531971
if (error || async_error) {
1972+
dpm_async_suspend_complete_all(&dpm_prepared_list);
19541973
/*
19551974
* Move all devices to the target list to resume them
19561975
* properly.

0 commit comments

Comments
 (0)