linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] PCI: PM/ACPI: Refresh all stale power state data in pci_pm_complete()
@ 2019-06-25 12:09 Rafael J. Wysocki
  2019-06-25 16:09 ` Mika Westerberg
  0 siblings, 1 reply; 3+ messages in thread
From: Rafael J. Wysocki @ 2019-06-25 12:09 UTC (permalink / raw)
  To: Linux PCI
  Cc: Linux ACPI, Linux PM, LKML, Bjorn Helgaas, Mika Westerberg, Lukas Wunner

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

In pci_pm_complete() there are checks to decide whether or not to
resume devices that were left in runtime-suspend during the preceding
system-wide transition into a sleep state.  They involve checking the
current power state of the device and comparing it with the power
state of it set before the preceding system-wide transition, but the
platform component of the device's power state is not handled
correctly in there.

Namely, on platforms with ACPI, the device power state information
needs to be updated with care, so that the reference counters of
power resources used by the device (if any) are set to ensure that
the refreshed power state of it will be maintained going forward.

To that end, introduce a new ->refresh_state() platform PM callback
for PCI devices, for asking the platform to refresh the device power
state data and ensure that the corresponding power state will be
maintained going forward, make it invoke acpi_device_update_power()
(for devices with ACPI PM) on platforms with ACPI and make
pci_pm_complete() use it, through a new pci_refresh_power_state()
wrapper function.

Fixes: a0d2a959d3da (PCI: Avoid unnecessary resume after direct-complete)
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/pci/pci-acpi.c   |    9 +++++++++
 drivers/pci/pci-driver.c |    9 ++++++++-
 drivers/pci/pci.c        |   21 +++++++++++++++++++++
 drivers/pci/pci.h        |    4 ++++
 4 files changed, 42 insertions(+), 1 deletion(-)

Index: linux-pm/drivers/pci/pci-acpi.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-acpi.c
+++ linux-pm/drivers/pci/pci-acpi.c
@@ -691,6 +691,14 @@ static pci_power_t acpi_pci_get_power_st
 	return state_conv[state];
 }
 
+static void acpi_pci_refresh_power_state(struct pci_dev *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
+
+	if (adev && acpi_device_power_manageable(adev))
+		acpi_device_update_power(adev, NULL);
+}
+
 static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
 {
 	while (bus->parent) {
@@ -748,6 +756,7 @@ static const struct pci_platform_pm_ops
 	.is_manageable = acpi_pci_power_manageable,
 	.set_state = acpi_pci_set_power_state,
 	.get_state = acpi_pci_get_power_state,
+	.refresh_state = acpi_pci_refresh_power_state,
 	.choose_state = acpi_pci_choose_state,
 	.set_wakeup = acpi_pci_wakeup,
 	.need_resume = acpi_pci_need_resume,
Index: linux-pm/drivers/pci/pci.h
===================================================================
--- linux-pm.orig/drivers/pci/pci.h
+++ linux-pm/drivers/pci/pci.h
@@ -51,6 +51,8 @@ int pci_bus_error_reset(struct pci_dev *
  *
  * @get_state: queries the platform firmware for a device's current power state
  *
+ * @refresh_state: asks the platform to refresh the device's power state data
+ *
  * @choose_state: returns PCI power state of given device preferred by the
  *		  platform; to be used during system-wide transitions from a
  *		  sleeping state to the working state and vice versa
@@ -69,6 +71,7 @@ struct pci_platform_pm_ops {
 	bool (*is_manageable)(struct pci_dev *dev);
 	int (*set_state)(struct pci_dev *dev, pci_power_t state);
 	pci_power_t (*get_state)(struct pci_dev *dev);
+	void (*refresh_state)(struct pci_dev *dev);
 	pci_power_t (*choose_state)(struct pci_dev *dev);
 	int (*set_wakeup)(struct pci_dev *dev, bool enable);
 	bool (*need_resume)(struct pci_dev *dev);
@@ -76,6 +79,7 @@ struct pci_platform_pm_ops {
 
 int pci_set_platform_pm(const struct pci_platform_pm_ops *ops);
 void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
+void pci_refresh_power_state(struct pci_dev *dev);
 void pci_power_up(struct pci_dev *dev);
 void pci_disable_enabled_device(struct pci_dev *dev);
 int pci_finish_runtime_suspend(struct pci_dev *dev);
Index: linux-pm/drivers/pci/pci.c
===================================================================
--- linux-pm.orig/drivers/pci/pci.c
+++ linux-pm/drivers/pci/pci.c
@@ -777,6 +777,12 @@ static inline pci_power_t platform_pci_g
 	return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN;
 }
 
+static inline void platform_pci_refresh_power_state(struct pci_dev *dev)
+{
+	if (pci_platform_pm && pci_platform_pm->refresh_state)
+		pci_platform_pm->refresh_state(dev);
+}
+
 static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
 {
 	return pci_platform_pm ?
@@ -938,6 +944,21 @@ void pci_update_current_state(struct pci
 }
 
 /**
+ * pci_refresh_power_state - Refresh the given device's power state data
+ * @dev: Target PCI device.
+ *
+ * Ask the platform to refresh the devices power state information and invoke
+ * pci_update_current_state() to update its current PCI power state.
+ */
+void pci_refresh_power_state(struct pci_dev *dev)
+{
+	if (platform_pci_power_manageable(dev))
+		platform_pci_refresh_power_state(dev);
+
+	pci_update_current_state(dev, dev->current_state);
+}
+
+/**
  * pci_power_up - Put the given device into D0 forcibly
  * @dev: PCI device to power up
  */
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -710,7 +710,14 @@ static void pci_pm_complete(struct devic
 	if (pm_runtime_suspended(dev) && pm_resume_via_firmware()) {
 		pci_power_t pre_sleep_state = pci_dev->current_state;
 
-		pci_update_current_state(pci_dev, pci_dev->current_state);
+		pci_refresh_power_state(pci_dev);
+		/*
+		 * On platforms with ACPI this check may also trigger for
+		 * devices sharing power resources if one of those power
+		 * resources has been activated as a result of a change of the
+		 * power state of another device sharing it.  However, in that
+		 * case it is also better to resume the device, in general.
+		 */
 		if (pci_dev->current_state < pre_sleep_state)
 			pm_request_resume(dev);
 	}




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] PCI: PM/ACPI: Refresh all stale power state data in pci_pm_complete()
  2019-06-25 12:09 [PATCH] PCI: PM/ACPI: Refresh all stale power state data in pci_pm_complete() Rafael J. Wysocki
@ 2019-06-25 16:09 ` Mika Westerberg
  2019-07-05  9:48   ` Rafael J. Wysocki
  0 siblings, 1 reply; 3+ messages in thread
From: Mika Westerberg @ 2019-06-25 16:09 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PCI, Linux ACPI, Linux PM, LKML, Bjorn Helgaas, Lukas Wunner

On Tue, Jun 25, 2019 at 02:09:12PM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> In pci_pm_complete() there are checks to decide whether or not to
> resume devices that were left in runtime-suspend during the preceding
> system-wide transition into a sleep state.  They involve checking the
> current power state of the device and comparing it with the power
> state of it set before the preceding system-wide transition, but the
> platform component of the device's power state is not handled
> correctly in there.
> 
> Namely, on platforms with ACPI, the device power state information
> needs to be updated with care, so that the reference counters of
> power resources used by the device (if any) are set to ensure that
> the refreshed power state of it will be maintained going forward.
> 
> To that end, introduce a new ->refresh_state() platform PM callback
> for PCI devices, for asking the platform to refresh the device power
> state data and ensure that the corresponding power state will be
> maintained going forward, make it invoke acpi_device_update_power()
> (for devices with ACPI PM) on platforms with ACPI and make
> pci_pm_complete() use it, through a new pci_refresh_power_state()
> wrapper function.
> 
> Fixes: a0d2a959d3da (PCI: Avoid unnecessary resume after direct-complete)
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>

I also tested this on Ice Lake system and did not see any issues over
several suspend-to-ram cycles.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] PCI: PM/ACPI: Refresh all stale power state data in pci_pm_complete()
  2019-06-25 16:09 ` Mika Westerberg
