aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-12-09 19:00:45 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2018-01-02 18:29:55 -0500
commit75e94645fc3b1007eacb4c7863059f8e8d098cda (patch)
treedb110b2815282374f08bf5d74509c73b9158cb68
parent4fa3061a6856cc72f3f984702145bb30f16ee40e (diff)
PM / core: Direct DPM_FLAG_SMART_SUSPEND optimization
Make the PM core avoid invoking the "late" and "noirq" system-wide suspend (or analogous) callbacks provided by device drivers directly for devices with DPM_FLAG_SMART_SUSPEND set that are in runtime suspend during the "late" and "noirq" phases of system-wide suspend (or analogous) transitions. That is only done for devices without any middle-layer "late" and "noirq" suspend callbacks (to avoid confusing the middle layer if there is one). The underlying observation is that runtime PM is disabled for devices during the "late" and "noirq" system-wide suspend phases, so if they remain in runtime suspend from the "late" phase forward, it doesn't make sense to invoke the "late" and "noirq" callbacks provided by the drivers for them (arguably, the device is already suspended and in the right state). Thus, if the remaining driver suspend callbacks are to be invoked directly by the core, they can be skipped. This change really makes it possible for, say, platform device drivers to re-use runtime PM suspend and resume callbacks by pointing ->suspend_late and ->resume_early, respectively (and possibly the analogous hibernation-related callback pointers too), to them without adding any extra "is the device already suspended?" type of checks to the callback routines, as long as they will be invoked directly by the core. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--Documentation/driver-api/pm/devices.rst18
-rw-r--r--drivers/base/power/main.c85
2 files changed, 88 insertions, 15 deletions
diff --git a/Documentation/driver-api/pm/devices.rst b/Documentation/driver-api/pm/devices.rst
index b0fe63c91f8d..07026811dcae 100644
--- a/Documentation/driver-api/pm/devices.rst
+++ b/Documentation/driver-api/pm/devices.rst
@@ -777,14 +777,16 @@ The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in
777runtime suspend at the beginning of the ``suspend_late`` phase of system-wide 777runtime suspend at the beginning of the ``suspend_late`` phase of system-wide
778suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM 778suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM
779has been disabled for it, under the assumption that its state should not change 779has been disabled for it, under the assumption that its state should not change
780after that point until the system-wide transition is over. If that happens, the 780after that point until the system-wide transition is over (the PM core itself
781driver's system-wide resume callbacks, if present, may still be invoked during 781does that for devices whose "noirq", "late" and "early" system-wide PM callbacks
782the subsequent system-wide resume transition and the device's runtime power 782are executed directly by it). If that happens, the driver's system-wide resume
783management status may be set to "active" before enabling runtime PM for it, 783callbacks, if present, may still be invoked during the subsequent system-wide
784so the driver must be prepared to cope with the invocation of its system-wide 784resume transition and the device's runtime power management status may be set
785resume callbacks back-to-back with its ``->runtime_suspend`` one (without the 785to "active" before enabling runtime PM for it, so the driver must be prepared to
786intervening ``->runtime_resume`` and so on) and the final state of the device 786cope with the invocation of its system-wide resume callbacks back-to-back with
787must reflect the "active" status for runtime PM in that case. 787its ``->runtime_suspend`` one (without the intervening ``->runtime_resume`` and
788so on) and the final state of the device must reflect the "active" runtime PM
789status in that case.
788 790
789During system-wide resume from a sleep state it's easiest to put devices into 791During system-wide resume from a sleep state it's easiest to put devices into
790the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`. 792the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 3c5fdf155c91..154f7b4db8d0 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -540,6 +540,24 @@ void dev_pm_skip_next_resume_phases(struct device *dev)
540} 540}
541 541
542/** 542/**
543 * suspend_event - Return a "suspend" message for given "resume" one.
544 * @resume_msg: PM message representing a system-wide resume transition.
545 */
546static pm_message_t suspend_event(pm_message_t resume_msg)
547{
548 switch (resume_msg.event) {
549 case PM_EVENT_RESUME:
550 return PMSG_SUSPEND;
551 case PM_EVENT_THAW:
552 case PM_EVENT_RESTORE:
553 return PMSG_FREEZE;
554 case PM_EVENT_RECOVER:
555 return PMSG_HIBERNATE;
556 }
557 return PMSG_ON;
558}
559
560/**
543 * dev_pm_may_skip_resume - System-wide device resume optimization check. 561 * dev_pm_may_skip_resume - System-wide device resume optimization check.
544 * @dev: Target device. 562 * @dev: Target device.
545 * 563 *
@@ -580,6 +598,14 @@ static pm_callback_t dpm_subsys_resume_noirq_cb(struct device *dev,
580 return callback; 598 return callback;
581} 599}
582 600
601static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
602 pm_message_t state,
603 const char **info_p);
604
605static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
606 pm_message_t state,
607 const char **info_p);
608
583/** 609/**
584 * device_resume_noirq - Execute a "noirq resume" callback for given device. 610 * device_resume_noirq - Execute a "noirq resume" callback for given device.
585 * @dev: Device to handle. 611 * @dev: Device to handle.
@@ -607,13 +633,40 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
607 dpm_wait_for_superior(dev, async); 633 dpm_wait_for_superior(dev, async);
608 634
609 callback = dpm_subsys_resume_noirq_cb(dev, state, &info); 635 callback = dpm_subsys_resume_noirq_cb(dev, state, &info);
636 if (callback)
637 goto Run;
610 638
611 if (!callback && dev->driver && dev->driver->pm) { 639 if (dev_pm_smart_suspend_and_suspended(dev)) {
640 pm_message_t suspend_msg = suspend_event(state);
641
642 /*
643 * If "freeze" callbacks have been skipped during a transition
644 * related to hibernation, the subsequent "thaw" callbacks must
645 * be skipped too or bad things may happen. Otherwise, resume
646 * callbacks are going to be run for the device, so its runtime
647 * PM status must be changed to reflect the new state after the
648 * transition under way.
649 */
650 if (!dpm_subsys_suspend_late_cb(dev, suspend_msg, NULL) &&
651 !dpm_subsys_suspend_noirq_cb(dev, suspend_msg, NULL)) {
652 if (state.event == PM_EVENT_THAW) {
653 dev_pm_skip_next_resume_phases(dev);
654 goto Skip;
655 } else {
656 pm_runtime_set_active(dev);
657 }
658 }
659 }
660
661 if (dev->driver && dev->driver->pm) {
612 info = "noirq driver "; 662 info = "noirq driver ";
613 callback = pm_noirq_op(dev->driver->pm, state); 663 callback = pm_noirq_op(dev->driver->pm, state);
614 } 664 }
615 665
666Run:
616 error = dpm_run_callback(callback, dev, state, info); 667 error = dpm_run_callback(callback, dev, state, info);
668
669Skip:
617 dev->power.is_noirq_suspended = false; 670 dev->power.is_noirq_suspended = false;
618 671
619 if (dev_pm_may_skip_resume(dev)) { 672 if (dev_pm_may_skip_resume(dev)) {
@@ -628,7 +681,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
628 dev_pm_skip_next_resume_phases(dev); 681 dev_pm_skip_next_resume_phases(dev);
629 } 682 }
630 683
631 Out: 684Out:
632 complete_all(&dev->power.completion); 685 complete_all(&dev->power.completion);
633 TRACE_RESUME(error); 686 TRACE_RESUME(error);
634 return error; 687 return error;
@@ -1223,18 +1276,26 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
1223 goto Complete; 1276 goto Complete;
1224 1277
1225 callback = dpm_subsys_suspend_noirq_cb(dev, state, &info); 1278 callback = dpm_subsys_suspend_noirq_cb(dev, state, &info);
1279 if (callback)
1280 goto Run;
1226 1281
1227 if (!callback && dev->driver && dev->driver->pm) { 1282 if (dev_pm_smart_suspend_and_suspended(dev) &&
1283 !dpm_subsys_suspend_late_cb(dev, state, NULL))
1284 goto Skip;
1285
1286 if (dev->driver && dev->driver->pm) {
1228 info = "noirq driver "; 1287 info = "noirq driver ";
1229 callback = pm_noirq_op(dev->driver->pm, state); 1288 callback = pm_noirq_op(dev->driver->pm, state);
1230 } 1289 }
1231 1290
1291Run:
1232 error = dpm_run_callback(callback, dev, state, info); 1292 error = dpm_run_callback(callback, dev, state, info);
1233 if (error) { 1293 if (error) {
1234 async_error = error; 1294 async_error = error;
1235 goto Complete; 1295 goto Complete;
1236 } 1296 }
1237 1297
1298Skip:
1238 dev->power.is_noirq_suspended = true; 1299 dev->power.is_noirq_suspended = true;
1239 1300
1240 if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) { 1301 if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) {
@@ -1419,17 +1480,27 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
1419 goto Complete; 1480 goto Complete;
1420 1481
1421 callback = dpm_subsys_suspend_late_cb(dev, state, &info); 1482 callback = dpm_subsys_suspend_late_cb(dev, state, &info);
1483 if (callback)
1484 goto Run;
1422 1485
1423 if (!callback && dev->driver && dev->driver->pm) { 1486 if (dev_pm_smart_suspend_and_suspended(dev) &&
1487 !dpm_subsys_suspend_noirq_cb(dev, state, NULL))
1488 goto Skip;
1489
1490 if (dev->driver && dev->driver->pm) {
1424 info = "late driver "; 1491 info = "late driver ";
1425 callback = pm_late_early_op(dev->driver->pm, state); 1492 callback = pm_late_early_op(dev->driver->pm, state);
1426 } 1493 }
1427 1494
1495Run:
1428 error = dpm_run_callback(callback, dev, state, info); 1496 error = dpm_run_callback(callback, dev, state, info);
1429 if (!error) 1497 if (error) {
1430 dev->power.is_late_suspended = true;
1431 else
1432 async_error = error; 1498 async_error = error;
1499 goto Complete;
1500 }
1501
1502Skip:
1503 dev->power.is_late_suspended = true;
1433 1504
1434Complete: 1505Complete:
1435 TRACE_SUSPEND(error); 1506 TRACE_SUSPEND(error);