[RFC][PATCH] PM / Runtime: Add sysfs switch for disabling device run-time PM

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Rafael J. Wysocki <rjw@xxxxxxx>
Subject: PM / Runtime: Add sysfs switch for disabling device run-time PM

Add new device sysfs attribute, power/runtime, allowing the user
space to block the run-time power management of the device.  If this
attribute is set to "disabled", the driver of the device won't be
able to enable run-time power management for it (without breaking the
rules).

Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx>
---
 drivers/base/power/power.h   |    4 +
 drivers/base/power/runtime.c |  107 +++++++++++++++++++++++++++++++++----------
 drivers/base/power/sysfs.c   |   46 ++++++++++++++++++
 include/linux/pm.h           |    1 
 include/linux/pm_runtime.h   |   11 +---
 5 files changed, 139 insertions(+), 30 deletions(-)

Index: linux-2.6/drivers/base/power/power.h
===================================================================
--- linux-2.6.orig/drivers/base/power/power.h
+++ linux-2.6/drivers/base/power/power.h
@@ -2,11 +2,15 @@
 
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
+extern void pm_runtime_allow(struct device *dev);
+extern void pm_runtime_forbid(struct device *dev);
 
 #else /* !CONFIG_PM_RUNTIME */
 
 static inline void pm_runtime_init(struct device *dev) {}
 static inline void pm_runtime_remove(struct device *dev) {}
+static inline void pm_runtime_allow(struct device *dev) {}
+static inline void pm_runtime_forbid(struct device *dev) {}
 
 #endif /* !CONFIG_PM_RUNTIME */
 
