@@ -1317,6 +1317,8 @@
device_links_read_unlock(idx);
}
+static void async_suspend_noirq(void *data, async_cookie_t cookie);
+
/**
* device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle.
@@ -1396,7 +1398,13 @@
Complete:
complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
- return error;
+
+ if (error || async_error)
+ return error;
+
+ dpm_async_suspend_superior(dev, async_suspend_noirq);
+
+ return 0;
}
static void async_suspend_noirq(void *data, async_cookie_t cookie)
@@ -1410,6 +1418,7 @@
static int dpm_noirq_suspend_devices(pm_message_t state)
{
ktime_t starttime = ktime_get();
+ struct device *dev;
int error = 0;
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
@@ -1419,15 +1428,28 @@
mutex_lock(&dpm_list_mtx);
+ /*
+ * Start processing "async" leaf devices upfront because they don't need
+ * to wait.
+ */
+ list_for_each_entry_reverse(dev, &dpm_late_early_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_leaf_device(dev))
+ 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);
- dpm_clear_async_state(dev);
- if (dpm_async_fn(dev, async_suspend_noirq))
+ dpm_async_unless_in_progress(dev, async_suspend_noirq);
+
+ if (dev->power.work_in_progress)
continue;
+ dev->power.work_in_progress = true;
+
get_device(dev);
mutex_unlock(&dpm_list_mtx);
@@ -1492,6 +1514,8 @@
spin_unlock_irq(&parent->power.lock);
}
+static void async_suspend_late(void *data, async_cookie_t cookie);
+
/**
* device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle.
@@ -1568,7 +1592,13 @@
Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion);
- return error;
+
+ if (error || async_error)
+ return error;
+
+ dpm_async_suspend_superior(dev, async_suspend_late);
+
+ return 0;
}
static void async_suspend_late(void *data, async_cookie_t cookie)
@@ -1586,6 +1616,7 @@
int dpm_suspend_late(pm_message_t state)
{
ktime_t starttime = ktime_get();
+ struct device *dev;
int error = 0;
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
@@ -1597,15 +1628,28 @@
mutex_lock(&dpm_list_mtx);
+ /*
+ * Start processing "async" leaf devices upfront because they don't need
+ * to wait.
+ */
+ list_for_each_entry_reverse(dev, &dpm_suspended_list, power.entry) {
+ dpm_clear_async_state(dev);
+ if (dpm_leaf_device(dev))
+ 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);
- dpm_clear_async_state(dev);
- if (dpm_async_fn(dev, async_suspend_late))
+ dpm_async_unless_in_progress(dev, async_suspend_late);
+
+ if (dev->power.work_in_progress)
continue;
+ dev->power.work_in_progress = true;
+
get_device(dev);
mutex_unlock(&dpm_list_mtx);