2019-10-15 01:27:49

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH v3 0/7] 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.

[1] https://lore.kernel.org/r/KU1P153MB016637CAEAD346F0AA8E3801BFAD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM


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

--
2.23.0.700.g56cf767bdb-goog


2019-10-15 01:28:01

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 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]
Link: https://lore.kernel.org/r/KU1P153MB016637CAEAD346F0AA8E3801BFAD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM
Signed-off-by: Dexuan Cui <[email protected]>
Signed-off-by: Bjorn Helgaas <[email protected]>
Cc: [email protected] # v4.13+
---
drivers/pci/pci-driver.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index a8124e47bf6e..d4ac8ce8c1f9 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -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);

--
2.23.0.700.g56cf767bdb-goog

2019-10-15 01:29:00

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 2/7] PCI/PM: Correct pci_pm_thaw_noirq() documentation

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]>
---
Documentation/power/pci.rst | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Documentation/power/pci.rst b/Documentation/power/pci.rst
index 0e2ef7429304..1525c594d631 100644
--- a/Documentation/power/pci.rst
+++ b/Documentation/power/pci.rst
@@ -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
--
2.23.0.700.g56cf767bdb-goog

2019-10-15 01:31:23

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 3/7] PCI/PM: Clear PCIe PME Status even for legacy power management

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.

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]>
---
drivers/pci/pci-driver.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d4ac8ce8c1f9..0c3086793e4e 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -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);

--
2.23.0.700.g56cf767bdb-goog

2019-10-15 01:31:56

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 4/7] PCI/PM: Run resume fixups before disabling wakeup events

From: 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.

Signed-off-by: Bjorn Helgaas <[email protected]>
---
drivers/pci/pci-driver.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 0c3086793e4e..55acb658273f 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -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);
--
2.23.0.700.g56cf767bdb-goog

2019-10-15 01:32:11

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 5/7] PCI/PM: Make power management op coding style consistent

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;
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]>
---
drivers/pci/pci-driver.c | 76 +++++++++++++++++++---------------------
1 file changed, 36 insertions(+), 40 deletions(-)

diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 55acb658273f..abbf5c39cb9c 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -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 = {
--
2.23.0.700.g56cf767bdb-goog

2019-10-15 01:33:02

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 6/7] PCI/PM: Wrap long lines in documentation

From: 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.

Signed-off-by: Bjorn Helgaas <[email protected]>
---
Documentation/power/pci.rst | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/Documentation/power/pci.rst b/Documentation/power/pci.rst
index 1525c594d631..db41a770a2f5 100644
--- a/Documentation/power/pci.rst
+++ b/Documentation/power/pci.rst
@@ -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
--
2.23.0.700.g56cf767bdb-goog

2019-10-15 01:34:58

by Bjorn Helgaas

[permalink] [raw]
Subject: [PATCH 7/7] PCI/MSI: Move power state check out of pci_msi_supported()

From: 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.

Signed-off-by: Bjorn Helgaas <[email protected]>
---
drivers/pci/msi.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 0884bedcfc7a..20e9c729617c 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -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 */
--
2.23.0.700.g56cf767bdb-goog

2019-10-15 21:18:07

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 1/7] PCI/PM: Always return devices to D0 when thawing

On Tuesday, October 15, 2019 1:00:10 AM CEST Bjorn Helgaas wrote:
> 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]
> Link: https://lore.kernel.org/r/KU1P153MB016637CAEAD346F0AA8E3801BFAD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM
> Signed-off-by: Dexuan Cui <[email protected]>
> Signed-off-by: Bjorn Helgaas <[email protected]>
> Cc: [email protected] # v4.13+

No issues found, so

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> drivers/pci/pci-driver.c | 17 +++++++++++------
> 1 file changed, 11 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
> index a8124e47bf6e..d4ac8ce8c1f9 100644
> --- a/drivers/pci/pci-driver.c
> +++ b/drivers/pci/pci-driver.c
> @@ -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);
>
>




2019-10-15 21:18:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 2/7] PCI/PM: Correct pci_pm_thaw_noirq() documentation

