[RFC][PATCH 0/3] PM: Mechanism to avoid resuming runtime-suspended devices during system suspend
第一个:
引进了power.no_suspend这个变量,设备设置这个变量可以保持在suspend状态。
在系统整体休眠的时候,子系统知道设备保持在supend状态,告诉PM改变设备的prepare回调,用来指示设备已经进入supend不需要进行resume操作。
如果prepare返回积极的一个数值,PM设置no_suspend,再后来的流程中将不会调用到设备的suspend函数。
然而,父节点通常还是需要resume,因为他们的子节点需要进行suspend。这样PM将清理no_suspend(除非父节点设置了ignore_children)。
第二个:
runtime PM加入了一个函数,在prepare时帮忙检查设备是否进入runtime suspend状态。
第三个:
实现子系统的ACPI PM
drivers/base/power/main.c | 38 ++++++++++++++++++++++++++----
include/linux/pm.h | 1 +
2 files changed, 27 insertions(+), 12 deletions(-)
Index: linux-pm/drivers/base/power/
==============================
--- linux-pm.orig/drivers/base/
+++ linux-pm/drivers/base/power/
@@ -918,7 +918,7 @@ static int device_suspend_noirq(struct d
pm_callback_t callback = NULL;
char *info = NULL;
- if (dev->power.syscore)
+ if (dev->power.syscore || dev->power.no_suspend)
return 0;
if (dev->pm_domain) {
@@ -1006,7 +1006,7 @@ static int device_suspend_late(struct de
__pm_runtime_disable(dev, false);
- if (dev->power.syscore)
+ if (dev->power.syscore || dev->power.no_suspend)
return 0;
if (dev->pm_domain) {
@@ -1143,8 +1143,10 @@ static int __device_suspend(struct devic
* for it, this is equivalent to the device signaling wakeup, so the
* system suspend operation should be aborted.
*/
- if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
+ if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
pm_wakeup_event(dev, 0);
+ dev->power.no_suspend = false;
+ }
if (pm_wakeup_pending()) {
async_error = -EBUSY;
@@ -1157,6 +1159,9 @@ static int __device_suspend(struct devic
dpm_watchdog_set(&wd, dev);
device_lock(dev);
+ if (dev->power.no_suspend)
+ goto End;
+
if (dev->pm_domain) {
info = "power domain ";
callback = pm_op(&dev->pm_domain->ops, state);
@@ -1205,9 +1210,13 @@ static int __device_suspend(struct devic
End:
if (!error) {
dev->power.is_suspended = true;
- if (dev->power.wakeup_path
- && dev->parent && !dev->parent->power.ignore_
- dev->parent->power.wakeup_path = true;
+ if (dev->parent && !dev->parent->power.ignore_
+ if (dev->power.wakeup_path)
+ dev->parent->power.wakeup_path = true;
+
+ if (!dev->power.no_suspend)
+ dev->parent->power.no_suspend = false;
+ }
}
device_unlock(dev);
@@ -1307,7 +1316,7 @@ static int device_prepare(struct device
{
int (*callback)(struct device *) = NULL;
char *info = NULL;
- int error = 0;
+ int ret = 0;
if (dev->power.syscore)
return 0;
@@ -1323,6 +1332,7 @@ static int device_prepare(struct device
device_lock(dev);
dev->power.wakeup_path = device_may_wakeup(dev);
+ dev->power.no_suspend = false;
if (dev->pm_domain) {
info = "preparing power domain ";
@@ -1344,16 +1354,20 @@ static int device_prepare(struct device
}
if (callback) {
- error = callback(dev);
- suspend_report_result(
+ ret = callback(dev);
+ suspend_report_result(
}
device_unlock(dev);
- if (error)
+ if (ret < 0) {
pm_runtime_put(dev);
+ } else if (ret > 0) {
+ dev->power.no_suspend = true;
+ ret = 0;
+ }
- return error;
+ return ret;
}
/**
@@ -1422,7 +1436,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_
void __suspend_report_result(const char *function, void *fn, int ret)
{
- if (ret)
+ if (ret < 0)
printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
}
EXPORT_SYMBOL_GPL(__suspend_
Index: linux-pm/include/linux/pm.h
==============================
--- linux-pm.orig/include/linux/
+++ linux-pm/include/linux/pm.h
@@ -544,6 +544,7 @@ struct dev_pm_info {
bool is_suspended:1; /* Ditto */
bool ignore_children:1;
bool early_init:1; /* Owned by the PM core */
+ bool no_suspend:1;
spinlock_t lock;
#ifdef CONFIG_PM_SLEEP
struct list_head entry;
---
drivers/base/power/runtime.c | 28 ++++++++++++++++++++++++++++
include/linux/pm_runtime.h | 2 ++
2 files changed, 30 insertions(+)
Index: linux-pm/include/linux/pm_
==============================
--- linux-pm.orig/include/linux/
+++ linux-pm/include/linux/pm_
@@ -53,6 +53,7 @@ extern unsigned long pm_runtime_autosusp
extern void pm_runtime_update_max_time_
s64 delta_ns);
extern void pm_runtime_set_memalloc_noio(
+extern bool pm_runtime_enabled_and_
static inline bool pm_children_suspended(struct device *dev)
{
@@ -161,6 +162,7 @@ static inline unsigned long pm_runtime_a
struct device *dev) { return 0; }
static inline void pm_runtime_set_memalloc_noio(
bool enable){}
+static inline bool pm_runtime_enabled_and_
#endif /* !CONFIG_PM_RUNTIME */
Index: linux-pm/drivers/base/power/
==============================
--- linux-pm.orig/drivers/base/
+++ linux-pm/drivers/base/power/
@@ -1194,6 +1194,34 @@ void pm_runtime_enable(struct device *de
EXPORT_SYMBOL_GPL(pm_runtime_
/**
+ * pm_runtime_enabled_and_
+ * @dev: Device to handle.
+ *
+ * This routine is to be executed during system suspend only, after
+ * device_prepare() has been executed for @dev.
+ *
+ * Return false if runtime PM is disabled for the device. Otherwise, wait
+ * for pending transitions to complete and check the runtime PM status of the
+ * device after that. Return true if it is RPM_SUSPENDED.
+ */
+bool pm_runtime_enabled_and_
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&dev->power.
+ if (dev->power.disable_depth) {
+ ret = false;
+ } else {
+ __pm_runtime_barrier(dev);
+ ret = pm_runtime_status_suspended(
+ }
+ spin_unlock_irqrestore(&dev->
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm_runtime_
+
+/**
* pm_runtime_forbid - Block runtime PM of a device.
* @dev: Device to handle.
*
--