PCI: PM: Move to D0 before calling pci_legacy_resume_early()
Subsystems (5)
v3 cover PCI: PM: Move to D0 before calling pci_legacy_resume_early()
From: Bjorn Helgaas <[email protected]>
Dexuan, the important thing here is the first patch, which is your [1], which I modified by doing pci_restore_state() as well as setting to D0:
pci_set_power_state(pci_dev, PCI_D0); pci_restore_state(pci_dev);
I'm proposing some more patches on top. None are relevant to the problem you're solving; they're just minor doc and other updates in the same area.
Rafael, if you have a chance to look at these, I'd appreciate it. I tried to make the doc match the code, but I'm no PM expert.
Dexuan Cui (1):
PCI/PM: Always return devices to D0 when thawing
Bjorn Helgaas (6):
PCI/PM: Correct pci_pm_thaw_noirq() documentation PCI/PM: Clear PCIe PME Status even for legacy power management PCI/PM: Run resume fixups before disabling wakeup events PCI/PM: Make power management op coding style consistent PCI/PM: Wrap long lines in documentation
PCI/MSI: Move power state check out of pci_msi_supported()
Documentation/power/pci.rst | 38 +++++++------- drivers/pci/msi.c | 6 +-- drivers/pci/pci-driver.c | 99 ++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 72 deletions(-)
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
PATCH v3 1/7 PCI/PM: Always return devices to D0 when thawing
From: Dexuan Cui <[email protected]>
pci_pm_thaw_noirq() is supposed to return the device to D0 and restore its configuration registers, but previously it only did that for devices whose drivers implemented the new power management ops.
Hibernation, e.g., via "echo disk > /sys/power/state", involves freezing devices, creating a hibernation image, thawing devices, writing the image, and powering off. The fact that thawing did not return devices with legacy power management to D0 caused errors, e.g., in this path:
pci_pm_thaw_noirq
if (pci_has_legacy_pm_support(pci_dev)) # true for Mellanox VF driver
return pci_legacy_resume_early(dev) # ... legacy PM skips the rest
pci_set_power_state(pci_dev, PCI_D0) pci_restore_state(pci_dev)
pci_pm_thaw
if (pci_has_legacy_pm_support(pci_dev))
pci_legacy_resume
drv->resume
mlx4_resume
...
pci_enable_msix_range
...
if (dev->current_state != PCI_D0) # <---
return -EINVAL;which caused these warnings:
mlx4_core a6d1:00:02.0: INTx is not supported in multi-function mode, aborting
PM: dpm_run_callback(): pci_pm_thaw+0x0/0xd7 returns -95
PM: Device a6d1:00:02.0 failed to thaw: error -95
Return devices to D0 and restore config registers for all devices, not just those whose drivers support new power management.
[bhelgaas: also call pci_restore_state() before pci_legacy_resume_early(), update comment, add stable tag, commit log]
--- drivers/pci/pci-driver.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
drivers/pci/pci-driver.c +11 -6
@@ -1076,17 +1076,22 @@ static int pci_pm_thaw_noirq(struct device *dev)return error; } -if (pci_has_legacy_pm_support (pci_dev)) -return pci_legacy_resume_early (dev); -/* -* pci_restore_state() requires the device to be in D0 (because of MSI -* restoration among other things), so force it into D0 in case the -* driver's "freeze" callbacks put it into a low-power state directly. +* Both the legacy ->resume_early() and the new pm->thaw_noirq() +* callbacks assume the device has been returned to D0 and its +* config state has been restored. +* +* In addition, pci_restore_state() restores MSI-X state in MMIO +* space, which requires the device to be in D0, so return it to D0 +* in case the driver's "freeze" callbacks put it into a low-power +* state. */ pci_set_power_state (pci_dev,PCI_D0 );pci_restore_state (pci_dev); +if (pci_has_legacy_pm_support (pci_dev)) +return pci_legacy_resume_early (dev); +if (drv && drv->pm && drv->pm->thaw_noirq) error = drv->pm->thaw_noirq (dev);
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 1/7] PCI/PM: Always return devices to D0 when thawing
Quoted 36 lines · CEST Bjorn Helgaas
From: Dexuan Cui <[email protected]>
pci_pm_thaw_noirq()is supposed to return the device to D0 and restore its configuration registers, but previously it only did that for devices whose drivers implemented the new power management ops.Hibernation, e.g., via "echo disk >
/sys/power/state", involves freezing devices, creating a hibernation image, thawing devices, writing the image, and powering off. The fact that thawing did not return devices with legacy power management to D0 caused errors, e.g., in this path:
pci_pm_thaw_noirq
if (pci_has_legacy_pm_support(pci_dev)) # true for Mellanox VF driver
returnpci_legacy_resume_early(dev)# ... legacy PM skips the rest
pci_set_power_state(pci_dev, PCI_D0)pci_restore_state(pci_dev)
pci_pm_thawif (pci_has_legacy_pm_support(pci_dev)) pci_legacy_resume drv->resume mlx4_resume ... pci_enable_msix_range ... if (dev->current_state != PCI_D0) # <--- return -EINVAL;which caused these warnings:
mlx4_corea6d1:00:02.0: INTx is not supported in multi-function mode, aborting
PM:dpm_run_callback():pci_pm_thaw+0x0/0xd7returns -95
PM: Device a6d1:00:02.0 failed to thaw: error -95Return devices to D0 and restore config registers for all devices, not just those whose drivers support new power management.
Quoted 6 lines
[bhelgaas: also call
pci_restore_state()beforepci_legacy_resume_early(), update comment, add stable tag, commit log]
No issues found, so
Quoted 38 lines
--- drivers/pci/pci-driver.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)drivers/pci/pci-driver.c +11
-6@@ -1076,17 +1076,22 @@ static int pci_pm_thaw_noirq(struct device *dev)return error; } -if (pci_has_legacy_pm_support (pci_dev)) -return pci_legacy_resume_early (dev); -/* -* pci_restore_state() requires the device to be in D0 (because of MSI -* restoration among other things), so force it into D0 in case the -* driver's "freeze" callbacks put it into a low-power state directly. +* Both the legacy ->resume_early() and the new pm->thaw_noirq() +* callbacks assume the device has been returned to D0 and its +* config state has been restored. +* +* In addition, pci_restore_state() restores MSI-X state in MMIO +* space, which requires the device to be in D0, so return it to D0 +* in case the driver's "freeze" callbacks put it into a low-power +* state. */ pci_set_power_state (pci_dev,PCI_D0 );pci_restore_state (pci_dev); +if (pci_has_legacy_pm_support (pci_dev)) +return pci_legacy_resume_early (dev); +if (drv && drv->pm && drv->pm->thaw_noirq) error = drv->pm->thaw_noirq (dev);
PATCH v3 2/7 PCI/PM: Correct pci_pm_thaw_noirq() documentation
In reply to Bjorn HelgaasFrom: Bjorn Helgaas <[email protected]>
According to the documentation, pci_pm_thaw_noirq() did not put the device into the full-power state and restore its standard configuration registers. This is incorrect, so update the documentation to match the code.
--- Documentation/power/pci.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
Documentation/power/pci.rst +5 -5
@@ -600,17 +600,17 @@ using the following PCI bus type's callbacks:: respectively. -The first of them, pci_pm_thaw_noirq(), is analogous to pci_pm_resume_noirq(), -but it doesn't put the device into the full power state and doesn't attempt to -restore its standard configuration registers. It also executes the device -driver's pm->thaw_noirq() callback, if defined, instead of pm->resume_noirq(). +The first of them, pci_pm_thaw_noirq(), is analogous to pci_pm_resume_noirq(). +It puts the device into the full power state and restores its standard +configuration registers. It also executes the device driver's pm->thaw_noirq() +callback, if defined, instead of pm->resume_noirq(). The pci_pm_thaw() routine is similar to pci_pm_resume(), but it runs the device driver's pm->thaw() callback instead of pm->resume(). It is executed asynchronously for different PCI devices that don't depend on each other in a known way. -The complete phase it the same as for system resume. +The complete phase is the same as for system resume. After saving the image, devices need to be powered down before the system can enter the target sleep state (ACPI S4 for ACPI-based systems). This is done in
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 2/7] PCI/PM: Correct pci_pm_thaw_noirq() documentation
Quoted 7 lines · CEST Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
According to the documentation,
pci_pm_thaw_noirq()did not put the device into the full-power state and restore its standard configuration registers. This is incorrect, so update the documentation to match the code.Signed-off-by Bjorn Helgaas <[email protected]>
Right, the documentation is outdated, so
Quoted 32 lines
--- Documentation/power/pci.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)Documentation/power/pci.rst +5
-5@@ -600,17 +600,17 @@ using the following PCI bus type's callbacks:: respectively. -The first of them, pci_pm_thaw_noirq(), is analogous to pci_pm_resume_noirq(), -but it doesn't put the device into the full power state and doesn't attempt to -restore its standard configuration registers. It also executes the device -driver's pm->thaw_noirq() callback, if defined, instead of pm->resume_noirq(). +The first of them, pci_pm_thaw_noirq(), is analogous to pci_pm_resume_noirq(). +It puts the device into the full power state and restores its standard +configuration registers. It also executes the device driver's pm->thaw_noirq() +callback, if defined, instead of pm->resume_noirq(). The pci_pm_thaw() routine is similar to pci_pm_resume(), but it runs the device driver's pm->thaw() callback instead of pm->resume(). It is executed asynchronously for different PCI devices that don't depend on each other in a known way. -The complete phase it the same as for system resume. +The complete phase is the same as for system resume. After saving the image, devices need to be powered down before the system can enter the target sleep state (ACPI S4 for ACPI-based systems). This is done in
PATCH v3 3/7 PCI/PM: Clear PCIe PME Status even for legacy power management
In reply to Bjorn HelgaasFrom: Bjorn Helgaas <[email protected]>
Previously, pci_pm_resume_noirq() cleared the PME Status bit in the Root Status register only if the device had no driver or the driver did not implement legacy power management. It should clear PME Status regardless of what sort of power management the driver supports, so do this before checking for legacy power management.
This affects Root Ports and Root Complex Event Collectors, for which the usual driver is the PCIe portdrv, which implements new power management, so this change is just on principle, not to fix any actual defects.
--- drivers/pci/pci-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/pci/pci-driver.c +1 -2
@@ -941,12 +941,11 @@ static int pci_pm_resume_noirq(struct device *dev)pci_pm_default_resume_early (pci_dev);pci_fixup_device (pci_fixup_resume_early, pci_dev); +pcie_pme_root_status_cleanup (pci_dev);if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -pcie_pme_root_status_cleanup (pci_dev); -if (drv && drv->pm && drv->pm->resume_noirq) error = drv->pm->resume_noirq (dev);
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 3/7] PCI/PM: Clear PCIe PME Status even for legacy power management
Quoted 8 lines · CEST Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
Previously,
pci_pm_resume_noirq()cleared the PME Status bit in the Root Status register only if the device had no driver or the driver did not implement legacy power management. It should clear PME Status regardless of what sort of power management the driver supports, so do this before checking for legacy power management.
Quoted 6 lines
This affects Root Ports and Root Complex Event Collectors, for which the usual driver is the PCIe portdrv, which implements new power management, so this change is just on principle, not to fix any actual defects.
Fixes a39bd851dccf ("PCI/PM: Clear PCIe PME Status bit in core, not PCIe port driver")Signed-off-by Bjorn Helgaas <[email protected]>
This is a reasonable change in my view, so
Quoted 23 lines
--- drivers/pci/pci-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)drivers/pci/pci-driver.c +1
-2@@ -941,12 +941,11 @@ static int pci_pm_resume_noirq(struct device *dev)pci_pm_default_resume_early (pci_dev);pci_fixup_device (pci_fixup_resume_early, pci_dev); +pcie_pme_root_status_cleanup (pci_dev);if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -pcie_pme_root_status_cleanup (pci_dev); -if (drv && drv->pm && drv->pm->resume_noirq) error = drv->pm->resume_noirq (dev);
PATCH v3 4/7 PCI/PM: Run resume fixups before disabling wakeup events
In reply to Bjorn HelgaasFrom: Bjorn Helgaas <[email protected]>
pci_pm_resume() and pci_pm_restore() call pci_pm_default_resume(), which runs resume fixups before disabling wakeup events:
static void pci_pm_default_resume(struct pci_dev *pci_dev)
{
pci_fixup_device(pci_fixup_resume, pci_dev);
pci_enable_wake(pci_dev, PCI_D0, false);
}pci_pm_runtime_resume() does both of these, but in the opposite order:
pci_enable_wake(pci_dev, PCI_D0, false); pci_fixup_device(pci_fixup_resume, pci_dev);
We should always use the same ordering unless there's a reason to do otherwise. Change pci_pm_runtime_resume() to call pci_pm_default_resume() instead of open-coding this, so the fixups are always done before disabling wakeup events.
--- drivers/pci/pci-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/pci/pci-driver.c +1 -2
@@ -1345,8 +1345,7 @@ static int pci_pm_runtime_resume(struct device *dev)return 0 ;pci_fixup_device (pci_fixup_resume_early, pci_dev); -pci_enable_wake (pci_dev,PCI_D0 , false); -pci_fixup_device (pci_fixup_resume, pci_dev); +pci_pm_default_resume (pci_dev);if (pm && pm->runtime_resume) rc = pm->runtime_resume (dev);
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 4/7] PCI/PM: Run resume fixups before disabling wakeup events
Quoted 11 lines · CEST Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
pci_pm_resume()andpci_pm_restore()callpci_pm_default_resume(), which runs resume fixups before disabling wakeup events:static void pci_pm_default_resume(struct pci_dev *pci_dev) { pci_fixup_device(pci_fixup_resume, pci_dev); pci_enable_wake(pci_dev, PCI_D0, false); }
Quoted 7 lines
pci_pm_runtime_resume()does both of these, but in the opposite order:pci_enable_wake(pci_dev, PCI_D0, false); pci_fixup_device(pci_fixup_resume, pci_dev);We should always use the same ordering unless there's a reason to do otherwise.
Right.
Quoted 5 lines
Change
pci_pm_runtime_resume()to callpci_pm_default_resume()instead of open-coding this, so the fixups are always done before disabling wakeup events.Signed-off-by Bjorn Helgaas <[email protected]>
No concerns about this change, so
Quoted 19 lines
--- drivers/pci/pci-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)drivers/pci/pci-driver.c +1
-2@@ -1345,8 +1345,7 @@ static int pci_pm_runtime_resume(struct device *dev)return 0 ;pci_fixup_device (pci_fixup_resume_early, pci_dev); -pci_enable_wake (pci_dev,PCI_D0 , false); -pci_fixup_device (pci_fixup_resume, pci_dev); +pci_pm_default_resume (pci_dev);if (pm && pm->runtime_resume) rc = pm->runtime_resume (dev);
PATCH v3 5/7 PCI/PM: Make power management op coding style consistent
In reply to Bjorn HelgaasFrom: Bjorn Helgaas <[email protected]>
Some of the power management ops use this style:
struct device_driver *drv = dev->driver;
if (drv && drv->pm && drv->pm->prepare(dev))
drv->pm->prepare(dev);while others use this:
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (pm && pm->runtime_resume)
pm->runtime_resume(dev);Convert the first style to the second so they're all consistent. Remove local "error" variables when unnecessary. No functional change intended.
--- drivers/pci/pci-driver.c | 76 +++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 40 deletions(-)
drivers/pci/pci-driver.c +36 -40
@@ -679,11 +679,11 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)static int pci_pm_prepare (struct device *dev) { -struct device_driver *drv = dev->driver;struct pci_dev *pci_dev =to_pci_dev (dev); +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -if (drv && drv->pm && drv->pm->prepare) { -int error = drv->pm->prepare (dev); +if (pm && pm->prepare) { +int error = pm->prepare (dev);if (error <0 )return error;
@@ -917,8 +917,7 @@ static int pci_pm_suspend_noirq(struct device *dev)static int pci_pm_resume_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; -int error =0 ; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ;if (dev_pm_may_skip_resume (dev))return 0 ;
@@ -946,17 +945,16 @@ static int pci_pm_resume_noirq(struct device *dev)if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -if (drv && drv->pm && drv->pm->resume_noirq) - error = drv->pm->resume_noirq (dev); +if (pm && pm->resume_noirq) +return pm->resume_noirq (dev); -return error; +return 0 ; }static int pci_pm_resume (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -int error =0 ;/* * This is necessary for the suspend error path in which resume is
@@ -972,12 +970,12 @@ static int pci_pm_resume(struct device *dev)if (pm) {if (pm->resume) - error = pm->resume (dev); +return pm->resume (dev); }else {pci_pm_reenable_device (pci_dev); } - return error; + return 0; }#else /* !CONFIG_SUSPEND */
@@ -1038,16 +1036,16 @@ static int pci_pm_freeze(struct device *dev)static int pci_pm_freeze_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ;if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_suspend_late (dev,PMSG_FREEZE ); -if (drv && drv->pm && drv->pm->freeze_noirq) { +if (pm && pm->freeze_noirq) {int error; - error = drv->pm->freeze_noirq (dev); -suspend_report_result (drv->pm->freeze_noirq, error); + error = pm->freeze_noirq (dev); +suspend_report_result (pm->freeze_noirq, error);if (error)return error; }
@@ -1066,8 +1064,8 @@ static int pci_pm_freeze_noirq(struct device *dev)static int pci_pm_thaw_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; -int error =0 ; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; +int error;if (pcibios_pm_ops.thaw_noirq) { error = pcibios_pm_ops.thaw_noirq (dev);
@@ -1091,10 +1089,10 @@ static int pci_pm_thaw_noirq(struct device *dev)if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -if (drv && drv->pm && drv->pm->thaw_noirq) - error = drv->pm->thaw_noirq (dev); +if (pm && pm->thaw_noirq) +return pm->thaw_noirq (dev); -return error; +return 0 ; }static int pci_pm_thaw (struct device *dev)
@@ -1165,24 +1163,24 @@ static int pci_pm_poweroff_late(struct device *dev)static int pci_pm_poweroff_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ;if (dev_pm_smart_suspend_and_suspended (dev))return 0 ; -if (pci_has_legacy_pm_support (to_pci_dev (dev))) +if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_suspend_late (dev,PMSG_HIBERNATE ); -if (!drv || !drv->pm) { +if (!pm) {pci_fixup_device (pci_fixup_suspend_late, pci_dev);return 0 ; } -if (drv->pm->poweroff_noirq) { +if (pm->poweroff_noirq) {int error; - error = drv->pm->poweroff_noirq (dev); -suspend_report_result (drv->pm->poweroff_noirq, error); + error = pm->poweroff_noirq (dev); +suspend_report_result (pm->poweroff_noirq, error);if (error)return error; }
@@ -1208,8 +1206,8 @@ static int pci_pm_poweroff_noirq(struct device *dev)static int pci_pm_restore_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; -int error =0 ; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; +int error;if (pcibios_pm_ops.restore_noirq) { error = pcibios_pm_ops.restore_noirq (dev);
@@ -1223,17 +1221,16 @@ static int pci_pm_restore_noirq(struct device *dev)if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -if (drv && drv->pm && drv->pm->restore_noirq) - error = drv->pm->restore_noirq (dev); +if (pm && pm->restore_noirq) +return pm->restore_noirq (dev); -return error; +return 0 ; }static int pci_pm_restore (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -int error =0 ;/* * This is necessary for the hibernation error path in which restore is
@@ -1249,12 +1246,12 @@ static int pci_pm_restore(struct device *dev)if (pm) {if (pm->restore) - error = pm->restore (dev); +return pm->restore (dev); }else {pci_pm_reenable_device (pci_dev); } - return error; + return 0; }#else /* !CONFIG_HIBERNATE_CALLBACKS */
@@ -1330,9 +1327,9 @@ static int pci_pm_runtime_suspend(struct device *dev)static int pci_pm_runtime_resume (struct device *dev) { -int rc =0 ;struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; +int error =0 ;/* * Restoring config space is necessary even if the device is not bound
@@ -1348,18 +1345,17 @@ static int pci_pm_runtime_resume(struct device *dev)pci_pm_default_resume (pci_dev);if (pm && pm->runtime_resume) - rc = pm->runtime_resume (dev); + error = pm->runtime_resume (dev); pci_dev->runtime_d3cold = false; -return rc; +return error; }static int pci_pm_runtime_idle (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -int ret =0 ;/* * If pci_dev->driver is not set (unbound), the device should
@@ -1372,9 +1368,9 @@ static int pci_pm_runtime_idle(struct device *dev)return -ENOSYS ;if (pm->runtime_idle) - ret = pm->runtime_idle (dev); +return pm->runtime_idle (dev); -return ret; +return 0 ; }static const struct dev_pm_ops pci_dev_pm_ops = {
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 5/7] PCI/PM: Make power management op coding style consistent
Quoted 10 lines · CEST Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
Some of the power management ops use this style:
struct device_driver *drv = dev->driver; if (drv && drv->pm && drv->pm->prepare(dev)) drv->pm->prepare(dev);while others use this:
Quoted 8 lines
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pm && pm->runtime_resume) pm->runtime_resume(dev);Convert the first style to the second so they're all consistent. Remove local "error" variables when unnecessary. No functional change intended.
Signed-off-by Bjorn Helgaas <[email protected]>
No issues found, so
Quoted 238 lines
--- drivers/pci/pci-driver.c | 76 +++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 40 deletions(-)drivers/pci/pci-driver.c +36
-40@@ -679,11 +679,11 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)static int pci_pm_prepare (struct device *dev) { -struct device_driver *drv = dev->driver;struct pci_dev *pci_dev =to_pci_dev (dev); +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -if (drv && drv->pm && drv->pm->prepare) { -int error = drv->pm->prepare (dev); +if (pm && pm->prepare) { +int error = pm->prepare (dev);if (error <0 )return error;@@ -917,8 +917,7 @@ static int pci_pm_suspend_noirq(struct device *dev)static int pci_pm_resume_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; -int error =0 ; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ;if (dev_pm_may_skip_resume (dev))return 0 ;@@ -946,17 +945,16 @@ static int pci_pm_resume_noirq(struct device *dev)if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -if (drv && drv->pm && drv->pm->resume_noirq) - error = drv->pm->resume_noirq (dev); +if (pm && pm->resume_noirq) +return pm->resume_noirq (dev); -return error; +return 0 ; }static int pci_pm_resume (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -int error =0 ;/* * This is necessary for the suspend error path in which resume is @@ -972,12 +970,12 @@ static int pci_pm_resume(struct device *dev)if (pm) {if (pm->resume) - error = pm->resume (dev); +return pm->resume (dev); }else {pci_pm_reenable_device (pci_dev); } - return error; + return 0; }#else /* !CONFIG_SUSPEND */ @@ -1038,16 +1036,16 @@ static int pci_pm_freeze(struct device *dev)static int pci_pm_freeze_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ;if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_suspend_late (dev,PMSG_FREEZE ); -if (drv && drv->pm && drv->pm->freeze_noirq) { +if (pm && pm->freeze_noirq) {int error; - error = drv->pm->freeze_noirq (dev); -suspend_report_result (drv->pm->freeze_noirq, error); + error = pm->freeze_noirq (dev); +suspend_report_result (pm->freeze_noirq, error);if (error)return error; }@@ -1066,8 +1064,8 @@ static int pci_pm_freeze_noirq(struct device *dev)static int pci_pm_thaw_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; -int error =0 ; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; +int error;if (pcibios_pm_ops.thaw_noirq) { error = pcibios_pm_ops.thaw_noirq (dev);@@ -1091,10 +1089,10 @@ static int pci_pm_thaw_noirq(struct device *dev)if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -if (drv && drv->pm && drv->pm->thaw_noirq) - error = drv->pm->thaw_noirq (dev); +if (pm && pm->thaw_noirq) +return pm->thaw_noirq (dev); -return error; +return 0 ; }static int pci_pm_thaw (struct device *dev)@@ -1165,24 +1163,24 @@ static int pci_pm_poweroff_late(struct device *dev)static int pci_pm_poweroff_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ;if (dev_pm_smart_suspend_and_suspended (dev))return 0 ; -if (pci_has_legacy_pm_support (to_pci_dev (dev))) +if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_suspend_late (dev,PMSG_HIBERNATE ); -if (!drv || !drv->pm) { +if (!pm) {pci_fixup_device (pci_fixup_suspend_late, pci_dev);return 0 ; } -if (drv->pm->poweroff_noirq) { +if (pm->poweroff_noirq) {int error; - error = drv->pm->poweroff_noirq (dev); -suspend_report_result (drv->pm->poweroff_noirq, error); + error = pm->poweroff_noirq (dev); +suspend_report_result (pm->poweroff_noirq, error);if (error)return error; }@@ -1208,8 +1206,8 @@ static int pci_pm_poweroff_noirq(struct device *dev)static int pci_pm_restore_noirq (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev); -struct device_driver *drv = dev->driver; -int error =0 ; +const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; +int error;if (pcibios_pm_ops.restore_noirq) { error = pcibios_pm_ops.restore_noirq (dev);@@ -1223,17 +1221,16 @@ static int pci_pm_restore_noirq(struct device *dev)if (pci_has_legacy_pm_support (pci_dev))return pci_legacy_resume_early (dev); -if (drv && drv->pm && drv->pm->restore_noirq) - error = drv->pm->restore_noirq (dev); +if (pm && pm->restore_noirq) +return pm->restore_noirq (dev); -return error; +return 0 ; }static int pci_pm_restore (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -int error =0 ;/* * This is necessary for the hibernation error path in which restore is @@ -1249,12 +1246,12 @@ static int pci_pm_restore(struct device *dev)if (pm) {if (pm->restore) - error = pm->restore (dev); +return pm->restore (dev); }else {pci_pm_reenable_device (pci_dev); } - return error; + return 0; }#else /* !CONFIG_HIBERNATE_CALLBACKS */ @@ -1330,9 +1327,9 @@ static int pci_pm_runtime_suspend(struct device *dev)static int pci_pm_runtime_resume (struct device *dev) { -int rc =0 ;struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; +int error =0 ;/* * Restoring config space is necessary even if the device is not bound @@ -1348,18 +1345,17 @@ static int pci_pm_runtime_resume(struct device *dev)pci_pm_default_resume (pci_dev);if (pm && pm->runtime_resume) - rc = pm->runtime_resume (dev); + error = pm->runtime_resume (dev); pci_dev->runtime_d3cold = false; -return rc; +return error; }static int pci_pm_runtime_idle (struct device *dev) {struct pci_dev *pci_dev =to_pci_dev (dev);const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm :NULL ; -int ret =0 ;/* * If pci_dev->driver is not set (unbound), the device should @@ -1372,9 +1368,9 @@ static int pci_pm_runtime_idle(struct device *dev)return -ENOSYS ;if (pm->runtime_idle) - ret = pm->runtime_idle (dev); +return pm->runtime_idle (dev); -return ret; +return 0 ; }static const struct dev_pm_ops pci_dev_pm_ops = {
Re: [PATCH 5/7] PCI/PM: Make power management op coding style consistent
In reply to Bjorn HelgaasQuoted 11 lines · Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
Some of the power management ops use this style:
struct device_driver *drv = dev->driver; if (drv && drv->pm && drv->pm->prepare(dev)) drv->pm->prepare(dev);while others use this:
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
I like this patch a lot, especially the direct returns. But it occurs to me that in the future this conditional would look better as
const struct dev_pm_ops *pm = driver_to_pm(dev->driver);
or something.
regards,
dan carpenter
PATCH v3 6/7 PCI/PM: Wrap long lines in documentation
In reply to Bjorn HelgaasFrom: Bjorn Helgaas <[email protected]>
Documentation/power/pci.rst is wrapped to fit in 80 columns, but directory structure changes made a few lines longer. Wrap them so they all fit in 80 columns again.
--- Documentation/power/pci.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
Documentation/power/pci.rst +14 -14
@@ -426,12 +426,12 @@ pm->runtime_idle() callback. 2.4. System-Wide Power Transitions ---------------------------------- There are a few different types of system-wide power transitions, described in -Documentation/driver-api/pm/devices.rst. Each of them requires devices to be handled -in a specific way and the PM core executes subsystem-level power management -callbacks for this purpose. They are executed in phases such that each phase -involves executing the same subsystem-level callback for every device belonging -to the given subsystem before the next phase begins. These phases always run -after tasks have been frozen. +Documentation/driver-api/pm/devices.rst. Each of them requires devices to be +handled in a specific way and the PM core executes subsystem-level power +management callbacks for this purpose. They are executed in phases such that +each phase involves executing the same subsystem-level callback for every device +belonging to the given subsystem before the next phase begins. These phases +always run after tasks have been frozen. 2.4.1. System Suspend ^^^^^^^^^^^^^^^^^^^^^
@@ -636,12 +636,12 @@ System restore requires a hibernation image to be loaded into memory and the pre-hibernation memory contents to be restored before the pre-hibernation system activity can be resumed. -As described in Documentation/driver-api/pm/devices.rst, the hibernation image is loaded -into memory by a fresh instance of the kernel, called the boot kernel, which in -turn is loaded and run by a boot loader in the usual way. After the boot kernel -has loaded the image, it needs to replace its own code and data with the code -and data of the "hibernated" kernel stored within the image, called the image -kernel. For this purpose all devices are frozen just like before creating +As described in Documentation/driver-api/pm/devices.rst, the hibernation image +is loaded into memory by a fresh instance of the kernel, called the boot kernel, +which in turn is loaded and run by a boot loader in the usual way. After the +boot kernel has loaded the image, it needs to replace its own code and data with +the code and data of the "hibernated" kernel stored within the image, called the +image kernel. For this purpose all devices are frozen just like before creating the image during hibernation, in the prepare, freeze, freeze_noirq
@@ -691,8 +691,8 @@ controlling the runtime power management of their devices. At the time of this writing there are two ways to define power management callbacks for a PCI device driver, the recommended one, based on using a -dev_pm_ops structure described in Documentation/driver-api/pm/devices.rst, and the -"legacy" one, in which the .suspend(), .suspend_late(), .resume_early(), and +dev_pm_ops structure described in Documentation/driver-api/pm/devices.rst, and +the "legacy" one, in which the .suspend(), .suspend_late(), .resume_early(), and .resume() callbacks from struct pci_driver are used. The legacy approach, however, doesn't allow one to define runtime power management callbacks and is not really suitable for any new drivers. Therefore it is not covered by this
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 6/7] PCI/PM: Wrap long lines in documentation
Quoted 7 lines · CEST Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
Documentation/power/pci.rstis wrapped to fit in 80 columns, but directory structure changes made a few lines longer. Wrap them so they all fit in 80 columns again.Signed-off-by Bjorn Helgaas <[email protected]>
Well, looks better this way. :-)
Quoted 58 lines
--- Documentation/power/pci.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)Documentation/power/pci.rst +14
-14@@ -426,12 +426,12 @@ pm->runtime_idle() callback. 2.4. System-Wide Power Transitions ---------------------------------- There are a few different types of system-wide power transitions, described in -Documentation/driver-api/pm/devices.rst. Each of them requires devices to be handled -in a specific way and the PM core executes subsystem-level power management -callbacks for this purpose. They are executed in phases such that each phase -involves executing the same subsystem-level callback for every device belonging -to the given subsystem before the next phase begins. These phases always run -after tasks have been frozen. +Documentation/driver-api/pm/devices.rst. Each of them requires devices to be +handled in a specific way and the PM core executes subsystem-level power +management callbacks for this purpose. They are executed in phases such that +each phase involves executing the same subsystem-level callback for every device +belonging to the given subsystem before the next phase begins. These phases +always run after tasks have been frozen. 2.4.1. System Suspend ^^^^^^^^^^^^^^^^^^^^^@@ -636,12 +636,12 @@ System restore requires a hibernation image to be loaded into memory and the pre-hibernation memory contents to be restored before the pre-hibernation system activity can be resumed. -As described in Documentation/driver-api/pm/devices.rst, the hibernation image is loaded -into memory by a fresh instance of the kernel, called the boot kernel, which in -turn is loaded and run by a boot loader in the usual way. After the boot kernel -has loaded the image, it needs to replace its own code and data with the code -and data of the "hibernated" kernel stored within the image, called the image -kernel. For this purpose all devices are frozen just like before creating +As described in Documentation/driver-api/pm/devices.rst, the hibernation image +is loaded into memory by a fresh instance of the kernel, called the boot kernel, +which in turn is loaded and run by a boot loader in the usual way. After the +boot kernel has loaded the image, it needs to replace its own code and data with +the code and data of the "hibernated" kernel stored within the image, called the +image kernel. For this purpose all devices are frozen just like before creating the image during hibernation, in the prepare, freeze, freeze_noirq@@ -691,8 +691,8 @@ controlling the runtime power management of their devices. At the time of this writing there are two ways to define power management callbacks for a PCI device driver, the recommended one, based on using a -dev_pm_ops structure described in Documentation/driver-api/pm/devices.rst, and the -"legacy" one, in which the .suspend(), .suspend_late(), .resume_early(), and +dev_pm_ops structure described in Documentation/driver-api/pm/devices.rst, and +the "legacy" one, in which the .suspend(), .suspend_late(), .resume_early(), and .resume() callbacks from struct pci_driver are used. The legacy approach, however, doesn't allow one to define runtime power management callbacks and is not really suitable for any new drivers. Therefore it is not covered by this
PATCH v3 7/7 PCI/MSI: Move power state check out of pci_msi_supported()
In reply to Bjorn HelgaasFrom: Bjorn Helgaas <[email protected]>
27e20603c54b ("PCI/MSI: Move D0 check into pci_msi_check_device()") moved the power state check into pci_msi_check_device(), which was subsequently renamed to pci_msi_supported(). This didn't change the behavior, since both callers checked the power state.
However, it doesn't fit the current "pci_msi_supported()" name, which should return what the device is capable of, independent of the power state.
Move the power state check back into the callers for readability. No functional change intended.
--- drivers/pci/msi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/pci/msi.c +3 -3
@@ -861,7 +861,7 @@ static int pci_msi_supported(struct pci_dev *dev, int nvec)if (!pci_msi_enable)return 0 ; -if (!dev || dev->no_msi || dev->current_state !=PCI_D0 ) +if (!dev || dev->no_msi)return 0 ;/*
@@ -972,7 +972,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,int nr_entries;int i, j; -if (!pci_msi_supported (dev, nvec)) +if (!pci_msi_supported (dev, nvec) || dev->current_state !=PCI_D0 )return -EINVAL ; nr_entries =pci_msix_vec_count (dev);
@@ -1058,7 +1058,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,int nvec;int rc; -if (!pci_msi_supported (dev, minvec)) +if (!pci_msi_supported (dev, minvec) || dev->current_state !=PCI_D0 )return -EINVAL ;/* Check whether driver already requested MSI-X IRQs */
Signature · 1 line
2.23.0.700.g56cf767bdb-goog
Re: [PATCH 7/7] PCI/MSI: Move power state check out of pci_msi_supported()
Quoted 7 lines · CEST Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
27e20603c54b("PCI/MSI: Move D0 check intopci_msi_check_device()") moved the power state check intopci_msi_check_device(), which was subsequently renamed topci_msi_supported(). This didn't change the behavior, since both callers checked the power state.
Quoted 8 lines
However, it doesn't fit the current "
pci_msi_supported()" name, which should return what the device is capable of, independent of the power state.Move the power state check back into the callers for readability. No functional change intended.
Signed-off-by Bjorn Helgaas <[email protected]>
No issues found, so
Quoted 36 lines
--- drivers/pci/msi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)drivers/pci/msi.c +3
-3@@ -861,7 +861,7 @@ static int pci_msi_supported(struct pci_dev *dev, int nvec)if (!pci_msi_enable)return 0 ; -if (!dev || dev->no_msi || dev->current_state !=PCI_D0 ) +if (!dev || dev->no_msi)return 0 ;/* @@ -972,7 +972,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,int nr_entries;int i, j; -if (!pci_msi_supported (dev, nvec)) +if (!pci_msi_supported (dev, nvec) || dev->current_state !=PCI_D0 )return -EINVAL ; nr_entries =pci_msix_vec_count (dev);@@ -1058,7 +1058,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,int nvec;int rc; -if (!pci_msi_supported (dev, minvec)) +if (!pci_msi_supported (dev, minvec) || dev->current_state !=PCI_D0 )return -EINVAL ;/* Check whether driver already requested MSI-X IRQs */
Dexuan Cui
maintainerQuoted 7 lines
From: Bjorn Helgaas <[email protected]>
Sent: Monday, October 14, 2019 4:00 PM
...Dexuan, the important thing here is the first patch, which is your [1], which I modified by doing
pci_restore_state()as well as setting to D0:
Quoted 8 lines
pci_set_power_state(pci_dev, PCI_D0); pci_restore_state(pci_dev);I'm proposing some more patches on top. None are relevant to the problem you're solving; they're just minor doc and other updates in the same area.
Rafael, if you have a chance to look at these, I'd appreciate it. I tried to make the doc match the code, but I'm no PM expert.
Thank you very much, Bjorn! The patchset looks good to me.
Thanks,
-- Dexuan
Bjorn Helgaas
AppliedQuoted 28 lines · Bjorn Helgaas
From: Bjorn Helgaas <[email protected]>
Dexuan, the important thing here is the first patch, which is your [1], which I modified by doing
pci_restore_state()as well as setting to D0:pci_set_power_state(pci_dev, PCI_D0); pci_restore_state(pci_dev);I'm proposing some more patches on top. None are relevant to the problem you're solving; they're just minor doc and other updates in the same area.
Rafael, if you have a chance to look at these, I'd appreciate it. I tried to make the doc match the code, but I'm no PM expert.
Dexuan Cui (1):
PCI/PM: Always return devices to D0 when thawingBjorn Helgaas (6):
PCI/PM: Correctpci_pm_thaw_noirq()documentation PCI/PM: Clear PCIe PME Status even for legacy power management PCI/PM: Run resume fixups before disabling wakeup events PCI/PM: Make power management op coding style consistent PCI/PM: Wrap long lines in documentation
PCI/MSI: Move power state check out ofpci_msi_supported()
Quoted 4 lines
Documentation/power/pci.rst | 38 +++++++------- drivers/pci/msi.c | 6 +-- drivers/pci/pci-driver.c | 99 ++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 72 deletions(-)
Thanks Dexuan and Rafael for taking a look at these!
I applied the first six to pci/pm and the last to pci/msi, all for v5.5.