@ 2019-07-05  9:48   ` Rafael J. Wysocki
  0 siblings, 0 replies; 3+ messages in thread
From: Rafael J. Wysocki @ 2019-07-05  9:48 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Linux PCI, Linux ACPI, Linux PM, LKML, Bjorn Helgaas, Lukas Wunner

On Tuesday, June 25, 2019 6:09:18 PM CEST Mika Westerberg wrote:
> On Tue, Jun 25, 2019 at 02:09:12PM +0200, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > 
> > In pci_pm_complete() there are checks to decide whether or not to
> > resume devices that were left in runtime-suspend during the preceding
> > system-wide transition into a sleep state.  They involve checking the
> > current power state of the device and comparing it with the power
> > state of it set before the preceding system-wide transition, but the
> > platform component of the device's power state is not handled
> > correctly in there.
> > 
> > Namely, on platforms with ACPI, the device power state information
> > needs to be updated with care, so that the reference counters of
> > power resources used by the device (if any) are set to ensure that
> > the refreshed power state of it will be maintained going forward.
> > 
> > To that end, introduce a new ->refresh_state() platform PM callback
> > for PCI devices, for asking the platform to refresh the device power
> > state data and ensure that the corresponding power state will be
> > maintained going forward, make it invoke acpi_device_update_power()
> > (for devices with ACPI PM) on platforms with ACPI and make
> > pci_pm_complete() use it, through a new pci_refresh_power_state()
> > wrapper function.
> > 
> > Fixes: a0d2a959d3da (PCI: Avoid unnecessary resume after direct-complete)
> > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> 
> I also tested this on Ice Lake system and did not see any issues over
> several suspend-to-ram cycles.
> 

Thanks!

Patch queued for 5.3.





^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2019-07-05  9:48 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-25 12:09 [PATCH] PCI: PM/ACPI: Refresh all stale power state data in pci_pm_complete() Rafael J. Wysocki
2019-06-25 16:09 ` Mika Westerberg
2019-07-05  9:48   ` Rafael J. Wysocki

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).