Index: linux-2.6/drivers/base/power/sysfs.c
===================================================================
--- linux-2.6.orig/drivers/base/power/sysfs.c
+++ linux-2.6/drivers/base/power/sysfs.c
@@ -7,6 +7,20 @@
 #include "power.h"
 
 /*
+ *	runtime - Report/change current runtime PM setting of the device
+ *
+ *	Runtime power management of a device can be disabled with the help of
+ *	this attribute.  All devices have one of the following two values for
+ *	the power/runtime file:
+ *
+ *	 + "enabled\n" to allow the device to be power managed at run time;
+ *	 + "disabled\n" to disable this feature for the device;
+ *
+ *	The default for all devices is "enabled\n", but the runtime power
+ *	management of the device has to be enabled by its driver to be actually
+ *	used.  Changing this attribute to "disabled\n" while the device is
+ *	suspended causes it to be woken up.
+ *
  *	wakeup - Report/change current wakeup option for device
  *
  *	Some devices support "wakeup" events, which are hardware signals
@@ -59,6 +73,35 @@
 static const char enabled[] = "enabled";
 static const char disabled[] = "disabled";
 
+#ifdef CONFIG_PM_RUNTIME
+static ssize_t runtime_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	return sprintf(buf, "%s\n",
+			dev->power.runtime_forbidden ? disabled : enabled);
+}
+
+static ssize_t runtime_store(struct device * dev, struct device_attribute *attr,
+			     const char * buf, size_t n)
+{
+	char *cp;
+	int len = n;
+
+	cp = memchr(buf, '\n', n);
+	if (cp)
+		len = cp - buf;
+	if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
+		pm_runtime_allow(dev);
+	else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
+		pm_runtime_forbid(dev);
+	else
+		return -EINVAL;
+	return n;
+}
+
+static DEVICE_ATTR(runtime, 0644, runtime_show, runtime_store);
+#endif
+
 static ssize_t
 wake_show(struct device * dev, struct device_attribute *attr, char * buf)
 {
@@ -123,6 +166,9 @@ static DEVICE_ATTR(async, 0644, async_sh
 #endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */
 
 static struct attribute * power_attrs[] = {
+#ifdef CONFIG_PM_RUNTIME
+	&dev_attr_runtime.attr,
+#endif
 	&dev_attr_wakeup.attr,
 #ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG
 	&dev_attr_async.attr,
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -433,6 +433,7 @@ struct dev_pm_info {
 	unsigned int		request_pending:1;
 	unsigned int		deferred_resume:1;
 	unsigned int		run_wake:1;
+	unsigned int		runtime_forbidden:1;
 	enum rpm_request	request;
 	enum rpm_status		runtime_status;
 	int			runtime_error;
Index: linux-2.6/drivers/base/power/runtime.c
===================================================================
--- linux-2.6.orig/drivers/base/power/runtime.c
+++ linux-2.6/drivers/base/power/runtime.c
@@ -943,35 +943,26 @@ int pm_runtime_barrier(struct device *de
 EXPORT_SYMBOL_GPL(pm_runtime_barrier);
 
 /**
- * __pm_runtime_disable - Disable run-time PM of a device.
+ * __pm_runtime_disable - Disable run-time PM of a device, no locking.
  * @dev: Device to handle.
- * @check_resume: If set, check if there's a resume request for the device.
+ * @resume: If set, resume the device before disabling its run-time PM.
  *
  * Increment power.disable_depth for the device and if was zero previously,
  * cancel all pending run-time PM requests for the device and wait for all
  * operations in progress to complete.  The device can be either active or
  * suspended after its run-time PM has been disabled.
  *
- * If @check_resume is set and there's a resume request pending when
- * __pm_runtime_disable() is called and power.disable_depth is zero, the
- * function will wake up the device before disabling its run-time PM.
+ * If @resume is set, the function will wake up the device before disabling its
+ * run-time PM.
  */
-void __pm_runtime_disable(struct device *dev, bool check_resume)
+static void __pm_runtime_disable(struct device *dev, bool resume)
 {
-	spin_lock_irq(&dev->power.lock);
-
 	if (dev->power.disable_depth > 0) {
 		dev->power.disable_depth++;
-		goto out;
+		return;
 	}
 
-	/*
-	 * Wake up the device if there's a resume request pending, because that
-	 * means there probably is some I/O to process and disabling run-time PM
-	 * shouldn't prevent the device from processing the I/O.
-	 */
-	if (check_resume && dev->power.request_pending
-	    && dev->power.request == RPM_REQ_RESUME) {
+	if (resume) {
 		/*
 		 * Prevent suspends and idle notifications from being carried
 		 * out after we have woken up the device.
@@ -985,32 +976,101 @@ void __pm_runtime_disable(struct device 
 
 	if (!dev->power.disable_depth++)
 		__pm_runtime_barrier(dev);
+}
 
- out:
+/**
+ * pm_runtime_disable_resume - Disable run-time PM of a device and resume it.
+ * @dev: Device to handle.
+ *
+ * Execute __pm_raw_runtime_disable() for the device in such a way that the
+ * device will be woken up if there's a resume request pending for it.
+ */
+void pm_runtime_disable_resume(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	/*
+	 * Wake up the device if there's a resume request pending, because that
+	 * means there probably is some I/O to process and disabling run-time PM
+	 * shouldn't prevent the device from processing the I/O.
+	 */
+	__pm_runtime_disable(dev, dev->power.request_pending
+				&& dev->power.request == RPM_REQ_RESUME);
 	spin_unlock_irq(&dev->power.lock);
 }
-EXPORT_SYMBOL_GPL(__pm_runtime_disable);
+EXPORT_SYMBOL_GPL(pm_runtime_disable_resume);
 
 /**
- * pm_runtime_enable - Enable run-time PM of a device.
+ * pm_runtime_disable - Disable run-time PM of a device.
  * @dev: Device to handle.
  */
-void pm_runtime_enable(struct device *dev)
+void pm_runtime_disable(struct device *dev)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&dev->power.lock, flags);
+	spin_lock_irq(&dev->power.lock);
+	__pm_runtime_disable(dev, false);
+	spin_unlock_irq(&dev->power.lock);
+}
+EXPORT_SYMBOL_GPL(pm_runtime_disable);
 
+/**
+ * __pm_runtime_enable - Enable run-time PM of a device, no locking.
+ * @dev: Device to handle.
+ */
+static void __pm_runtime_enable(struct device *dev)
+{
 	if (dev->power.disable_depth > 0)
 		dev->power.disable_depth--;
 	else
 		dev_warn(dev, "Unbalanced %s!\n", __func__);
+}
+
+/**
+ * pm_runtime_enable - Enable run-time PM of a device.
+ * @dev: Device to handle.
+ */
+void pm_runtime_enable(struct device *dev)
+{
+	unsigned long flags;
 
+	spin_lock_irqsave(&dev->power.lock, flags);
+	__pm_runtime_enable(dev);
 	spin_unlock_irqrestore(&dev->power.lock, flags);
 }
 EXPORT_SYMBOL_GPL(pm_runtime_enable);
 
 /**
+ * pm_runtime_forbid - Block run-time PM of a device.
+ * @dev: Device to handle.
+ *
+ * Increase the device's disable count and set its power.runtime_forbidden flag,
+ * so that it's not enabled until pm_runtime_allow() is called for @dev.
+ */
+void pm_runtime_forbid(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	if (!dev->power.runtime_forbidden) {
+		dev->power.runtime_forbidden = true;
+		__pm_runtime_disable(dev, true);
+	}
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * pm_runtime_allow - Unblock run-time PM of a device.
+ * @dev: Device to handle.
+ *
+ * Decrease the device's disable count and set its power.runtime_forbidden flag.
+ */
+void pm_runtime_allow(struct device *dev)
+{
+	spin_lock_irq(&dev->power.lock);
+	if (dev->power.runtime_forbidden) {
+		dev->power.runtime_forbidden = false;
+		__pm_runtime_enable(dev);
+	}
+	spin_unlock_irq(&dev->power.lock);
+}
+
+/**
  * pm_runtime_init - Initialize run-time PM fields in given device object.
  * @dev: Device object to initialize.
  */
@@ -1023,6 +1083,7 @@ void pm_runtime_init(struct device *dev)
 
 	dev->power.disable_depth = 1;
 	atomic_set(&dev->power.usage_count, 0);
+	dev->power.runtime_forbidden = false;
 
 	dev->power.runtime_error = 0;
 
Index: linux-2.6/include/linux/pm_runtime.h
===================================================================
--- linux-2.6.orig/include/linux/pm_runtime.h
+++ linux-2.6/include/linux/pm_runtime.h
@@ -27,7 +27,8 @@ extern int __pm_runtime_put(struct devic
 extern int __pm_runtime_set_status(struct device *dev, unsigned int status);
 extern int pm_runtime_barrier(struct device *dev);
 extern void pm_runtime_enable(struct device *dev);
-extern void __pm_runtime_disable(struct device *dev, bool check_resume);
+extern void pm_runtime_disable_resume(struct device *dev);
+extern void pm_runtime_disable(struct device *dev);
 
 static inline bool pm_children_suspended(struct device *dev)
 {
@@ -77,7 +78,8 @@ static inline int __pm_runtime_set_statu
 					    unsigned int status) { return 0; }
 static inline int pm_runtime_barrier(struct device *dev) { return 0; }
 static inline void pm_runtime_enable(struct device *dev) {}
-static inline void __pm_runtime_disable(struct device *dev, bool c) {}
+static inline void pm_runtime_disable_resume(struct device *dev) {}
+static inline void pm_runtime_disable(struct device *dev) {}
 
 static inline bool pm_children_suspended(struct device *dev) { return false; }
 static inline void pm_suspend_ignore_children(struct device *dev, bool en) {}
@@ -118,9 +120,4 @@ static inline void pm_runtime_set_suspen
 	__pm_runtime_set_status(dev, RPM_SUSPENDED);
 }
 
-static inline void pm_runtime_disable(struct device *dev)
-{
-	__pm_runtime_disable(dev, true);
-}
-
 #endif
_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux