Skip to content

Commit dd5176f

Browse files
committed
PM: sleep: Update power.completion for all devices on errors
JIRA: https://issues.redhat.com/browse/RHEL-109251 commit ebd6884 Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Date: Tue, 15 Jul 2025 14:55:11 +0000 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/e13740a0-88f3-4a6f-920f-15805071a7d6@linaro.org/ Reported-and-tested-by: Tudor Ambarus <tudor.ambarus@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Link: https://patch.msgid.link/6191258.lOV4Wx5bFT@rjwysocki.net Signed-off-by: Mark Langsdorf <mlangsdo@redhat.com>
1 parent 0ea79cd commit dd5176f

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
@@ -1336,6 +1336,22 @@ static void dpm_async_suspend_superior(struct device *dev, async_func_t func)
13361336
device_links_read_unlock(idx);
13371337
}
13381338

1339+
static void dpm_async_suspend_complete_all(struct list_head *device_list)
1340+
{
1341+
struct device *dev;
1342+
1343+
guard(mutex)(&async_wip_mtx);
1344+
1345+
list_for_each_entry_reverse(dev, device_list, power.entry) {
1346+
/*
1347+
* In case the device is being waited for and async processing
1348+
* has not started for it yet, let the waiters make progress.
1349+
*/
1350+
if (!dev->power.work_in_progress)
1351+
complete_all(&dev->power.completion);
1352+
}
1353+
}
1354+
13391355
/**
13401356
* resume_event - Return a "resume" message for given "suspend" sleep state.
13411357
* @sleep_state: PM message representing a sleep state.
@@ -1512,6 +1528,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
15121528
mutex_lock(&dpm_list_mtx);
15131529

15141530
if (error || async_error) {
1531+
dpm_async_suspend_complete_all(&dpm_late_early_list);
15151532
/*
15161533
* Move all devices to the target list to resume them
15171534
* properly.
@@ -1714,6 +1731,7 @@ int dpm_suspend_late(pm_message_t state)
17141731
mutex_lock(&dpm_list_mtx);
17151732

17161733
if (error || async_error) {
1734+
dpm_async_suspend_complete_all(&dpm_suspended_list);
17171735
/*
17181736
* Move all devices to the target list to resume them
17191737
* properly.
@@ -2007,6 +2025,7 @@ int dpm_suspend(pm_message_t state)
20072025
mutex_lock(&dpm_list_mtx);
20082026

20092027
if (error || async_error) {
2028+
dpm_async_suspend_complete_all(&dpm_prepared_list);
20102029
/*
20112030
* Move all devices to the target list to resume them
20122031
* properly.

0 commit comments

Comments
 (0)