Commit 107d47b2 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

PM: sleep: core: Simplify the SMART_SUSPEND flag handling

The code to handle the SMART_SUSPEND driver PM flag is hard to follow
and somewhat inconsistent with respect to devices without middle-layer
(subsystem) callbacks.

Namely, for those devices the core takes the role of a middle layer
in providing the expected ordering of execution of callbacks (under
the assumption that the drivers setting SMART_SUSPEND can reuse their
PM-runtime callbacks directly for system-wide suspend).  To that end,
it prevents driver ->suspend_late and ->suspend_noirq callbacks from
being executed for devices that are still runtime-suspended in
__device_suspend_late(), because running the same callback funtion
that was previously run by PM-runtime for them may be invalid.

However, it does that only for devices without any middle-layer
callbacks for the late/noirq/early suspend/resume phases even
though it would be simpler and more consistent to skip the
driver-lavel callbacks for all devices with SMART_SUSPEND set
that are runtime-suspended in __device_suspend_late().

Simplify the code in accordance with the above observation.
Suggested-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
parent ae83d0b4
...@@ -561,24 +561,6 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) ...@@ -561,24 +561,6 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
/*------------------------- Resume routines -------------------------*/ /*------------------------- Resume routines -------------------------*/
/**
* suspend_event - Return a "suspend" message for given "resume" one.
* @resume_msg: PM message representing a system-wide resume transition.
*/
static pm_message_t suspend_event(pm_message_t resume_msg)
{
switch (resume_msg.event) {
case PM_EVENT_RESUME:
return PMSG_SUSPEND;
case PM_EVENT_THAW:
case PM_EVENT_RESTORE:
return PMSG_FREEZE;
case PM_EVENT_RECOVER:
return PMSG_HIBERNATE;
}
return PMSG_ON;
}
/** /**
* dev_pm_may_skip_resume - System-wide device resume optimization check. * dev_pm_may_skip_resume - System-wide device resume optimization check.
* @dev: Target device. * @dev: Target device.
...@@ -656,37 +638,36 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn ...@@ -656,37 +638,36 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
if (!dpm_wait_for_superior(dev, async)) if (!dpm_wait_for_superior(dev, async))
goto Out; goto Out;
skip_resume = dev_pm_may_skip_resume(dev);
callback = dpm_subsys_resume_noirq_cb(dev, state, &info); callback = dpm_subsys_resume_noirq_cb(dev, state, &info);
if (callback) if (callback) {
skip_resume = false;
goto Run; goto Run;
}
skip_resume = dev_pm_may_skip_resume(dev);
if (skip_resume) if (skip_resume)
goto Skip; goto Skip;
if (dev_pm_smart_suspend_and_suspended(dev)) { /*
pm_message_t suspend_msg = suspend_event(state); * If "freeze" driver callbacks have been skipped during hibernation,
* because the device was runtime-suspended in __device_suspend_late(),
/* * the corresponding "thaw" callbacks must be skipped too, because
* If "freeze" callbacks have been skipped during a transition * running them for a runtime-suspended device may not be valid.
* related to hibernation, the subsequent "thaw" callbacks must */
* be skipped too or bad things may happen. Otherwise, resume if (dev_pm_smart_suspend_and_suspended(dev) &&
* callbacks are going to be run for the device, so its runtime state.event == PM_EVENT_THAW) {
* PM status must be changed to reflect the new state after the skip_resume = true;
* transition under way. goto Skip;
*/
if (!dpm_subsys_suspend_late_cb(dev, suspend_msg, NULL) &&
!dpm_subsys_suspend_noirq_cb(dev, suspend_msg, NULL)) {
if (state.event == PM_EVENT_THAW) {
skip_resume = true;
goto Skip;
} else {
pm_runtime_set_active(dev);
}
}
} }
/*
* The device is going to be resumed, so set its PM-runtime status to
* "active", but do that only if DPM_FLAG_SMART_SUSPEND is set to avoid
* confusing drivers that don't use it.
*/
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);
if (dev->driver && dev->driver->pm) { if (dev->driver && dev->driver->pm) {
info = "noirq driver "; info = "noirq driver ";
callback = pm_noirq_op(dev->driver->pm, state); callback = pm_noirq_op(dev->driver->pm, state);
...@@ -1274,32 +1255,6 @@ static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev, ...@@ -1274,32 +1255,6 @@ static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
return callback; return callback;
} }
static bool device_must_resume(struct device *dev, pm_message_t state,
bool no_subsys_suspend_noirq)
{
pm_message_t resume_msg = resume_event(state);
/*
* If all of the device driver's "noirq", "late" and "early" callbacks
* are invoked directly by the core, the decision to allow the device to
* stay in suspend can be based on its current runtime PM status and its
* wakeup settings.
*/
if (no_subsys_suspend_noirq &&
!dpm_subsys_suspend_late_cb(dev, state, NULL) &&
!dpm_subsys_resume_early_cb(dev, resume_msg, NULL) &&
!dpm_subsys_resume_noirq_cb(dev, resume_msg, NULL))
return !pm_runtime_status_suspended(dev) &&
(resume_msg.event != PM_EVENT_RESUME ||
(device_can_wakeup(dev) && !device_may_wakeup(dev)));
/*
* The only safe strategy here is to require that if the device may not
* be left in suspend, resume callbacks must be invoked for it.
*/
return !dev->power.may_skip_resume;
}
/** /**
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device. * __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle. * @dev: Device to handle.
...@@ -1313,7 +1268,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a ...@@ -1313,7 +1268,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
{ {
pm_callback_t callback; pm_callback_t callback;
const char *info; const char *info;
bool no_subsys_cb = false;
int error = 0; int error = 0;
TRACE_DEVICE(dev); TRACE_DEVICE(dev);
...@@ -1331,9 +1285,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a ...@@ -1331,9 +1285,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (callback) if (callback)
goto Run; goto Run;
no_subsys_cb = !dpm_subsys_suspend_late_cb(dev, state, NULL); if (dev_pm_smart_suspend_and_suspended(dev))
if (dev_pm_smart_suspend_and_suspended(dev) && no_subsys_cb)
goto Skip; goto Skip;
if (dev->driver && dev->driver->pm) { if (dev->driver && dev->driver->pm) {
...@@ -1351,13 +1303,16 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a ...@@ -1351,13 +1303,16 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
Skip: Skip:
dev->power.is_noirq_suspended = true; dev->power.is_noirq_suspended = true;
if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) { /*
dev->power.must_resume = dev->power.must_resume || * Skipping the resume of devices that were in use right before the
atomic_read(&dev->power.usage_count) > 1 || * system suspend (as indicated by their PM-runtime usage counters)
device_must_resume(dev, state, no_subsys_cb); * would be suboptimal. Also resume them if doing that is not allowed
} else { * to be skipped.
*/
if (atomic_read(&dev->power.usage_count) > 1 ||
!(dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED) &&
dev->power.may_skip_resume))
dev->power.must_resume = true; dev->power.must_resume = true;
}
if (dev->power.must_resume) if (dev->power.must_resume)
dpm_superior_set_must_resume(dev); dpm_superior_set_must_resume(dev);
...@@ -1539,9 +1494,14 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as ...@@ -1539,9 +1494,14 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
if (callback) if (callback)
goto Run; goto Run;
if (dev_pm_smart_suspend_and_suspended(dev) && if (dev_pm_smart_suspend_and_suspended(dev)) {
!dpm_subsys_suspend_noirq_cb(dev, state, NULL)) /*
* In principle, the resume of the device may be skippend if it
* remains in runtime suspend at this point.
*/
dev->power.may_skip_resume = true;
goto Skip; goto Skip;
}
if (dev->driver && dev->driver->pm) { if (dev->driver && dev->driver->pm) {
info = "late driver "; info = "late driver ";
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment