Skip to content

Commit c24efa6

Browse files
committed
PM: runtime: Capture device status before disabling runtime PM
In some cases (for example, during system-wide suspend and resume of devices) it is useful to know whether or not runtime PM has ever been enabled for a given device and, if so, what the runtime PM status of it had been right before runtime PM was disabled for it last time. For this reason, introduce a new struct dev_pm_info field called last_status that will be used for capturing the runtime PM status of the device when its power.disable_depth counter changes from 0 to 1. The new field will be set to RPM_INVALID to start with and whenever power.disable_depth changes from 1 to 0, so it will be valid only when runtime PM of the device is currently disabled, but it has been enabled at least once. Immediately use power.last_status in rpm_resume() to make it handle the case when PM runtime is disabled for the device, but its runtime PM status is RPM_ACTIVE more consistently. Namely, make it return 1 if power.last_status is also equal to RPM_ACTIVE in that case (the idea being that if the status was RPM_ACTIVE last time when power.disable_depth was changing from 0 to 1 and it is still RPM_ACTIVE, it can be assumed to reflect what happened to the device last time when it was using runtime PM) and -EACCES otherwise. Update the documentation to provide a description of last_status and change the description of pm_runtime_resume() in it to reflect the new behavior of rpm_active(). While at it, rearrange the code in pm_runtime_enable() to be more straightforward and replace the WARN() macro in it with a pr_warn() invocation which is less disruptive. Link: https://lore.kernel.org/linux-pm/[email protected]/t/#u Reviewed-by: Ulf Hansson <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 1a3c7bb commit c24efa6

File tree

3 files changed

+37
-24
lines changed

3 files changed

+37
-24
lines changed

Documentation/power/runtime_pm.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ defined in include/linux/pm.h:
265265
RPM_SUSPENDED, which means that each device is initially regarded by the
266266
PM core as 'suspended', regardless of its real hardware status
267267

268+
`enum rpm_status last_status;`
269+
- the last runtime PM status of the device captured before disabling runtime
270+
PM for it (invalid initially and when disable_depth is 0)
271+
268272
`unsigned int runtime_auto;`
269273
- if set, indicates that the user space has allowed the device driver to
270274
power manage the device at run time via the /sys/devices/.../power/control
@@ -333,10 +337,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
333337

334338
`int pm_runtime_resume(struct device *dev);`
335339
- execute the subsystem-level resume callback for the device; returns 0 on
336-
success, 1 if the device's runtime PM status was already 'active' or
337-
error code on failure, where -EAGAIN means it may be safe to attempt to
338-
resume the device again in future, but 'power.runtime_error' should be
339-
checked additionally, and -EACCES means that 'power.disable_depth' is
340+
success, 1 if the device's runtime PM status is already 'active' (also if
341+
'power.disable_depth' is nonzero, but the status was 'active' when it was
342+
changing from 0 to 1) or error code on failure, where -EAGAIN means it may
343+
be safe to attempt to resume the device again in future, but
344+
'power.runtime_error' should be checked additionally, and -EACCES means
345+
that the callback could not be run, because 'power.disable_depth' was
340346
different from 0
341347

342348
`int pm_runtime_resume_and_get(struct device *dev);`

drivers/base/power/runtime.c

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -742,13 +742,15 @@ static int rpm_resume(struct device *dev, int rpmflags)
742742
trace_rpm_resume_rcuidle(dev, rpmflags);
743743

744744
repeat:
745-
if (dev->power.runtime_error)
745+
if (dev->power.runtime_error) {
746746
retval = -EINVAL;
747-
else if (dev->power.disable_depth == 1 && dev->power.is_suspended
748-
&& dev->power.runtime_status == RPM_ACTIVE)
749-
retval = 1;
750-
else if (dev->power.disable_depth > 0)
751-
retval = -EACCES;
747+
} else if (dev->power.disable_depth > 0) {
748+
if (dev->power.runtime_status == RPM_ACTIVE &&
749+
dev->power.last_status == RPM_ACTIVE)
750+
retval = 1;
751+
else
752+
retval = -EACCES;
753+
}
752754
if (retval)
753755
goto out;
754756

@@ -1410,8 +1412,10 @@ void __pm_runtime_disable(struct device *dev, bool check_resume)
14101412
/* Update time accounting before disabling PM-runtime. */
14111413
update_pm_runtime_accounting(dev);
14121414

1413-
if (!dev->power.disable_depth++)
1415+
if (!dev->power.disable_depth++) {
14141416
__pm_runtime_barrier(dev);
1417+
dev->power.last_status = dev->power.runtime_status;
1418+
}
14151419

14161420
out:
14171421
spin_unlock_irq(&dev->power.lock);
@@ -1428,23 +1432,23 @@ void pm_runtime_enable(struct device *dev)
14281432

14291433
spin_lock_irqsave(&dev->power.lock, flags);
14301434

1431-
if (dev->power.disable_depth > 0) {
1432-
dev->power.disable_depth--;
1433-
1434-
/* About to enable runtime pm, set accounting_timestamp to now */
1435-
if (!dev->power.disable_depth)
1436-
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();
1437-
} else {
1435+
if (!dev->power.disable_depth) {
14381436
dev_warn(dev, "Unbalanced %s!\n", __func__);
1437+
goto out;
14391438
}
14401439

1441-
WARN(!dev->power.disable_depth &&
1442-
dev->power.runtime_status == RPM_SUSPENDED &&
1443-
!dev->power.ignore_children &&
1444-
atomic_read(&dev->power.child_count) > 0,
1445-
"Enabling runtime PM for inactive device (%s) with active children\n",
1446-
dev_name(dev));
1440+
if (--dev->power.disable_depth > 0)
1441+
goto out;
14471442

1443+
dev->power.last_status = RPM_INVALID;
1444+
dev->power.accounting_timestamp = ktime_get_mono_fast_ns();
1445+
1446+
if (dev->power.runtime_status == RPM_SUSPENDED &&
1447+
!dev->power.ignore_children &&
1448+
atomic_read(&dev->power.child_count) > 0)
1449+
dev_warn(dev, "Enabling runtime PM for inactive device with active children\n");
1450+
1451+
out:
14481452
spin_unlock_irqrestore(&dev->power.lock, flags);
14491453
}
14501454
EXPORT_SYMBOL_GPL(pm_runtime_enable);
@@ -1640,6 +1644,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_use_autosuspend);
16401644
void pm_runtime_init(struct device *dev)
16411645
{
16421646
dev->power.runtime_status = RPM_SUSPENDED;
1647+
dev->power.last_status = RPM_INVALID;
16431648
dev->power.idle_notification = false;
16441649

16451650
dev->power.disable_depth = 1;

include/linux/pm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ const struct dev_pm_ops __maybe_unused name = { \
521521
*/
522522

523523
enum rpm_status {
524+
RPM_INVALID = -1,
524525
RPM_ACTIVE = 0,
525526
RPM_RESUMING,
526527
RPM_SUSPENDED,
@@ -634,6 +635,7 @@ struct dev_pm_info {
634635
unsigned int links_count;
635636
enum rpm_request request;
636637
enum rpm_status runtime_status;
638+
enum rpm_status last_status;
637639
int runtime_error;
638640
int autosuspend_delay;
639641
u64 last_busy;

0 commit comments

Comments
 (0)