Skip to content

Commit 0d4b54c

Browse files
committed
PM / core: Add LEAVE_SUSPENDED driver flag
Define and document a new driver flag, DPM_FLAG_LEAVE_SUSPENDED, to instruct the PM core and middle-layer (bus type, PM domain, etc.) code that it is desirable to leave the device in runtime suspend after system-wide transitions to the working state (for example, the device may be slow to resume and it may be better to avoid resuming it right away). Generally, the middle-layer code involved in the handling of the device is expected to indicate to the PM core whether or not the device may be left in suspend with the help of the device's power.may_skip_resume status bit. That has to happen in the "noirq" phase of the preceding system suspend (or analogous) transition. The middle layer is then responsible for handling the device as appropriate in its "noirq" resume callback which is executed regardless of whether or not the device may be left suspended, but the other resume callbacks (except for ->complete) will be skipped automatically by the core if the device really can be left in suspend. The additional power.must_resume status bit introduced for the implementation of this mechanisn is used internally by the PM core to track the requirement to resume the device (which may depend on its children etc). Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
1 parent 4fbd8d1 commit 0d4b54c

File tree

3 files changed

+105
-11
lines changed

3 files changed

+105
-11
lines changed

Documentation/driver-api/pm/devices.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,29 @@ must reflect the "active" status for runtime PM in that case.
788788

789789
During system-wide resume from a sleep state it's easiest to put devices into
790790
the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
791-
Refer to that document for more information regarding this particular issue as
791+
[Refer to that document for more information regarding this particular issue as
792792
well as for information on the device runtime power management framework in
793-
general.
793+
general.]
794+
795+
However, it often is desirable to leave devices in suspend after system
796+
transitions to the working state, especially if those devices had been in
797+
runtime suspend before the preceding system-wide suspend (or analogous)
798+
transition. Device drivers can use the ``DPM_FLAG_LEAVE_SUSPENDED`` flag to
799+
indicate to the PM core (and middle-layer code) that they prefer the specific
800+
devices handled by them to be left suspended and they have no problems with
801+
skipping their system-wide resume callbacks for this reason. Whether or not the
802+
devices will actually be left in suspend may depend on their state before the
803+
given system suspend-resume cycle and on the type of the system transition under
804+
way. In particular, devices are not left suspended if that transition is a
805+
restore from hibernation, as device states are not guaranteed to be reflected
806+
by the information stored in the hibernation image in that case.
807+
808+
The middle-layer code involved in the handling of the device is expected to
809+
indicate to the PM core if the device may be left in suspend by setting its
810+
:c:member:`power.may_skip_resume` status bit which is checked by the PM core
811+
during the "noirq" phase of the preceding system-wide suspend (or analogous)
812+
transition. The middle layer is then responsible for handling the device as
813+
appropriate in its "noirq" resume callback, which is executed regardless of
814+
whether or not the device is left suspended, but the other resume callbacks
815+
(except for ``->complete``) will be skipped automatically by the PM core if the
816+
device really can be left in suspend.

drivers/base/power/main.c

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,18 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
525525

526526
/*------------------------- Resume routines -------------------------*/
527527

528+
/**
529+
* dev_pm_may_skip_resume - System-wide device resume optimization check.
530+
* @dev: Target device.
531+
*
532+
* Checks whether or not the device may be left in suspend after a system-wide
533+
* transition to the working state.
534+
*/
535+
bool dev_pm_may_skip_resume(struct device *dev)
536+
{
537+
return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE;
538+
}
539+
528540
/**
529541
* device_resume_noirq - Execute a "noirq resume" callback for given device.
530542
* @dev: Device to handle.
@@ -573,6 +585,19 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
573585
error = dpm_run_callback(callback, dev, state, info);
574586
dev->power.is_noirq_suspended = false;
575587

588+
if (dev_pm_may_skip_resume(dev)) {
589+
/*
590+
* The device is going to be left in suspend, but it might not
591+
* have been in runtime suspend before the system suspended, so
592+
* its runtime PM status needs to be updated to avoid confusing
593+
* the runtime PM framework when runtime PM is enabled for the
594+
* device again.
595+
*/
596+
pm_runtime_set_suspended(dev);
597+
dev->power.is_late_suspended = false;
598+
dev->power.is_suspended = false;
599+
}
600+
576601
Out:
577602
complete_all(&dev->power.completion);
578603
TRACE_RESUME(error);
@@ -1074,6 +1099,22 @@ static pm_message_t resume_event(pm_message_t sleep_state)
10741099
return PMSG_ON;
10751100
}
10761101

