diff mbox series

[v1,10/10] PM: runtime: Discover the lack of runtime PM support

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

Commit Message

Rafael J. Wysocki Feb. 11, 2025, 9:25 p.m. UTC
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.

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(+)

Comments

Rafael J. Wysocki Feb. 12, 2025, 11:14 a.m. UTC | #1
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) {}
>
>
>
>
>
diff mbox series

Patch

--- 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) {}