On Tuesday, October 15, 2019 1:00:11 AM CEST Bjorn Helgaas wrote:
> 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

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> Documentation/power/pci.rst | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/power/pci.rst b/Documentation/power/pci.rst
> index 0e2ef7429304..1525c594d631 100644
> --- a/Documentation/power/pci.rst
> +++ b/Documentation/power/pci.rst
> @@ -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
>




2019-10-15 21:19:08

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 6/7] PCI/PM: Wrap long lines in documentation

On Tuesday, October 15, 2019 1:00:15 AM CEST Bjorn Helgaas wrote:
> From: 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.
>
> Signed-off-by: Bjorn Helgaas <[email protected]>

Well, looks better this way. :-)

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> Documentation/power/pci.rst | 28 ++++++++++++++--------------
> 1 file changed, 14 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/power/pci.rst b/Documentation/power/pci.rst
> index 1525c594d631..db41a770a2f5 100644
> --- a/Documentation/power/pci.rst
> +++ b/Documentation/power/pci.rst
> @@ -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
>




2019-10-15 21:19:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 7/7] PCI/MSI: Move power state check out of pci_msi_supported()

On Tuesday, October 15, 2019 1:00:16 AM CEST Bjorn Helgaas wrote:
> From: 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.
>
> Signed-off-by: Bjorn Helgaas <[email protected]>

No issues found, so

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> drivers/pci/msi.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
> index 0884bedcfc7a..20e9c729617c 100644
> --- a/drivers/pci/msi.c
> +++ b/drivers/pci/msi.c
> @@ -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 */
>




2019-10-15 21:20:39

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 3/7] PCI/PM: Clear PCIe PME Status even for legacy power management

On Tuesday, October 15, 2019 1:00:12 AM CEST Bjorn Helgaas wrote:
> 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.
>
> 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

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> drivers/pci/pci-driver.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
> index d4ac8ce8c1f9..0c3086793e4e 100644
> --- a/drivers/pci/pci-driver.c
> +++ b/drivers/pci/pci-driver.c
> @@ -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);
>
>




2019-10-15 21:20:43

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 4/7] PCI/PM: Run resume fixups before disabling wakeup events

On Tuesday, October 15, 2019 1:00:13 AM CEST Bjorn Helgaas wrote:
> From: 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.

Right.

> 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.
>
> Signed-off-by: Bjorn Helgaas <[email protected]>

No concerns about this change, so

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> drivers/pci/pci-driver.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
> index 0c3086793e4e..55acb658273f 100644
> --- a/drivers/pci/pci-driver.c
> +++ b/drivers/pci/pci-driver.c
> @@ -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);
>




2019-10-15 21:20:57

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 5/7] PCI/PM: Make power management op coding style consistent

On Tuesday, October 15, 2019 1:00:14 AM CEST Bjorn Helgaas wrote:
> 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;
> 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

Reviewed-by: Rafael J. Wysocki <[email protected]>

> ---
> drivers/pci/pci-driver.c | 76 +++++++++++++++++++---------------------
> 1 file changed, 36 insertions(+), 40 deletions(-)
>
> diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
> index 55acb658273f..abbf5c39cb9c 100644
> --- a/drivers/pci/pci-driver.c
> +++ b/drivers/pci/pci-driver.c
> @@ -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 = {
>




2019-10-15 21:25:04

by Dexuan Cui

[permalink] [raw]
Subject: RE: [PATCH v3 0/7] PCI: PM: Move to D0 before calling pci_legacy_resume_early()

> 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:
>
> 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

2019-10-15 21:28:32

by Bjorn Helgaas

[permalink] [raw]
Subject: Re: [PATCH v3 0/7] PCI: PM: Move to D0 before calling pci_legacy_resume_early()

On Mon, Oct 14, 2019 at 06:00:09PM -0500, Bjorn Helgaas wrote:
> 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.
>
> [1] https://lore.kernel.org/r/KU1P153MB016637CAEAD346F0AA8E3801BFAD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM
>
>
> 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(-)

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.

2019-10-16 16:10:39

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH 5/7] PCI/PM: Make power management op coding style consistent

On Mon, Oct 14, 2019 at 06:00:14PM -0500, Bjorn Helgaas wrote:
> 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