Skip to content

Commit 443046d

Browse files
committed
PM: sleep: Make suspend of devices more asynchronous
In analogy with previous changes, make device_suspend_late() and device_suspend_noirq() start the async suspend of the device's parent after the device itself has been processed and make dpm_suspend_late() and dpm_noirq_suspend_devices() start processing "async" leaf devices (that is, devices without children) upfront so they don't need to wait for the other devices they don't depend on. This change reduces the total duration of device suspend on some systems measurably, but not significantly. Suggested-by: Saravana Kannan <saravanak@google.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Link: https://patch.msgid.link/1924195.CQOukoFCf9@rjwysocki.net
1 parent aa7a927 commit 443046d

1 file changed

Lines changed: 56 additions & 8 deletions

File tree

drivers/base/power/main.c

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,8 @@ static void dpm_superior_set_must_resume(struct device *dev)
13081308
device_links_read_unlock(idx);
13091309
}
13101310

1311+
static void async_suspend_noirq(void *data, async_cookie_t cookie);
1312+
13111313
/**
13121314
* device_suspend_noirq - Execute a "noirq suspend" callback for given device.
13131315
* @dev: Device to handle.
@@ -1386,7 +1388,13 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state, bool asy
13861388
Complete:
13871389
complete_all(&dev->power.completion);
13881390
TRACE_SUSPEND(error);
1389-
return error;
1391+
1392+
if (error || async_error)
1393+
return error;
1394+
1395+
dpm_async_suspend_parent(dev, async_suspend_noirq);
1396+
1397+
return 0;
13901398
}
13911399

13921400
static void async_suspend_noirq(void *data, async_cookie_t cookie)
@@ -1400,6 +1408,7 @@ static void async_suspend_noirq(void *data, async_cookie_t cookie)
14001408
static int dpm_noirq_suspend_devices(pm_message_t state)
14011409
{
14021410
ktime_t starttime = ktime_get();
1411+
struct device *dev;
14031412
int error = 0;
14041413

14051414
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
@@ -1409,12 +1418,21 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
14091418

14101419
mutex_lock(&dpm_list_mtx);
14111420

1421+
/*
1422+
* Start processing "async" leaf devices upfront so they don't need to
1423+
* wait for the "sync" devices they don't depend on.
1424+
*/
1425+
list_for_each_entry_reverse(dev, &dpm_late_early_list, power.entry) {
1426+
dpm_clear_async_state(dev);
1427+
if (dpm_leaf_device(dev))
1428+
dpm_async_with_cleanup(dev, async_suspend_noirq);
1429+
}
1430+
14121431
while (!list_empty(&dpm_late_early_list)) {
1413-
struct device *dev = to_device(dpm_late_early_list.prev);
1432+
dev = to_device(dpm_late_early_list.prev);
14141433

14151434
list_move(&dev->power.entry, &dpm_noirq_list);
14161435

1417-
dpm_clear_async_state(dev);
14181436
if (dpm_async_fn(dev, async_suspend_noirq))
14191437
continue;
14201438

@@ -1428,8 +1446,14 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
14281446

14291447
mutex_lock(&dpm_list_mtx);
14301448

1431-
if (error || async_error)
1449+
if (error || async_error) {
1450+
/*
1451+
* Move all devices to the target list to resume them
1452+
* properly.
1453+
*/
1454+
list_splice(&dpm_late_early_list, &dpm_noirq_list);
14321455
break;
1456+
}
14331457
}
14341458

14351459
mutex_unlock(&dpm_list_mtx);
@@ -1482,6 +1506,8 @@ static void dpm_propagate_wakeup_to_parent(struct device *dev)
14821506
spin_unlock_irq(&parent->power.lock);
14831507
}
14841508

1509+
static void async_suspend_late(void *data, async_cookie_t cookie);
1510+
14851511
/**
14861512
* device_suspend_late - Execute a "late suspend" callback for given device.
14871513
* @dev: Device to handle.
@@ -1558,7 +1584,13 @@ static int device_suspend_late(struct device *dev, pm_message_t state, bool asyn
15581584
Complete:
15591585
TRACE_SUSPEND(error);
15601586
complete_all(&dev->power.completion);
1561-
return error;
1587+
1588+
if (error || async_error)
1589+
return error;
1590+
1591+
dpm_async_suspend_parent(dev, async_suspend_late);
1592+
1593+
return 0;
15621594
}
15631595

15641596
static void async_suspend_late(void *data, async_cookie_t cookie)
@@ -1576,6 +1608,7 @@ static void async_suspend_late(void *data, async_cookie_t cookie)
15761608
int dpm_suspend_late(pm_message_t state)
15771609
{
15781610
ktime_t starttime = ktime_get();
1611+
struct device *dev;
15791612
int error = 0;
15801613

15811614
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
@@ -1587,12 +1620,21 @@ int dpm_suspend_late(pm_message_t state)
15871620

15881621
mutex_lock(&dpm_list_mtx);
15891622

1623+
/*
1624+
* Start processing "async" leaf devices upfront so they don't need to
1625+
* wait for the "sync" devices they don't depend on.
1626+
*/
1627+
list_for_each_entry_reverse(dev, &dpm_suspended_list, power.entry) {
1628+
dpm_clear_async_state(dev);
1629+
if (dpm_leaf_device(dev))
1630+
dpm_async_with_cleanup(dev, async_suspend_late);
1631+
}
1632+
15901633
while (!list_empty(&dpm_suspended_list)) {
1591-
struct device *dev = to_device(dpm_suspended_list.prev);
1634+
dev = to_device(dpm_suspended_list.prev);
15921635

15931636
list_move(&dev->power.entry, &dpm_late_early_list);
15941637

1595-
dpm_clear_async_state(dev);
15961638
if (dpm_async_fn(dev, async_suspend_late))
15971639
continue;
15981640

@@ -1606,8 +1648,14 @@ int dpm_suspend_late(pm_message_t state)
16061648

16071649
mutex_lock(&dpm_list_mtx);
16081650

1609-
if (error || async_error)
1651+
if (error || async_error) {
1652+
/*
1653+
* Move all devices to the target list to resume them
1654+
* properly.
1655+
*/
1656+
list_splice(&dpm_suspended_list, &dpm_late_early_list);
16101657
break;
1658+
}
16111659
}
16121660

16131661
mutex_unlock(&dpm_list_mtx);

0 commit comments

Comments
 (0)