linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Bjorn Helgaas <helgaas@kernel.org>
To: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Linux PM <linux-pm@vger.kernel.org>,
	LKML <linux-kernel@vger.kernel.org>,
	Alan Stern <stern@rowland.harvard.edu>,
	Linux PCI <linux-pci@vger.kernel.org>,
	Ulf Hansson <ulf.hansson@linaro.org>,
	Johan Hovold <johan@kernel.org>,
	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>,
	Jon Hunter <jonathanh@nvidia.com>,
	Linux ACPI <linux-acpi@vger.kernel.org>,
	Mika Westerberg <mika.westerberg@linux.intel.com>,
	Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Subject: Re: [PATCH v2 3/4] PM: sleep: Use DPM_FLAG_SMART_SUSPEND conditionally
Date: Tue, 18 Feb 2025 16:20:11 -0600	[thread overview]
Message-ID: <20250218222011.GA196831@bhelgaas> (raw)
In-Reply-To: <1914558.tdWV9SEqCh@rjwysocki.net>

On Tue, Feb 18, 2025 at 09:16:48PM +0100, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> A recent discussion has revealed that using DPM_FLAG_SMART_SUSPEND
> unconditionally is generally problematic because it may lead to
> situations in which the device's runtime PM information is internally
> inconsistent or does not reflect its real state [1].
> 
> For this reason, change the handling of DPM_FLAG_SMART_SUSPEND so that
> it is only taken into account if it is consistently set by the drivers
> of all devices having any PM callbacks throughout dependency graphs in
> accordance with the following rules:
> 
>  - The "smart suspend" feature is only enabled for devices whose drivers
>    ask for it (that is, set DPM_FLAG_SMART_SUSPEND) and for devices
>    without PM callbacks unless they have never had runtime PM enabled.
> 
>  - The "smart suspend" feature is not enabled for a device if it has not
>    been enabled for the device's parent unless the parent does not take
>    children into account or it has never had runtime PM enabled.
> 
>  - The "smart suspend" feature is not enabled for a device if it has not
>    been enabled for one of the device's suppliers taking runtime PM into
>    account unless that supplier has never had runtime PM enabled.
> 
> Namely, introduce a new device PM flag called smart_suspend that is only
> set if the above conditions are met and update all DPM_FLAG_SMART_SUSPEND
> users to check power.smart_suspend instead of directly checking the
> latter.
> 
> At the same time, drop the power.set_active flage introduced recently
> in commit 3775fc538f53 ("PM: sleep: core: Synchronize runtime PM status
> of parents and children") because it is now sufficient to check
> power.smart_suspend along with the dev_pm_skip_resume() return value
> to decide whether or not pm_runtime_set_active() needs to be called
> for the device.
> 
> Link: https://lore.kernel.org/linux-pm/CAPDyKFroyU3YDSfw_Y6k3giVfajg3NQGwNWeteJWqpW29BojhQ@mail.gmail.com/  [1]
> Fixes: 7585946243d6 ("PM: sleep: core: Restrict power.set_active propagation")
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Acked-by: Bjorn Helgaas <bhelgaas@google.com> # drivers/pci

> ---
> 
> v1 -> v2:
>    * Add helper function for reading power.smart_suspend() (Ulf), add the R-by.
>    * Rearrange conditionals in device_prepare_smart_suspend() so that the checks
>      involving locking are done last.
> 
> ---
>  drivers/acpi/device_pm.c  |    4 +-
>  drivers/base/power/main.c |   63 +++++++++++++++++++++++++++++++++++-----------
>  drivers/mfd/intel-lpss.c  |    2 -
>  drivers/pci/pci-driver.c  |    6 +---
>  include/linux/device.h    |    5 +++
>  include/linux/pm.h        |    2 -
>  6 files changed, 60 insertions(+), 22 deletions(-)
> 
> --- a/drivers/acpi/device_pm.c
> +++ b/drivers/acpi/device_pm.c
> @@ -1161,7 +1161,7 @@
>   */
>  int acpi_subsys_suspend(struct device *dev)
>  {
> -	if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
> +	if (!dev_pm_smart_suspend(dev) ||
>  	    acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
>  		pm_runtime_resume(dev);
>  
> @@ -1320,7 +1320,7 @@
>   */
>  int acpi_subsys_poweroff(struct device *dev)
>  {
> -	if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
> +	if (!dev_pm_smart_suspend(dev) ||
>  	    acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
>  		pm_runtime_resume(dev);
>  
> --- a/drivers/base/power/main.c
> +++ b/drivers/base/power/main.c
> @@ -656,15 +656,13 @@
>  	 * so change its status accordingly.
>  	 *
>  	 * Otherwise, the device is going to be resumed, so set its PM-runtime
> -	 * status to "active" unless its power.set_active flag is clear, in
> +	 * status to "active" unless its power.smart_suspend flag is clear, in
>  	 * which case it is not necessary to update its PM-runtime status.
>  	 */
> -	if (skip_resume) {
> +	if (skip_resume)
>  		pm_runtime_set_suspended(dev);
> -	} else if (dev->power.set_active) {
> +	else if (dev_pm_smart_suspend(dev))
>  		pm_runtime_set_active(dev);
> -		dev->power.set_active = false;
> -	}
>  
>  	if (dev->pm_domain) {
>  		info = "noirq power domain ";
> @@ -1282,14 +1280,8 @@
>  	      dev->power.may_skip_resume))
>  		dev->power.must_resume = true;
>  
> -	if (dev->power.must_resume) {
> -		if (dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) {
> -			dev->power.set_active = true;
> -			if (dev->parent && !dev->parent->power.ignore_children)
> -				dev->parent->power.set_active = true;
> -		}
> +	if (dev->power.must_resume)
>  		dpm_superior_set_must_resume(dev);
> -	}
>  
>  Complete:
>  	complete_all(&dev->power.completion);
> @@ -1797,6 +1789,49 @@
>  	return error;
>  }
>  
> +static void device_prepare_smart_suspend(struct device *dev)
> +{
> +	struct device_link *link;
> +	int idx;
> +
> +	/*
> +	 * The "smart suspend" feature is enabled for devices whose drivers ask
> +	 * for it and for devices without PM callbacks unless runtime PM is
> +	 * disabled and enabling it is blocked for them.
> +	 *
> +	 * However, if "smart suspend" is not enabled for the device's parent
> +	 * or any of its suppliers that take runtime PM into account, it cannot
> +	 * be enabled for the device either.
> +	 */
> +	dev->power.smart_suspend = (dev->power.no_pm_callbacks ||
> +		dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) &&
> +		!pm_runtime_blocked(dev);
> +
> +	if (!dev_pm_smart_suspend(dev))
> +		return;
> +
> +	if (dev->parent && !dev_pm_smart_suspend(dev->parent) &&
> +	    !dev->parent->power.ignore_children && !pm_runtime_blocked(dev->parent)) {
> +		dev->power.smart_suspend = false;
> +		return;
> +	}
> +
> +	idx = device_links_read_lock();
> +
> +	list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) {
> +		if (!(link->flags | DL_FLAG_PM_RUNTIME))
> +			continue;
> +
> +		if (!dev_pm_smart_suspend(link->supplier) &&
> +		    !pm_runtime_blocked(link->supplier)) {
> +			dev->power.smart_suspend = false;
> +			break;
> +		}
> +	}
> +
> +	device_links_read_unlock(idx);
> +}
> +
>  /**
>   * device_prepare - Prepare a device for system power transition.
>   * @dev: Device to handle.
> @@ -1858,6 +1893,7 @@
>  		pm_runtime_put(dev);
>  		return ret;
>  	}
> +	device_prepare_smart_suspend(dev);
>  	/*
>  	 * A positive return value from ->prepare() means "this device appears
>  	 * to be runtime-suspended and its state is fine, so if it really is
> @@ -2033,6 +2069,5 @@
>  
>  bool dev_pm_skip_suspend(struct device *dev)
>  {
> -	return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
> -		pm_runtime_status_suspended(dev);
> +	return dev_pm_smart_suspend(dev) && pm_runtime_status_suspended(dev);
>  }
> --- a/drivers/mfd/intel-lpss.c
> +++ b/drivers/mfd/intel-lpss.c
> @@ -480,7 +480,7 @@
>  
>  static int resume_lpss_device(struct device *dev, void *data)
>  {
> -	if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
> +	if (!dev_pm_smart_suspend(dev))
>  		pm_runtime_resume(dev);
>  
>  	return 0;
> --- a/drivers/pci/pci-driver.c
> +++ b/drivers/pci/pci-driver.c
> @@ -812,8 +812,7 @@
>  	 * suspend callbacks can cope with runtime-suspended devices, it is
>  	 * better to resume the device from runtime suspend here.
>  	 */
> -	if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
> -	    pci_dev_need_resume(pci_dev)) {
> +	if (!dev_pm_smart_suspend(dev) || pci_dev_need_resume(pci_dev)) {
>  		pm_runtime_resume(dev);
>  		pci_dev->state_saved = false;
>  	} else {
> @@ -1151,8 +1150,7 @@
>  	}
>  
>  	/* The reason to do that is the same as in pci_pm_suspend(). */
> -	if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
> -	    pci_dev_need_resume(pci_dev)) {
> +	if (!dev_pm_smart_suspend(dev) || pci_dev_need_resume(pci_dev)) {
>  		pm_runtime_resume(dev);
>  		pci_dev->state_saved = false;
>  	} else {
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -1025,6 +1025,11 @@
>  	return !!(dev->power.driver_flags & flags);
>  }
>  
> +static inline bool dev_pm_smart_suspend(struct device *dev)
> +{
> +	return dev->power.smart_suspend;
> +}
> +
>  static inline void device_lock(struct device *dev)
>  {
>  	mutex_lock(&dev->mutex);
> --- a/include/linux/pm.h
> +++ b/include/linux/pm.h
> @@ -680,8 +680,8 @@
>  	bool			syscore:1;
>  	bool			no_pm_callbacks:1;	/* Owned by the PM core */
>  	bool			async_in_progress:1;	/* Owned by the PM core */
> +	bool			smart_suspend:1;	/* Owned by the PM core */
>  	bool			must_resume:1;		/* Owned by the PM core */
> -	bool			set_active:1;		/* Owned by the PM core */
>  	bool			may_skip_resume:1;	/* Set by subsystems */
>  #else
>  	bool			should_wakeup:1;
> 
> 
> 

  reply	other threads:[~2025-02-18 22:20 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-18 20:09 [PATCH v2 0/4] PM: Use DPM_FLAG_SMART_SUSPEND conditionally Rafael J. Wysocki
2025-02-18 20:11 ` [PATCH v2 1/4] PM: Block enabling of runtime PM during system suspend Rafael J. Wysocki
2025-02-18 20:13 ` [PATCH v2 2/4] PM: runtime: Introduce pm_runtime_blocked() Rafael J. Wysocki
2025-02-18 20:16 ` [PATCH v2 3/4] PM: sleep: Use DPM_FLAG_SMART_SUSPEND conditionally Rafael J. Wysocki
2025-02-18 22:20   ` Bjorn Helgaas [this message]
2025-02-19 12:14     ` Rafael J. Wysocki
2025-02-18 20:20 ` [PATCH v2 4/4] PM: sleep: Avoid unnecessary checks in device_prepare_smart_suspend() Rafael J. Wysocki
2025-03-03 11:35   ` Ulf Hansson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250218222011.GA196831@bhelgaas \
    --to=helgaas@kernel.org \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=johan@kernel.org \
    --cc=jonathanh@nvidia.com \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=manivannan.sadhasivam@linaro.org \
    --cc=mika.westerberg@linux.intel.com \
    --cc=rjw@rjwysocki.net \
    --cc=stern@rowland.harvard.edu \
    --cc=ulf.hansson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).