@@ -1283,6 +1283,7 @@ static void async_suspend_noirq(void *data, async_cookie_t cookie)
static int dpm_noirq_suspend_devices(pm_message_t state)
{
+ struct device *dev;
ktime_t starttime = ktime_get();
int error = 0;
@@ -1293,26 +1294,33 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
mutex_lock(&dpm_list_mtx);
+ /*
+ * Trigger the suspend of "async" devices upfront so they don't have to
+ * wait for the "non-async" ones that don't depend on them.
+ */
+
+ list_for_each_entry_reverse(dev, &dpm_late_early_list, power.entry)
+ dpm_async_fn(dev, async_suspend_noirq);
+
while (!list_empty(&dpm_late_early_list)) {
- struct device *dev = to_device(dpm_late_early_list.prev);
+ dev = to_device(dpm_late_early_list.prev);
list_move(&dev->power.entry, &dpm_noirq_list);
- if (dpm_async_fn(dev, async_suspend_noirq))
- continue;
-
- get_device(dev);
+ if (!dev->power.async_in_progress) {
+ get_device(dev);
- mutex_unlock(&dpm_list_mtx);
+ mutex_unlock(&dpm_list_mtx);
- error = device_suspend_noirq(dev, state, false);
+ error = device_suspend_noirq(dev, state, false);
- put_device(dev);
+ put_device(dev);
- mutex_lock(&dpm_list_mtx);
+ mutex_lock(&dpm_list_mtx);
- if (error || async_error)
- break;
+ if (error || async_error)
+ break;
+ }
}
mutex_unlock(&dpm_list_mtx);
@@ -1454,6 +1462,7 @@ static void async_suspend_late(void *data, async_cookie_t cookie)
*/
int dpm_suspend_late(pm_message_t state)
{
+ struct device *dev;
ktime_t starttime = ktime_get();
int error = 0;
@@ -1466,26 +1475,33 @@ int dpm_suspend_late(pm_message_t state)
mutex_lock(&dpm_list_mtx);
+ /*
+ * Trigger the suspend of "async" devices upfront so they don't have to
+ * wait for the "non-async" ones that don't depend on them.
+ */
+
+ list_for_each_entry_reverse(dev, &dpm_suspended_list, power.entry)
+ dpm_async_fn(dev, async_suspend_late);
+
while (!list_empty(&dpm_suspended_list)) {
- struct device *dev = to_device(dpm_suspended_list.prev);
+ dev = to_device(dpm_suspended_list.prev);
list_move(&dev->power.entry, &dpm_late_early_list);
- if (dpm_async_fn(dev, async_suspend_late))
- continue;
-
- get_device(dev);
+ if (!dev->power.async_in_progress) {
+ get_device(dev);
- mutex_unlock(&dpm_list_mtx);
+ mutex_unlock(&dpm_list_mtx);
- error = device_suspend_late(dev, state, false);
+ error = device_suspend_late(dev, state, false);
- put_device(dev);
+ put_device(dev);
- mutex_lock(&dpm_list_mtx);
+ mutex_lock(&dpm_list_mtx);
- if (error || async_error)
- break;
+ if (error || async_error)
+ break;
+ }
}
mutex_unlock(&dpm_list_mtx);
@@ -1719,6 +1735,7 @@ static void async_suspend(void *data, async_cookie_t cookie)
*/
int dpm_suspend(pm_message_t state)
{
+ struct device *dev;
ktime_t starttime = ktime_get();
int error = 0;
@@ -1733,26 +1750,33 @@ int dpm_suspend(pm_message_t state)
mutex_lock(&dpm_list_mtx);
+ /*
+ * Trigger the suspend of "async" devices upfront so they don't have to
+ * wait for the "non-async" ones that don't depend on them.
+ */
+
+ list_for_each_entry_reverse(dev, &dpm_prepared_list, power.entry)
+ dpm_async_fn(dev, async_suspend);
+
while (!list_empty(&dpm_prepared_list)) {
- struct device *dev = to_device(dpm_prepared_list.prev);
+ dev = to_device(dpm_prepared_list.prev);
list_move(&dev->power.entry, &dpm_suspended_list);
- if (dpm_async_fn(dev, async_suspend))
- continue;
-
- get_device(dev);
+ if (!dev->power.async_in_progress) {
+ get_device(dev);
- mutex_unlock(&dpm_list_mtx);
+ mutex_unlock(&dpm_list_mtx);
- error = device_suspend(dev, state, false);
+ error = device_suspend(dev, state, false);
- put_device(dev);
+ put_device(dev);
- mutex_lock(&dpm_list_mtx);
+ mutex_lock(&dpm_list_mtx);
- if (error || async_error)
- break;
+ if (error || async_error)
+ break;
+ }
}
mutex_unlock(&dpm_list_mtx);
Currently, when performing a suspend operation, all devices on the dpm_list must wait for the "synchronous" devices that are listed after them to complete before the main suspend thread can start their suspend routines, even if they are "asynchronous". If the suspend routine of a synchronous device must enter a waiting state for some reason, it will cause the main suspend thread to go to sleep, thereby delaying the processing of all subsequent devices, including asynchronous ones, ultimately extending the overall suspend time. By starting the asynchronous suspend threads upfront, we can allow the system to handle the suspend routines of these asynchronous devices in parallel, without waiting for the suspend routines of the synchronous devices listed after them to complete, and without breaking their order with respect to their parents and children. This way, even if the main suspend thread is blocked, these asynchronous suspend threads can continue to run without being affected. Signed-off-by: Kaiyen Chang <kaiyen.chang@intel.com> --- Change from v1: Fix some unclear parts in the commit messages. --- drivers/base/power/main.c | 90 +++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 33 deletions(-)