Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PM: sleep: Update power.completion for all devices on errors

After commit aa7a9275ab81 ("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: aa7a9275ab81 ("PM: sleep: Suspend async parents after suspending children")
Fixes: 443046d1ad66 ("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

+19
+19
drivers/base/power/main.c
··· 1280 1280 dpm_async_with_cleanup(dev->parent, func); 1281 1281 } 1282 1282 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 + 1283 1299 /** 1284 1300 * resume_event - Return a "resume" message for given "suspend" sleep state. 1285 1301 * @sleep_state: PM message representing a sleep state. ··· 1472 1456 mutex_lock(&dpm_list_mtx); 1473 1457 1474 1458 if (error || async_error) { 1459 + dpm_async_suspend_complete_all(&dpm_late_early_list); 1475 1460 /* 1476 1461 * Move all devices to the target list to resume them 1477 1462 * properly. ··· 1675 1658 mutex_lock(&dpm_list_mtx); 1676 1659 1677 1660 if (error || async_error) { 1661 + dpm_async_suspend_complete_all(&dpm_suspended_list); 1678 1662 /* 1679 1663 * Move all devices to the target list to resume them 1680 1664 * properly. ··· 1969 1951 mutex_lock(&dpm_list_mtx); 1970 1952 1971 1953 if (error || async_error) { 1954 + dpm_async_suspend_complete_all(&dpm_prepared_list); 1972 1955 /* 1973 1956 * Move all devices to the target list to resume them 1974 1957 * properly.