Message ID | 2511990.jE0xQCEvom@rjwysocki.net (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Series | PM: Make the core and pm_runtime_force_suspend/resume() agree more | expand |
On Tue, Feb 11, 2025 at 10:25 PM Rafael J. Wysocki <rjw@rjwysocki.net> wrote: > > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > Previous changes have updated the PM core to special-case devices that > have never had runtime PM enabled in some places, but what if a device > had had runtime PM enabled at one point, but then it was permanently > disabled? Arguably, there is not much of a difference between such > devices and the devices that have never had runtime PM enabled as far > as system-wide suspend and resume is concerned, so they should be > handled in the same way. > > For this reason, add a mechanism for discovering "lost" runtime PM > support in devices with the help of the power.last_status field used > for saving the last runtime PM status of the device known at the time > when runtime PM was disabled for it. > > That field is set to RPM_INVALID initially and whenever runtime PM is > enabled for a device (that is, when its power.disable_depth counter > drops down to zero) and it is set to the current runtime PM status of > the device when runtime PM is disabled (that is, the power.disable_depth > counter becomes nonzero). Therefore, if power.last_status is equal to > RPM_INVALID for a device with runtime PM disabled, it means that > runtime PM has never been enabled for that device. > > The PM core will now change the power.last_status value to RPM_UNKNOWN > for devices having runtime PM disabled and power.last_status different > from RPM_INVALID during the "prepare" phase of system suspend. Then, > __pm_runtime_disable() called subsequently on the device will set > power.last_status to RPM_INVALID unless it changes from RPM_UNKNOWN > to some other value in the meantime which requires enabling runtime PM > for the device. When power.last_status becomes RPM_INVALID and runtime > PM is still disabled, the device will be handled as a "no runtime PM > support" one from that point on until runtime PM is enabled for it > again. So the interim RPM_UNKNOWN value of last_status isn't really necessary, it may as well be changed directly to RPM_INVALID in device_prepare(). Scratch this one and I'll replace it with a different patch. > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> > --- > drivers/base/power/main.c | 6 ++++++ > drivers/base/power/runtime.c | 25 +++++++++++++++++++++++++ > include/linux/pm.h | 1 + > include/linux/pm_runtime.h | 2 ++ > 4 files changed, 34 insertions(+) > > --- a/drivers/base/power/main.c > +++ b/drivers/base/power/main.c > @@ -1817,6 +1817,12 @@ > * it again during the complete phase. > */ > pm_runtime_get_noresume(dev); > + /* > + * Devices that have had runtime PM disabled recently may need to be > + * handled as though they have never supported it, so arrange for > + * detecting that situation. > + */ > + pm_runtime_kick_last_status(dev); > > if (dev->power.syscore) > return 0; > --- a/drivers/base/power/runtime.c > +++ b/drivers/base/power/runtime.c > @@ -1480,6 +1480,9 @@ > > if (dev->power.disable_depth > 0) { > dev->power.disable_depth++; > + if (dev->power.last_status == RPM_UNKNOWN) > + dev->power.last_status = RPM_INVALID; > + > goto out; > } > > @@ -1568,6 +1571,28 @@ > EXPORT_SYMBOL_GPL(devm_pm_runtime_enable); > > /** > + * pm_runtime_kick_last_status - Start runtime PM support verification. > + * @dev: Target device. > + * > + * If runtime PM is currently disabled for @dev, but it has been enabled at one > + * point, change power.last_status for it to RPM_UNKNOWN, and if it is still > + * RPM_UNKNOWN when __pm_runtime_disabled() is called for @dev next time, it > + * will be changed to RPM_INVALID indicating no runtime PM support going > + * forward until pm_runtime_enable() is called for @dev. > + * > + * This function is used by the PM core. > + */ > +void pm_runtime_kick_last_status(struct device *dev) > +{ > + spin_lock_irq(&dev->power.lock); > + > + if (dev->power.disable_depth && dev->power.last_status != RPM_INVALID) > + dev->power.last_status = RPM_UNKNOWN; > + > + spin_unlock_irq(&dev->power.lock); > +} > + > +/** > * pm_runtime_forbid - Block runtime PM of a device. > * @dev: Device to handle. > * > --- a/include/linux/pm.h > +++ b/include/linux/pm.h > @@ -597,6 +597,7 @@ > RPM_RESUMING, > RPM_SUSPENDED, > RPM_SUSPENDING, > + RPM_UNKNOWN, > }; > > /* > --- a/include/linux/pm_runtime.h > +++ b/include/linux/pm_runtime.h > @@ -80,6 +80,7 @@ > extern int pm_runtime_barrier(struct device *dev); > extern void pm_runtime_enable(struct device *dev); > extern void __pm_runtime_disable(struct device *dev, bool check_resume); > +extern void pm_runtime_kick_last_status(struct device *dev); > extern void pm_runtime_allow(struct device *dev); > extern void pm_runtime_forbid(struct device *dev); > extern void pm_runtime_no_callbacks(struct device *dev); > @@ -288,6 +289,7 @@ > static inline int pm_runtime_barrier(struct device *dev) { return 0; } > static inline void pm_runtime_enable(struct device *dev) {} > static inline void __pm_runtime_disable(struct device *dev, bool c) {} > +static inline void pm_runtime_kick_last_status(struct device *dev) {} > static inline void pm_runtime_allow(struct device *dev) {} > static inline void pm_runtime_forbid(struct device *dev) {} > > > > >
--- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1817,6 +1817,12 @@ * it again during the complete phase. */ pm_runtime_get_noresume(dev); + /* + * Devices that have had runtime PM disabled recently may need to be + * handled as though they have never supported it, so arrange for + * detecting that situation. + */ + pm_runtime_kick_last_status(dev); if (dev->power.syscore) return 0; --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1480,6 +1480,9 @@ if (dev->power.disable_depth > 0) { dev->power.disable_depth++; + if (dev->power.last_status == RPM_UNKNOWN) + dev->power.last_status = RPM_INVALID; + goto out; } @@ -1568,6 +1571,28 @@ EXPORT_SYMBOL_GPL(devm_pm_runtime_enable); /** + * pm_runtime_kick_last_status - Start runtime PM support verification. + * @dev: Target device. + * + * If runtime PM is currently disabled for @dev, but it has been enabled at one + * point, change power.last_status for it to RPM_UNKNOWN, and if it is still + * RPM_UNKNOWN when __pm_runtime_disabled() is called for @dev next time, it + * will be changed to RPM_INVALID indicating no runtime PM support going + * forward until pm_runtime_enable() is called for @dev. + * + * This function is used by the PM core. + */ +void pm_runtime_kick_last_status(struct device *dev) +{ + spin_lock_irq(&dev->power.lock); + + if (dev->power.disable_depth && dev->power.last_status != RPM_INVALID) + dev->power.last_status = RPM_UNKNOWN; + + spin_unlock_irq(&dev->power.lock); +} + +/** * pm_runtime_forbid - Block runtime PM of a device. * @dev: Device to handle. * --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -597,6 +597,7 @@ RPM_RESUMING, RPM_SUSPENDED, RPM_SUSPENDING, + RPM_UNKNOWN, }; /* --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -80,6 +80,7 @@ extern int pm_runtime_barrier(struct device *dev); extern void pm_runtime_enable(struct device *dev); extern void __pm_runtime_disable(struct device *dev, bool check_resume); +extern void pm_runtime_kick_last_status(struct device *dev); extern void pm_runtime_allow(struct device *dev); extern void pm_runtime_forbid(struct device *dev); extern void pm_runtime_no_callbacks(struct device *dev); @@ -288,6 +289,7 @@ static inline int pm_runtime_barrier(struct device *dev) { return 0; } static inline void pm_runtime_enable(struct device *dev) {} static inline void __pm_runtime_disable(struct device *dev, bool c) {} +static inline void pm_runtime_kick_last_status(struct device *dev) {} static inline void pm_runtime_allow(struct device *dev) {} static inline void pm_runtime_forbid(struct device *dev) {}