1102+
static void dpm_superior_set_must_resume(struct device *dev)
1103+
{
1104+
struct device_link *link;
1105+
int idx;
1106+
1107+
if (dev->parent)
1108+
dev->parent->power.must_resume = true;
1109+
1110+
idx = device_links_read_lock();
1111+
1112+
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node)
1113+
link->supplier->power.must_resume = true;
1114+
1115+
device_links_read_unlock(idx);
1116+
}
1117+
10771118
/**
10781119
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
10791120
* @dev: Device to handle.
@@ -1125,10 +1166,28 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
11251166
}
11261167

11271168
error = dpm_run_callback(callback, dev, state, info);
1128-
if (!error)
1129-
dev->power.is_noirq_suspended = true;
1130-
else
1169+
if (error) {
11311170
async_error = error;
1171+
goto Complete;
1172+
}
1173+
1174+
dev->power.is_noirq_suspended = true;
1175+
1176+
if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) {
1177+
/*
1178+
* The only safe strategy here is to require that if the device
1179+
* may not be left in suspend, resume callbacks must be invoked
1180+
* for it.
1181+
*/
1182+
dev->power.must_resume = dev->power.must_resume ||
1183+
!dev->power.may_skip_resume ||
1184+
atomic_read(&dev->power.usage_count) > 1;
1185+
} else {
1186+
dev->power.must_resume = true;
1187+
}
1188+
1189+
if (dev->power.must_resume)
1190+
dpm_superior_set_must_resume(dev);
11321191

11331192
Complete:
11341193
complete_all(&dev->power.completion);
@@ -1485,6 +1544,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
14851544
dev->power.direct_complete = false;
14861545
}
14871546

1547+
dev->power.may_skip_resume = false;
1548+
dev->power.must_resume = false;
1549+
14881550
dpm_watchdog_set(&wd, dev);
14891551
device_lock(dev);
14901552

@@ -1650,8 +1712,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
16501712
if (dev->power.syscore)
16511713
return 0;
16521714

1653-
WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
1654-
!pm_runtime_enabled(dev));
1715+
WARN_ON(!pm_runtime_enabled(dev) &&
1716+
dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
1717+
DPM_FLAG_LEAVE_SUSPENDED));
16551718

16561719
/*
16571720
* If a device's parent goes into runtime suspend at the wrong time,

include/linux/pm.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,10 @@ struct pm_subsys_data {
556556
* These flags can be set by device drivers at the probe time. They need not be
557557
* cleared by the drivers as the driver core will take care of that.
558558
*
559-
* NEVER_SKIP: Do not skip system suspend/resume callbacks for the device.
559+
* NEVER_SKIP: Do not skip all system suspend/resume callbacks for the device.
560560
* SMART_PREPARE: Check the return value of the driver's ->prepare callback.
561561
* SMART_SUSPEND: No need to resume the device from runtime suspend.
562+
* LEAVE_SUSPENDED: Avoid resuming the device during system resume if possible.
562563
*
563564
* Setting SMART_PREPARE instructs bus types and PM domains which may want
564565
* system suspend/resume callbacks to be skipped for the device to return 0 from
@@ -572,10 +573,14 @@ struct pm_subsys_data {
572573
* necessary from the driver's perspective. It also may cause them to skip
573574
* invocations of the ->suspend_late and ->suspend_noirq callbacks provided by
574575
* the driver if they decide to leave the device in runtime suspend.
576+
*
577+
* Setting LEAVE_SUSPENDED informs the PM core and middle-layer code that the
578+
* driver prefers the device to be left in suspend after system resume.
575579
*/
576-
#define DPM_FLAG_NEVER_SKIP BIT(0)
577-
#define DPM_FLAG_SMART_PREPARE BIT(1)
578-
#define DPM_FLAG_SMART_SUSPEND BIT(2)
580+
#define DPM_FLAG_NEVER_SKIP BIT(0)
581+
#define DPM_FLAG_SMART_PREPARE BIT(1)
582+
#define DPM_FLAG_SMART_SUSPEND BIT(2)
583+
#define DPM_FLAG_LEAVE_SUSPENDED BIT(3)
579584

580585
struct dev_pm_info {
581586
pm_message_t power_state;
@@ -597,6 +602,8 @@ struct dev_pm_info {
597602
bool wakeup_path:1;
598603
bool syscore:1;
599604
bool no_pm_callbacks:1; /* Owned by the PM core */
605+
unsigned int must_resume:1; /* Owned by the PM core */
606+
unsigned int may_skip_resume:1; /* Set by subsystems */
600607
#else
601608
unsigned int should_wakeup:1;
602609
#endif
@@ -765,6 +772,7 @@ extern int pm_generic_poweroff_late(struct device *dev);
765772
extern int pm_generic_poweroff(struct device *dev);
766773
extern void pm_generic_complete(struct device *dev);
767774

775+
extern bool dev_pm_may_skip_resume(struct device *dev);
768776
extern bool dev_pm_smart_suspend_and_suspended(struct device *dev);
769777

770778
#else /* !CONFIG_PM_SLEEP */

0 commit comments

Comments
 (0)