Hi All,
This series of patches addresses a few issues related to the handling of
hibernation in the PCI bus type and the ACPI PM domain and ACPI LPSS driver.
The v2 addresses Hans' concerns regarding the LPSS changes.
First of all, all of the runtime-suspended PCI devices and devices in the ACPI PM and LPSS
PM domains will be resumed during hibernation (first patch). This appears to be the
only way to avoid weird corner cases and the benefit from avoiding to resume those
devices during hibernation is questionable.
That change allows the the hibernation callbacks in all of the involved subsystems to be
simplified (patches 2 and 3).
Moreover, reusing bus-level suspend callbacks for the "poweroff" transition during
hibernation (which is the case for the ACPI PM domain and LPSS) is incorrect, so patch 4
fixes that.
Finally, there are some leftover items in linux/acpi.h that can be dropped (patch 5).
Thanks,
Rafael
From: Rafael J. Wysocki <[email protected]>
Remove a leftover function header and a static inline stub with no
users from the ACPI header file.
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
-> v2: No changes.
---
include/linux/acpi.h | 2 --
1 file changed, 2 deletions(-)
Index: linux-pm/include/linux/acpi.h
===================================================================
--- linux-pm.orig/include/linux/acpi.h
+++ linux-pm/include/linux/acpi.h
@@ -913,7 +913,6 @@ static inline int acpi_dev_pm_attach(str
#endif
#if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP)
-int acpi_dev_suspend_late(struct device *dev);
int acpi_subsys_prepare(struct device *dev);
void acpi_subsys_complete(struct device *dev);
int acpi_subsys_suspend_late(struct device *dev);
@@ -922,7 +921,6 @@ int acpi_subsys_suspend(struct device *d
int acpi_subsys_freeze(struct device *dev);
int acpi_subsys_poweroff(struct device *dev);
#else
-static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
static inline void acpi_subsys_complete(struct device *dev) {}
static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
From: Rafael J. Wysocki <[email protected]>
Both the PCI bus type and the ACPI PM domain avoid resuming
runtime-suspended devices with DPM_FLAG_SMART_SUSPEND set during
hibernation (before creating the snapshot image of system memory),
but that turns out to be a mistake. It leads to functional issues
and adds complexity that's hard to justify.
For this reason, resume all runtime-suspended PCI devices and all
devices in the ACPI PM domains before creating a snapshot image of
system memory during hibernation.
Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
Fixes: c4b65157aeef (PCI / PM: Take SMART_SUSPEND driver flag into account)
Link: https://lore.kernel.org/linux-acpi/[email protected]/T/#maf065fe6e4974f2a9d79f332ab99dfaba635f64c
Reported-by: Robert R. Howell <[email protected]>
Tested-by: Robert R. Howell <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
-> v2: No changes.
---
drivers/acpi/device_pm.c | 13 +++++++------
drivers/pci/pci-driver.c | 16 ++++++++--------
2 files changed, 15 insertions(+), 14 deletions(-)
Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -1155,13 +1155,14 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_ear
int acpi_subsys_freeze(struct device *dev)
{
/*
- * This used to be done in acpi_subsys_prepare() for all devices and
- * some drivers may depend on it, so do it here. Ideally, however,
- * runtime-suspended devices should not be touched during freeze/thaw
- * transitions.
+ * Resume all runtime-suspended devices before creating a snapshot
+ * image of system memory, because the restore kernel generally cannot
+ * be expected to always handle them consistently and they need to be
+ * put into the runtime-active metastate during system resume anyway,
+ * so it is better to ensure that the state saved in the image will be
+ * alwyas consistent with that.
*/
- if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
- pm_runtime_resume(dev);
+ pm_runtime_resume(dev);
return pm_generic_freeze(dev);
}
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -1012,15 +1012,15 @@ static int pci_pm_freeze(struct device *
}
/*
- * This used to be done in pci_pm_prepare() for all devices and some
- * drivers may depend on it, so do it here. Ideally, runtime-suspended
- * devices should not be touched during freeze/thaw transitions,
- * however.
+ * Resume all runtime-suspended devices before creating a snapshot
+ * image of system memory, because the restore kernel generally cannot
+ * be expected to always handle them consistently and they need to be
+ * put into the runtime-active metastate during system resume anyway,
+ * so it is better to ensure that the state saved in the image will be
+ * alwyas consistent with that.
*/
- if (!dev_pm_smart_suspend_and_suspended(dev)) {
- pm_runtime_resume(dev);
- pci_dev->state_saved = false;
- }
+ pm_runtime_resume(dev);
+ pci_dev->state_saved = false;
if (pm->freeze) {
int error;
From: Rafael J. Wysocki <[email protected]>
First, after a previous change causing all runtime-suspended devices
in the ACPI PM domain (and ACPI LPSS devices) to be resumed before
creating a snapshot image of memory during hibernation, it is not
necessary to worry about the case in which them might be left in
runtime-suspend any more, so get rid of the code related to that from
ACPI PM domain and ACPI LPSS hibernation callbacks.
Second, it is not correct to use pm_generic_resume_early() and
acpi_subsys_resume_noirq() in hibernation "restore" callbacks (which
currently happens in the ACPI PM domain and ACPI LPSS), so introduce
proper _restore_late and _restore_noirq callbacks for the ACPI PM
domain and ACPI LPSS.
Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
-> v2:
* Add a comment explaining the acpi_lpss_resume_noirq() behavior.
* Make the new LPSS "restore" callbacks follow that behavior.
---
drivers/acpi/acpi_lpss.c | 61 ++++++++++++++++++++++++++++++++++++++++-------
drivers/acpi/device_pm.c | 61 ++++++-----------------------------------------
include/linux/acpi.h | 10 -------
3 files changed, 61 insertions(+), 71 deletions(-)
Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -1116,7 +1116,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_no
* acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback.
* @dev: Device to handle.
*/
-int acpi_subsys_resume_noirq(struct device *dev)
+static int acpi_subsys_resume_noirq(struct device *dev)
{
if (dev_pm_may_skip_resume(dev))
return 0;
@@ -1131,7 +1131,6 @@ int acpi_subsys_resume_noirq(struct devi
return pm_generic_resume_noirq(dev);
}
-EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq);
/**
* acpi_subsys_resume_early - Resume device using ACPI.
@@ -1141,12 +1140,11 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_noi
* generic early resume procedure for it during system transition into the
* working state.
*/
-int acpi_subsys_resume_early(struct device *dev)
+static int acpi_subsys_resume_early(struct device *dev)
{
int ret = acpi_dev_resume(dev);
return ret ? ret : pm_generic_resume_early(dev);
}
-EXPORT_SYMBOL_GPL(acpi_subsys_resume_early);
/**
* acpi_subsys_freeze - Run the device driver's freeze callback.
@@ -1169,52 +1167,15 @@ int acpi_subsys_freeze(struct device *de
EXPORT_SYMBOL_GPL(acpi_subsys_freeze);
/**
- * acpi_subsys_freeze_late - Run the device driver's "late" freeze callback.
- * @dev: Device to handle.
- */
-int acpi_subsys_freeze_late(struct device *dev)
-{
-
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
- return pm_generic_freeze_late(dev);
-}
-EXPORT_SYMBOL_GPL(acpi_subsys_freeze_late);
-
-/**
- * acpi_subsys_freeze_noirq - Run the device driver's "noirq" freeze callback.
- * @dev: Device to handle.
- */
-int acpi_subsys_freeze_noirq(struct device *dev)
-{
-
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
- return pm_generic_freeze_noirq(dev);
-}
-EXPORT_SYMBOL_GPL(acpi_subsys_freeze_noirq);
-
-/**
- * acpi_subsys_thaw_noirq - Run the device driver's "noirq" thaw callback.
- * @dev: Device to handle.
+ * acpi_subsys_restore_early - Restore device using ACPI.
+ * @dev: Device to restore.
*/
-int acpi_subsys_thaw_noirq(struct device *dev)
+int acpi_subsys_restore_early(struct device *dev)
{
- /*
- * If the device is in runtime suspend, the "thaw" code may not work
- * correctly with it, so skip the driver callback and make the PM core
- * skip all of the subsequent "thaw" callbacks for the device.
- */
- if (dev_pm_smart_suspend_and_suspended(dev)) {
- dev_pm_skip_next_resume_phases(dev);
- return 0;
- }
-
- return pm_generic_thaw_noirq(dev);
+ int ret = acpi_dev_resume(dev);
+ return ret ? ret : pm_generic_restore_early(dev);
}
-EXPORT_SYMBOL_GPL(acpi_subsys_thaw_noirq);
+EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
#endif /* CONFIG_PM_SLEEP */
static struct dev_pm_domain acpi_general_pm_domain = {
@@ -1230,14 +1191,10 @@ static struct dev_pm_domain acpi_general
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze,
- .freeze_late = acpi_subsys_freeze_late,
- .freeze_noirq = acpi_subsys_freeze_noirq,
- .thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_subsys_suspend_late,
.poweroff_noirq = acpi_subsys_suspend_noirq,
- .restore_noirq = acpi_subsys_resume_noirq,
- .restore_early = acpi_subsys_resume_early,
+ .restore_early = acpi_subsys_restore_early,
#endif
},
};
Index: linux-pm/include/linux/acpi.h
===================================================================
--- linux-pm.orig/include/linux/acpi.h
+++ linux-pm/include/linux/acpi.h
@@ -918,26 +918,16 @@ int acpi_subsys_prepare(struct device *d
void acpi_subsys_complete(struct device *dev);
int acpi_subsys_suspend_late(struct device *dev);
int acpi_subsys_suspend_noirq(struct device *dev);
-int acpi_subsys_resume_noirq(struct device *dev);
-int acpi_subsys_resume_early(struct device *dev);
int acpi_subsys_suspend(struct device *dev);
int acpi_subsys_freeze(struct device *dev);
-int acpi_subsys_freeze_late(struct device *dev);
-int acpi_subsys_freeze_noirq(struct device *dev);
-int acpi_subsys_thaw_noirq(struct device *dev);
#else
static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
static inline void acpi_subsys_complete(struct device *dev) {}
static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
-static inline int acpi_subsys_resume_noirq(struct device *dev) { return 0; }
-static inline int acpi_subsys_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
-static inline int acpi_subsys_freeze_late(struct device *dev) { return 0; }
-static inline int acpi_subsys_freeze_noirq(struct device *dev) { return 0; }
-static inline int acpi_subsys_thaw_noirq(struct device *dev) { return 0; }
#endif
#ifdef CONFIG_ACPI
Index: linux-pm/drivers/acpi/acpi_lpss.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpi_lpss.c
+++ linux-pm/drivers/acpi/acpi_lpss.c
@@ -1091,16 +1091,62 @@ static int acpi_lpss_resume_noirq(struct
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
- ret = acpi_subsys_resume_noirq(dev);
+ /* Follow acpi_subsys_resume_noirq(). */
+ if (dev_pm_may_skip_resume(dev))
+ return 0;
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ pm_runtime_set_active(dev);
+
+ ret = pm_generic_resume_noirq(dev);
if (ret)
return ret;
- if (!dev_pm_may_skip_resume(dev) && pdata->dev_desc->resume_from_noirq)
- ret = acpi_lpss_do_resume_early(dev);
+ if (!pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ /*
+ * The driver's ->resume_early callback will be invoked by
+ * acpi_lpss_do_resume_early(), with the assumption that the driver
+ * really wanted to run that code in ->resume_noirq, but it could not
+ * run before acpi_dev_resume() and the driver expected the latter to be
+ * called in the "early" phase.
+ */
+ return acpi_lpss_do_resume_early(dev);
+}
+
+static int acpi_lpss_do_restore_early(struct device *dev)
+{
+ int ret = acpi_lpss_resume(dev);
+
+ return ret ? ret : pm_generic_restore_early(dev);
+}
+
+static int acpi_lpss_restore_early(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
- return ret;
+ return acpi_lpss_do_restore_early(dev);
}
+static int acpi_lpss_restore_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
+
+ ret = pm_generic_restore_noirq(dev);
+ if (ret)
+ return ret;
+
+ if (!pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ /* This is analogous to what happens in acpi_lpss_resume_noirq(). */
+ return acpi_lpss_do_restore_early(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev)
@@ -1134,14 +1180,11 @@ static struct dev_pm_domain acpi_lpss_pm
.resume_noirq = acpi_lpss_resume_noirq,
.resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze,
- .freeze_late = acpi_subsys_freeze_late,
- .freeze_noirq = acpi_subsys_freeze_noirq,
- .thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_lpss_suspend_late,
.poweroff_noirq = acpi_lpss_suspend_noirq,
- .restore_noirq = acpi_lpss_resume_noirq,
- .restore_early = acpi_lpss_resume_early,
+ .restore_noirq = acpi_lpss_restore_noirq,
+ .restore_early = acpi_lpss_restore_early,
#endif
.runtime_suspend = acpi_lpss_runtime_suspend,
.runtime_resume = acpi_lpss_runtime_resume,
From: Rafael J. Wysocki <[email protected]>
After a previous change causing all runtime-suspended PCI devices
to be resumed before creating a snapshot image of memory during
hibernation, it is not necessary to worry about the case in which
them might be left in runtime-suspend any more, so get rid of the
code related to that from bus-level PCI hibernation callbacks.
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
-> v2: No changes.
---
drivers/pci/pci-driver.c | 27 ---------------------------
1 file changed, 27 deletions(-)
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -1034,22 +1034,11 @@ static int pci_pm_freeze(struct device *
return 0;
}
-static int pci_pm_freeze_late(struct device *dev)
-{
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
- return pm_generic_freeze_late(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;
- if (dev_pm_smart_suspend_and_suspended(dev))
- return 0;
-
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_FREEZE);
@@ -1079,16 +1068,6 @@ static int pci_pm_thaw_noirq(struct devi
struct device_driver *drv = dev->driver;
int error = 0;
- /*
- * If the device is in runtime suspend, the code below may not work
- * correctly with it, so skip that code and make the PM core skip all of
- * the subsequent "thaw" callbacks for the device.
- */
- if (dev_pm_smart_suspend_and_suspended(dev)) {
- dev_pm_skip_next_resume_phases(dev);
- return 0;
- }
-
if (pcibios_pm_ops.thaw_noirq) {
error = pcibios_pm_ops.thaw_noirq(dev);
if (error)
@@ -1226,10 +1205,6 @@ static int pci_pm_restore_noirq(struct d
struct device_driver *drv = dev->driver;
int error = 0;
- /* This is analogous to the pci_pm_resume_noirq() case. */
- if (dev_pm_smart_suspend_and_suspended(dev))
- pm_runtime_set_active(dev);
-
if (pcibios_pm_ops.restore_noirq) {
error = pcibios_pm_ops.restore_noirq(dev);
if (error)
@@ -1279,7 +1254,6 @@ static int pci_pm_restore(struct device
#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define pci_pm_freeze NULL
-#define pci_pm_freeze_late NULL
#define pci_pm_freeze_noirq NULL
#define pci_pm_thaw NULL
#define pci_pm_thaw_noirq NULL
@@ -1405,7 +1379,6 @@ static const struct dev_pm_ops pci_dev_p
.suspend_late = pci_pm_suspend_late,
.resume = pci_pm_resume,
.freeze = pci_pm_freeze,
- .freeze_late = pci_pm_freeze_late,
.thaw = pci_pm_thaw,
.poweroff = pci_pm_poweroff,
.poweroff_late = pci_pm_poweroff_late,
From: Rafael J. Wysocki <[email protected]>
In general, it is not correct to call pm_generic_suspend(),
pm_generic_suspend_late() and pm_generic_suspend_noirq() during the
hibernation's "poweroff" transition, because device drivers may
provide special callbacks to be invoked then and the wrappers in
question cause system suspend callbacks to be run. Unfortunately,
that happens in the ACPI PM domain and ACPI LPSS.
To address this potential issue, introduce "poweroff" callbacks
for the ACPI PM and LPSS that will use pm_generic_poweroff(),
pm_generic_poweroff_late() and pm_generic_poweroff_noirq() as
appropriate.
Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
-> v2:
* Make the new "poweroff" callbacks for the LPSS follow the current
acpi_lpss_suspend_late/noirq() behavior.
---
drivers/acpi/acpi_lpss.c | 50 ++++++++++++++++++++++++++++++++++++++--
drivers/acpi/device_pm.c | 58 ++++++++++++++++++++++++++++++++++++++++++++---
include/linux/acpi.h | 2 +
3 files changed, 104 insertions(+), 6 deletions(-)
Index: linux-pm/drivers/acpi/acpi_lpss.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpi_lpss.c
+++ linux-pm/drivers/acpi/acpi_lpss.c
@@ -1061,6 +1061,13 @@ static int acpi_lpss_suspend_noirq(struc
int ret;
if (pdata->dev_desc->resume_from_noirq) {
+ /*
+ * The driver's ->suspend_late callback will be invoked by
+ * acpi_lpss_do_suspend_late(), with the assumption that the
+ * driver really wanted to run that code in ->suspend_noirq, but
+ * it could not run after acpi_dev_suspend() and the driver
+ * expected the latter to be called in the "late" phase.
+ */
ret = acpi_lpss_do_suspend_late(dev);
if (ret)
return ret;
@@ -1147,6 +1154,43 @@ static int acpi_lpss_restore_noirq(struc
/* This is analogous to what happens in acpi_lpss_resume_noirq(). */
return acpi_lpss_do_restore_early(dev);
}
+
+static int acpi_lpss_do_poweroff_late(struct device *dev)
+{
+ int ret = pm_generic_poweroff_late(dev);
+
+ return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
+}
+
+static int acpi_lpss_poweroff_late(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ if (pdata->dev_desc->resume_from_noirq)
+ return 0;
+
+ return acpi_lpss_do_poweroff_late(dev);
+}
+
+static int acpi_lpss_poweroff_noirq(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ if (pdata->dev_desc->resume_from_noirq) {
+ /* This is analogous to the acpi_lpss_suspend_noirq() case. */
+ int ret = acpi_lpss_do_poweroff_late(dev);
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_poweroff_noirq(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev)
@@ -1180,9 +1224,9 @@ static struct dev_pm_domain acpi_lpss_pm
.resume_noirq = acpi_lpss_resume_noirq,
.resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze,
- .poweroff = acpi_subsys_suspend,
- .poweroff_late = acpi_lpss_suspend_late,
- .poweroff_noirq = acpi_lpss_suspend_noirq,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_lpss_poweroff_late,
+ .poweroff_noirq = acpi_lpss_poweroff_noirq,
.restore_noirq = acpi_lpss_restore_noirq,
.restore_early = acpi_lpss_restore_early,
#endif
Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -1176,6 +1176,58 @@ int acpi_subsys_restore_early(struct dev
return ret ? ret : pm_generic_restore_early(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_restore_early);
+
+/**
+ * acpi_subsys_poweroff - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Follow PCI and resume devices from runtime suspend before running their
+ * system poweroff callbacks, unless the driver can cope with runtime-suspended
+ * devices during system suspend and there are no ACPI-specific reasons for
+ * resuming them.
+ */
+int acpi_subsys_poweroff(struct device *dev)
+{
+ if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+ acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
+ pm_runtime_resume(dev);
+
+ return pm_generic_poweroff(dev);
+}
+EXPORT_SYMBOL_GPL(acpi_subsys_poweroff);
+
+/**
+ * acpi_subsys_poweroff_late - Run the device driver's poweroff callback.
+ * @dev: Device to handle.
+ *
+ * Carry out the generic late poweroff procedure for @dev and use ACPI to put
+ * it into a low-power state during system transition into a sleep state.
+ */
+static int acpi_subsys_poweroff_late(struct device *dev)
+{
+ int ret;
+
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ ret = pm_generic_poweroff_late(dev);
+ if (ret)
+ return ret;
+
+ return acpi_dev_suspend(dev, device_may_wakeup(dev));
+}
+
+/**
+ * acpi_subsys_poweroff_noirq - Run the driver's "noirq" poweroff callback.
+ * @dev: Device to suspend.
+ */
+static int acpi_subsys_poweroff_noirq(struct device *dev)
+{
+ if (dev_pm_smart_suspend_and_suspended(dev))
+ return 0;
+
+ return pm_generic_poweroff_noirq(dev);
+}
#endif /* CONFIG_PM_SLEEP */
static struct dev_pm_domain acpi_general_pm_domain = {
@@ -1191,9 +1243,9 @@ static struct dev_pm_domain acpi_general
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze,
- .poweroff = acpi_subsys_suspend,
- .poweroff_late = acpi_subsys_suspend_late,
- .poweroff_noirq = acpi_subsys_suspend_noirq,
+ .poweroff = acpi_subsys_poweroff,
+ .poweroff_late = acpi_subsys_poweroff_late,
+ .poweroff_noirq = acpi_subsys_poweroff_noirq,
.restore_early = acpi_subsys_restore_early,
#endif
},
Index: linux-pm/include/linux/acpi.h
===================================================================
--- linux-pm.orig/include/linux/acpi.h
+++ linux-pm/include/linux/acpi.h
@@ -920,6 +920,7 @@ int acpi_subsys_suspend_late(struct devi
int acpi_subsys_suspend_noirq(struct device *dev);
int acpi_subsys_suspend(struct device *dev);
int acpi_subsys_freeze(struct device *dev);
+int acpi_subsys_poweroff(struct device *dev);
#else
static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
@@ -928,6 +929,7 @@ static inline int acpi_subsys_suspend_la
static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
+static inline int acpi_subsys_poweroff(struct device *dev) { return 0; }
#endif
#ifdef CONFIG_ACPI
Hi Rafael,
On 01-07-19 12:42, Rafael J. Wysocki wrote:
> Hi All,
>
> This series of patches addresses a few issues related to the handling of
> hibernation in the PCI bus type and the ACPI PM domain and ACPI LPSS driver.
>
> The v2 addresses Hans' concerns regarding the LPSS changes.
>
> First of all, all of the runtime-suspended PCI devices and devices in the ACPI PM and LPSS
> PM domains will be resumed during hibernation (first patch). This appears to be the
> only way to avoid weird corner cases and the benefit from avoiding to resume those
> devices during hibernation is questionable.
>
> That change allows the the hibernation callbacks in all of the involved subsystems to be
> simplified (patches 2 and 3).
>
> Moreover, reusing bus-level suspend callbacks for the "poweroff" transition during
> hibernation (which is the case for the ACPI PM domain and LPSS) is incorrect, so patch 4
> fixes that.
>
> Finally, there are some leftover items in linux/acpi.h that can be dropped (patch 5).
Thank you for the new version, the entire series looks good to me now:
Reviewed-by: Hans de Goede <[email protected]>
Regards,
Hans
p.s.
FWIW I agree that the calling of the suspend_late method of the i2c-designware driver
at suspend_noirq time is ugly, thank you for adding the comment documenting this.
On Mon, Jul 01, 2019 at 12:44:25PM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Both the PCI bus type and the ACPI PM domain avoid resuming
> runtime-suspended devices with DPM_FLAG_SMART_SUSPEND set during
> hibernation (before creating the snapshot image of system memory),
> but that turns out to be a mistake. It leads to functional issues
> and adds complexity that's hard to justify.
>
> For this reason, resume all runtime-suspended PCI devices and all
> devices in the ACPI PM domains before creating a snapshot image of
> system memory during hibernation.
>
> Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
> Fixes: c4b65157aeef (PCI / PM: Take SMART_SUSPEND driver flag into account)
> Link: https://lore.kernel.org/linux-acpi/[email protected]/T/#maf065fe6e4974f2a9d79f332ab99dfaba635f64c
> Reported-by: Robert R. Howell <[email protected]>
> Tested-by: Robert R. Howell <[email protected]>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> ---
>
> -> v2: No changes.
>
> ---
> drivers/acpi/device_pm.c | 13 +++++++------
> drivers/pci/pci-driver.c | 16 ++++++++--------
> 2 files changed, 15 insertions(+), 14 deletions(-)
>
> Index: linux-pm/drivers/acpi/device_pm.c
> ===================================================================
> --- linux-pm.orig/drivers/acpi/device_pm.c
> +++ linux-pm/drivers/acpi/device_pm.c
> @@ -1155,13 +1155,14 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_ear
> int acpi_subsys_freeze(struct device *dev)
> {
> /*
> - * This used to be done in acpi_subsys_prepare() for all devices and
> - * some drivers may depend on it, so do it here. Ideally, however,
> - * runtime-suspended devices should not be touched during freeze/thaw
> - * transitions.
> + * Resume all runtime-suspended devices before creating a snapshot
> + * image of system memory, because the restore kernel generally cannot
> + * be expected to always handle them consistently and they need to be
> + * put into the runtime-active metastate during system resume anyway,
> + * so it is better to ensure that the state saved in the image will be
> + * alwyas consistent with that.
alwyas -> always
> */
> - if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
> - pm_runtime_resume(dev);
> + pm_runtime_resume(dev);
>
> return pm_generic_freeze(dev);
> }
> Index: linux-pm/drivers/pci/pci-driver.c
> ===================================================================
> --- linux-pm.orig/drivers/pci/pci-driver.c
> +++ linux-pm/drivers/pci/pci-driver.c
> @@ -1012,15 +1012,15 @@ static int pci_pm_freeze(struct device *
> }
>
> /*
> - * This used to be done in pci_pm_prepare() for all devices and some
> - * drivers may depend on it, so do it here. Ideally, runtime-suspended
> - * devices should not be touched during freeze/thaw transitions,
> - * however.
> + * Resume all runtime-suspended devices before creating a snapshot
> + * image of system memory, because the restore kernel generally cannot
> + * be expected to always handle them consistently and they need to be
> + * put into the runtime-active metastate during system resume anyway,
> + * so it is better to ensure that the state saved in the image will be
> + * alwyas consistent with that.
ditto
> */
> - if (!dev_pm_smart_suspend_and_suspended(dev)) {
> - pm_runtime_resume(dev);
> - pci_dev->state_saved = false;
> - }
> + pm_runtime_resume(dev);
> + pci_dev->state_saved = false;
>
> if (pm->freeze) {
> int error;
>
>
>
On Mon, Jul 01, 2019 at 12:42:14PM +0200, Rafael J. Wysocki wrote:
> Hi All,
>
> This series of patches addresses a few issues related to the handling of
> hibernation in the PCI bus type and the ACPI PM domain and ACPI LPSS driver.
>
> The v2 addresses Hans' concerns regarding the LPSS changes.
>
> First of all, all of the runtime-suspended PCI devices and devices in the ACPI PM and LPSS
> PM domains will be resumed during hibernation (first patch). This appears to be the
> only way to avoid weird corner cases and the benefit from avoiding to resume those
> devices during hibernation is questionable.
>
> That change allows the the hibernation callbacks in all of the involved subsystems to be
> simplified (patches 2 and 3).
>
> Moreover, reusing bus-level suspend callbacks for the "poweroff" transition during
> hibernation (which is the case for the ACPI PM domain and LPSS) is incorrect, so patch 4
> fixes that.
>
> Finally, there are some leftover items in linux/acpi.h that can be dropped (patch 5).
For the whole series,
Reviewed-by: Mika Westerberg <[email protected]>
On 7/1/19 4:44 AM, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Both the PCI bus type and the ACPI PM domain avoid resuming
> runtime-suspended devices with DPM_FLAG_SMART_SUSPEND set during
> hibernation (before creating the snapshot image of system memory),
> but that turns out to be a mistake. It leads to functional issues
> and adds complexity that's hard to justify.
>
> For this reason, resume all runtime-suspended PCI devices and all
> devices in the ACPI PM domains before creating a snapshot image of
> system memory during hibernation.
>
> Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
> Fixes: c4b65157aeef (PCI / PM: Take SMART_SUSPEND driver flag into account)
> Link: https://lore.kernel.org/linux-acpi/[email protected]/T/#maf065fe6e4974f2a9d79f332ab99dfaba635f64c
> Reported-by: Robert R. Howell <[email protected]>
> Tested-by: Robert R. Howell <[email protected]>
> Signed-off-by: Rafael J. Wysocki <[email protected]>
> ---
>
> -> v2: No changes.
I've tested your v2 patch set on my ASUS T100TA, applying just those 5 patches to the
5.2-rc7 kernel, and they do fix the hibernation problem. The i2c_designware
timeout errors are now gone and sound does now work after resume from both
suspend and hibernate.
As before I still see the "i2c_designware 80860F41:00: Transfer while suspended" error
on the first resume from either suspend or hibernate, but also as before that
particular error doesn't seem to cause a persistent problem and the system works
normally after the resume.
Bob Howell
On Mon, Jul 1, 2019 at 6:15 PM Mika Westerberg
<[email protected]> wrote:
>
> On Mon, Jul 01, 2019 at 12:44:25PM +0200, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > Both the PCI bus type and the ACPI PM domain avoid resuming
> > runtime-suspended devices with DPM_FLAG_SMART_SUSPEND set during
> > hibernation (before creating the snapshot image of system memory),
> > but that turns out to be a mistake. It leads to functional issues
> > and adds complexity that's hard to justify.
> >
> > For this reason, resume all runtime-suspended PCI devices and all
> > devices in the ACPI PM domains before creating a snapshot image of
> > system memory during hibernation.
> >
> > Fixes: 05087360fd7a (ACPI / PM: Take SMART_SUSPEND driver flag into account)
> > Fixes: c4b65157aeef (PCI / PM: Take SMART_SUSPEND driver flag into account)
> > Link: https://lore.kernel.org/linux-acpi/[email protected]/T/#maf065fe6e4974f2a9d79f332ab99dfaba635f64c
> > Reported-by: Robert R. Howell <[email protected]>
> > Tested-by: Robert R. Howell <[email protected]>
> > Signed-off-by: Rafael J. Wysocki <[email protected]>
> > ---
> >
> > -> v2: No changes.
> >
> > ---
> > drivers/acpi/device_pm.c | 13 +++++++------
> > drivers/pci/pci-driver.c | 16 ++++++++--------
> > 2 files changed, 15 insertions(+), 14 deletions(-)
> >
> > Index: linux-pm/drivers/acpi/device_pm.c
> > ===================================================================
> > --- linux-pm.orig/drivers/acpi/device_pm.c
> > +++ linux-pm/drivers/acpi/device_pm.c
> > @@ -1155,13 +1155,14 @@ EXPORT_SYMBOL_GPL(acpi_subsys_resume_ear
> > int acpi_subsys_freeze(struct device *dev)
> > {
> > /*
> > - * This used to be done in acpi_subsys_prepare() for all devices and
> > - * some drivers may depend on it, so do it here. Ideally, however,
> > - * runtime-suspended devices should not be touched during freeze/thaw
> > - * transitions.
> > + * Resume all runtime-suspended devices before creating a snapshot
> > + * image of system memory, because the restore kernel generally cannot
> > + * be expected to always handle them consistently and they need to be
> > + * put into the runtime-active metastate during system resume anyway,
> > + * so it is better to ensure that the state saved in the image will be
> > + * alwyas consistent with that.
>
> alwyas -> always
>
> > */
> > - if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
> > - pm_runtime_resume(dev);
> > + pm_runtime_resume(dev);
> >
> > return pm_generic_freeze(dev);
> > }
> > Index: linux-pm/drivers/pci/pci-driver.c
> > ===================================================================
> > --- linux-pm.orig/drivers/pci/pci-driver.c
> > +++ linux-pm/drivers/pci/pci-driver.c
> > @@ -1012,15 +1012,15 @@ static int pci_pm_freeze(struct device *
> > }
> >
> > /*
> > - * This used to be done in pci_pm_prepare() for all devices and some
> > - * drivers may depend on it, so do it here. Ideally, runtime-suspended
> > - * devices should not be touched during freeze/thaw transitions,
> > - * however.
> > + * Resume all runtime-suspended devices before creating a snapshot
> > + * image of system memory, because the restore kernel generally cannot
> > + * be expected to always handle them consistently and they need to be
> > + * put into the runtime-active metastate during system resume anyway,
> > + * so it is better to ensure that the state saved in the image will be
> > + * alwyas consistent with that.
>
> ditto
Thanks, I'll fix these up when applying the patch.
On Monday, July 1, 2019 6:20:17 PM CEST Mika Westerberg wrote:
> On Mon, Jul 01, 2019 at 12:42:14PM +0200, Rafael J. Wysocki wrote:
> > Hi All,
> >
> > This series of patches addresses a few issues related to the handling of
> > hibernation in the PCI bus type and the ACPI PM domain and ACPI LPSS driver.
> >
> > The v2 addresses Hans' concerns regarding the LPSS changes.
> >
> > First of all, all of the runtime-suspended PCI devices and devices in the ACPI PM and LPSS
> > PM domains will be resumed during hibernation (first patch). This appears to be the
> > only way to avoid weird corner cases and the benefit from avoiding to resume those
> > devices during hibernation is questionable.
> >
> > That change allows the the hibernation callbacks in all of the involved subsystems to be
> > simplified (patches 2 and 3).
> >
> > Moreover, reusing bus-level suspend callbacks for the "poweroff" transition during
> > hibernation (which is the case for the ACPI PM domain and LPSS) is incorrect, so patch 4
> > fixes that.
> >
> > Finally, there are some leftover items in linux/acpi.h that can be dropped (patch 5).
>
> For the whole series,
>
> Reviewed-by: Mika Westerberg <[email protected]>
>
Thanks!
Queued for 5.3 with the tags from you and Hans (I've fixed up comments in the first patch while applying it).