Skip to contentSkip to conversation

PM: Introduce core framework for run-time PM of I/O devices (rev. 11)

PATCH RESEND Superseded Rafael J. Wysocki 3 participants 39 messages Aug 3, 2009 – Aug 9, 2009

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH RESEND 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 11)

Hi,

OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 11)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Special thanks to Alan Stern for his help with the design and to Magnus Damm for testing feedback.

---
 Documentation/power/runtime_pm.txt |  340 +++++++++++++
 drivers/base/dd.c                  |    7 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   20 
 drivers/base/power/power.h         |   11 
 drivers/base/power/runtime.c       |  946 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  102 +++
 include/linux/pm_runtime.h         |  111 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1558 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
Index: linux-2.6/kernel/power/Kconfig
===================================================================
@@ -208,3 +208,17 @@ config APM_EMULATION
 	  random kernel OOPSes or reboots that don't seem to be related to
 	  anything, try disabling/enabling this option (or disabling/enabling
 	  APM in your BIOS).
+
+config PM_RUNTIME
+	bool "Run-time PM core functionality"
+	depends on PM
+	---help---
+	  Enable functionality allowing I/O devices to be put into energy-saving
+	  (low power) states at run time (or autosuspended) after a specified
+	  period of inactivity and woken up in response to a hardware-generated
+	  wake-up event or a driver's request.
+
+	  Hardware support is generally required for this functionality to work
+	  and the bus type drivers of the buses the devices are on are
+	  responsible for the actual handling of the autosuspend requests and
+	  wake-up events.
kernel/power/main.c +17 -0
Index: linux-2.6/kernel/power/main.c
===================================================================
@@ -11,6 +11,7 @@
 #include <linux/kobject.h>
 #include <linux/string.h>
 #include <linux/resume-trace.h>
+#include <linux/workqueue.h>
 
 #include "power.h"
 
@@ -217,8 +218,24 @@ static struct attribute_group attr_group
 	.attrs = g,
 };
 
+#ifdef CONFIG_PM_RUNTIME
+struct workqueue_struct *pm_wq;
+
+static int __init pm_start_workqueue(void)
+{
+	pm_wq = create_freezeable_workqueue("pm");
+
+	return pm_wq ? 0 : -ENOMEM;
+}
+#else
+static inline int pm_start_workqueue(void) { return 0; }
+#endif
+
 static int __init pm_init(void)
 {
+	int error = pm_start_workqueue();
+	if (error)
+		return error;
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
include/linux/pm.h +99 -3

131 lines not on this page · raw message

drivers/base/power/Makefile +1 -0
Index: linux-2.6/drivers/base/power/Makefile
===================================================================
@@ -1,5 +1,6 @@
 obj-$(CONFIG_PM)	+= sysfs.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o
+obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +946 -0

946 lines not on this page · raw message

include/linux/pm_runtime.h new file +111 -0

111 lines not on this page · raw message

drivers/base/power/main.c +19 -1
Index: linux-2.6/drivers/base/power/main.c
===================================================================
@@ -21,6 +21,7 @@
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/resume-trace.h>
 #include <linux/rwsem.h>
 #include <linux/interrupt.h>
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object to initialize.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,8 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+
+	pm_runtime_remove(dev);
 }
 
 /**
@@ -512,6 +525,7 @@ static void dpm_complete(pm_message_t st
 			mutex_unlock(&dpm_list_mtx);
 
 			device_complete(dev, state);
+			pm_runtime_enable(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;
+		else
+			error = device_prepare(dev, state);
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
 			dev->power.status = DPM_ON;
+			pm_runtime_enable(dev);
 			if (error == -EAGAIN) {
 				put_device(dev);
 				error = 0;
drivers/base/dd.c +7 -0
Index: linux-2.6/drivers/base/dd.c
===================================================================
@@ -23,6 +23,7 @@
 #include <linux/kthread.h>
 #include <linux/wait.h>
 #include <linux/async.h>
+#include <linux/pm_runtime.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
 
 	return ret;
 }
@@ -306,6 +309,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +329,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }
 
drivers/base/power/power.h +4 -7
Index: linux-2.6/drivers/base/power/power.h
===================================================================
@@ -1,8 +1,3 @@
-static inline void device_pm_init(struct device *dev)
-{
-	dev->power.status = DPM_ON;
-}
-
 #ifdef CONFIG_PM_SLEEP
 
 /*
@@ -16,14 +11,16 @@ static inline struct device *to_device(s
 	return container_of(entry, struct device, power.entry);
 }
 
+extern void device_pm_init(struct device *dev);
 extern void device_pm_add(struct device *);
 extern void device_pm_remove(struct device *);
 extern void device_pm_move_before(struct device *, struct device *);
 extern void device_pm_move_after(struct device *, struct device *);
 extern void device_pm_move_last(struct device *);
 
-#else /* CONFIG_PM_SLEEP */
+#else /* !CONFIG_PM_SLEEP */
 
+static inline void device_pm_init(struct device *dev) {}
 static inline void device_pm_add(struct device *dev) {}
 static inline void device_pm_remove(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
@@ -32,7 +29,7 @@ static inline void device_pm_move_after(
 					struct device *devb) {}
 static inline void device_pm_move_last(struct device *dev) {}
 
-#endif
+#endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM
 
Documentation/power/runtime_pm.txt new file +340 -0

340 lines not on this page · raw message

#1 lore raw
Quoted 6 lines · Rafael J. Wysocki

Hi,

OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.

I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...

One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.

pm_runtime_disable() gets used for several different purposes. For the usage in pm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?

In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.

drivers/base/power/runtime.c line 39

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static void pm_runtime_cancel_pending(struct device *dev)
+{
+	pm_runtime_deactivate_timer(dev);
+	/*
+	 * If there's a request pending, make sure its work function will return
+	 * without doing anything.
+	 */
+	if (dev->power.request_pending)
+		dev->power.request = RPM_REQ_NONE;

No need for the "if"; you can always do the assignment.

drivers/base/power/runtime.c line 53

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_runtime_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;

Instead of assigning to retval, you could simply return these values.

drivers/base/power/runtime.c line 61

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+	else if (dev->power.idle_notification)
+		retval = -EINPROGRESS;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0
+	    || dev->power.runtime_status == RPM_SUSPENDED
+	    || dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EAGAIN;

Do we also want to rule out RPM_RESUMING? That is,

	    || dev->power.runtime_status != RPM_ACTIVE

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails. The same thing can be done in other places.

Quoted 3 lines

+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called via pm_wq.

Fix spelling of "function". Likewise in __pm_runtime_resume.

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+{

...

drivers/base/power/runtime.c line 218

Quoted 7 lines
drivers/base/power/runtime.c new file +7 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (parent && !parent->power.ignore_children)
+		pm_request_idle(parent);
+
+	if (notify)
+		pm_runtime_idle(dev);

Move this up before the spin_unlock_irq and call __pm_runtime_idle instead. The same sort of thing can be done in __pm_runtime_resume.

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_request_suspend(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		return -EINVAL;

drivers/base/power/runtime.c line 480

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+
+	if (dev->power.runtime_status == RPM_SUSPENDED)
+		retval = 1;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0)
+		retval = -EAGAIN;
+	else if (dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EINPROGRESS;
+	else if (!pm_children_suspended(dev))
+		retval = -EBUSY;

Insert:

	if (retval)
		return retval;

Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)

drivers/base/power/runtime.c line 493

Quoted 12 lines
drivers/base/power/runtime.c new file +12 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/*
+		 * Pending resume requests take precedence over us, but we can
+		 * overtake any other pending request.
+		 */
+		if (dev->power.request == RPM_REQ_RESUME)
+			retval = -EAGAIN;
+		else if (dev->power.request != RPM_REQ_SUSPEND)
+			dev->power.request = retval ?
+						RPM_REQ_NONE : RPM_REQ_SUSPEND;

Now there's no need to check retval.

drivers/base/power/runtime.c line 496

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+
+		if (dev->power.request == RPM_REQ_SUSPEND)
+			return 0;

Just simply:

		return retval;

Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.

drivers/base/power/runtime.c line 65

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	}
+
+	if (retval)
+		return retval;

Now this isn't needed. Similar code rearrangements can be made in __pm_request_resume.

Quoted 1 line

+int __pm_request_resume(struct device *dev)

Should be static.

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Quoted 34 lines
drivers/base/power/runtime.c new file +34 -0
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;
+
+	if (dev->power.runtime_status == status)
+		goto out_clear;
+
+	if (status == RPM_SUSPENDED) {
+		/* It always is possible to set the status to 'suspended'. */
+		if (parent)
+			atomic_add_unless(&parent->power.child_count, -1, 0);
+		dev->power.runtime_status = status;
+		goto out_clear;
+	}
+
+	if (parent) {
+		spin_lock_irq(&parent->power.lock);
+
+		/*
+		 * It is invalid to put an active child under a parent that is
+		 * not active, has run-time PM enabled and the
+		 * 'power.ignore_children' flag unset.
+		 */
+		if (!parent->power.disable_depth
+		    && !parent->power.ignore_children
+		    && parent->power.runtime_status != RPM_ACTIVE) {
+			error = -EBUSY;
+		} else {
+			if (dev->power.runtime_status == RPM_SUSPENDED)
+				atomic_inc(&parent->power.child_count);
+			dev->power.runtime_status = status;
+		}
+

drivers/base/power/runtime.c line 755

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+		spin_unlock_irq(&parent->power.lock);
+
+		if (error)
+			goto out;
+	} else {
+		dev->power.runtime_status = status;
+	}
+
+ out_clear:
+	dev->power.runtime_failure = false;

Move all those assignments to dev->power.runtime_status down here.

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

drivers/base/power/runtime.c line 842

Quoted 13 lines
drivers/base/power/runtime.c new file +13 -0
+
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		dev->power.request = RPM_REQ_NONE;
+
+		spin_unlock_irq(&dev->power.lock);
+
+		cancel_work_sync(&dev->power.work);
+
+		spin_lock_irq(&dev->power.lock);
+
+		dev->power.request_pending = false;

Remove excessive whitespace.

Quoted 33 lines
drivers/base/power/runtime.c new file +33 -0
+	}
+
+	if (dev->power.runtime_status == RPM_SUSPENDING
+	    || dev->power.runtime_status == RPM_RESUMING) {
+		DEFINE_WAIT(wait);
+
+		/* Suspend or wake-up in progress. */
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (dev->power.runtime_status != RPM_SUSPENDING
+			    && dev->power.runtime_status != RPM_RESUMING)
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}
+
+	if (dev->power.runtime_failure)
+		goto out;
+
+	if (dev->power.idle_notification) {
+		DEFINE_WAIT(wait);
+
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (!dev->power.idle_notification)

drivers/base/power/runtime.c line 885

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}

This wait loop should be merged with the previous loop.

drivers/base/power/runtime.c line 909

Quoted 12 lines
drivers/base/power/runtime.c new file +12 -0
+void pm_runtime_init(struct device *dev)
+{
+	spin_lock_init(&dev->power.lock);
+
+	dev->power.runtime_status = RPM_SUSPENDED;
+	dev->power.idle_notification = false;
+
+	dev->power.disable_depth = 1;
+	atomic_set(&dev->power.usage_count, 0);
+
+	dev->power.runtime_failure = false;
+	dev->power.last_error = 0;

You don't have to set values to 0; they are initialized by kzalloc.

drivers/base/power/runtime.c line 922

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	dev->power.suspend_timer.expires = jiffies;
+	dev->power.suspend_timer.data = (unsigned long)dev;
+	dev->power.suspend_timer.function = pm_suspend_timer_fn;

Use setup_timer() instead of assigning these fields directly.

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+

drivers/base/power/runtime.c line 944

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

include/linux/pm_runtime.h line 20

Quoted 5 lines
include/linux/pm_runtime.h new file +2 -0
 
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(struct device *dev);

I don't like seeing these two functions included in the public header file. It's enough to put them in drivers/base/power/power.h.

Quoted 20 lines
drivers/base/power/main.c +10 -0
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object to initialize.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)

drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121

Quoted 6 lines
drivers/base/power/main.c +2 -0
@@ -105,6 +116,8 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+
+	pm_runtime_remove(dev);
 }

Calling pm_runtime_init() from device_pm_init() and pm_runtime_remove() from device_pm_remove() isn't good. If CONFIG_PM_SLEEP isn't enabled then the calls won't be compiled, even if CONFIG_PM_RUNTIME is set.

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 9 lines
drivers/base/dd.c +2 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend. Instead we should do something like this:

	/* Wait for runtime PM calls to finish and prevent new calls
	 * until the probe is done.
	 */
	pm_runtime_disable(dev);
	pm_runtime_get_noresume(dev);
	pm_runtime_enable(dev):
	ret = really_probe(dev, drv);
	pm_runtime_put_sync(dev);

Documentation/power/runtime_pm.txt

Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3 -0
 
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

Documentation/power/runtime_pm.txt line 148

Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+Additionally, the helper functions provided by the PM core obey the following
+rules:
+
+  * If ->runtime_suspend() is about to be executed or the execution of it is
+    scheduled or there's a pending request to execute it, ->runtime_idle() will
+    not be executed for the same device.

Shouldn't we allow runtime_idle when a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.

Documentation/power/runtime_pm.txt line 266

Quoted 9 lines

+4. Run-time PM Device Helper Functions

Documentation/power/runtime_pm.txt new file +7 -0
+
+The following run-time PM helper functions are defined in
+drivers/base/power/runtime.c and include/linux/pm_runtime.h:
 
+  int pm_schedule_suspend(struct device *dev, unsigned int delay);
+    - schedule the execution of ->runtime_suspend() for the device's bus type
+      in future, where 'delay' is the time to wait before queuing up a suspend
+      work item in pm_wq, in miliseconds (if 'delay' is zero, the work item is

Fix spelling of "milli". Explain that the new delay will override the old one if a suspend was already scheduled and not yet expired.

Documentation/power/runtime_pm.txt line 285

Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2 -0
+  int pm_runtime_get_sync(struct device *dev);
+    - increment the device's usage counter, run pm_rutime_resume(dev) and return

Fix spelling of "runtime". Same under pm_runtime_put_sync.

Alan Stern

#2 lore raw
In reply to Rafael J. Wysocki
Quoted 6 lines · Rafael J. Wysocki

Hi,

OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.

I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...

One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.

pm_runtime_disable() gets used for several different purposes. For the usage in pm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?

In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.

drivers/base/power/runtime.c line 39

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static void pm_runtime_cancel_pending(struct device *dev)
+{
+	pm_runtime_deactivate_timer(dev);
+	/*
+	 * If there's a request pending, make sure its work function will return
+	 * without doing anything.
+	 */
+	if (dev->power.request_pending)
+		dev->power.request = RPM_REQ_NONE;

No need for the "if"; you can always do the assignment.

drivers/base/power/runtime.c line 53

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_runtime_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;

Instead of assigning to retval, you could simply return these values.

drivers/base/power/runtime.c line 61

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+	else if (dev->power.idle_notification)
+		retval = -EINPROGRESS;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0
+	    || dev->power.runtime_status == RPM_SUSPENDED
+	    || dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EAGAIN;

Do we also want to rule out RPM_RESUMING? That is,

	    || dev->power.runtime_status != RPM_ACTIVE

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails. The same thing can be done in other places.

Quoted 3 lines

+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called via pm_wq.

Fix spelling of "function". Likewise in __pm_runtime_resume.

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+{

...

drivers/base/power/runtime.c line 218

Quoted 7 lines
drivers/base/power/runtime.c new file +7 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (parent && !parent->power.ignore_children)
+		pm_request_idle(parent);
+
+	if (notify)
+		pm_runtime_idle(dev);

Move this up before the spin_unlock_irq and call __pm_runtime_idle instead. The same sort of thing can be done in __pm_runtime_resume.

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_request_suspend(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		return -EINVAL;

drivers/base/power/runtime.c line 480

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+
+	if (dev->power.runtime_status == RPM_SUSPENDED)
+		retval = 1;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0)
+		retval = -EAGAIN;
+	else if (dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EINPROGRESS;
+	else if (!pm_children_suspended(dev))
+		retval = -EBUSY;

Insert:

	if (retval)
		return retval;

Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)

drivers/base/power/runtime.c line 493

Quoted 12 lines
drivers/base/power/runtime.c new file +12 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/*
+		 * Pending resume requests take precedence over us, but we can
+		 * overtake any other pending request.
+		 */
+		if (dev->power.request == RPM_REQ_RESUME)
+			retval = -EAGAIN;
+		else if (dev->power.request != RPM_REQ_SUSPEND)
+			dev->power.request = retval ?
+						RPM_REQ_NONE : RPM_REQ_SUSPEND;

Now there's no need to check retval.

drivers/base/power/runtime.c line 496

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+
+		if (dev->power.request == RPM_REQ_SUSPEND)
+			return 0;

Just simply:

		return retval;

Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.

drivers/base/power/runtime.c line 65

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	}
+
+	if (retval)
+		return retval;

Now this isn't needed. Similar code rearrangements can be made in __pm_request_resume.

Quoted 1 line

+int __pm_request_resume(struct device *dev)

Should be static.

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Quoted 34 lines
drivers/base/power/runtime.c new file +34 -0
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;
+
+	if (dev->power.runtime_status == status)
+		goto out_clear;
+
+	if (status == RPM_SUSPENDED) {
+		/* It always is possible to set the status to 'suspended'. */
+		if (parent)
+			atomic_add_unless(&parent->power.child_count, -1, 0);
+		dev->power.runtime_status = status;
+		goto out_clear;
+	}
+
+	if (parent) {
+		spin_lock_irq(&parent->power.lock);
+
+		/*
+		 * It is invalid to put an active child under a parent that is
+		 * not active, has run-time PM enabled and the
+		 * 'power.ignore_children' flag unset.
+		 */
+		if (!parent->power.disable_depth
+		    && !parent->power.ignore_children
+		    && parent->power.runtime_status != RPM_ACTIVE) {
+			error = -EBUSY;
+		} else {
+			if (dev->power.runtime_status == RPM_SUSPENDED)
+				atomic_inc(&parent->power.child_count);
+			dev->power.runtime_status = status;
+		}
+

drivers/base/power/runtime.c line 755

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+		spin_unlock_irq(&parent->power.lock);
+
+		if (error)
+			goto out;
+	} else {
+		dev->power.runtime_status = status;
+	}
+
+ out_clear:
+	dev->power.runtime_failure = false;

Move all those assignments to dev->power.runtime_status down here.

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

drivers/base/power/runtime.c line 842

Quoted 13 lines
drivers/base/power/runtime.c new file +13 -0
+
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		dev->power.request = RPM_REQ_NONE;
+
+		spin_unlock_irq(&dev->power.lock);
+
+		cancel_work_sync(&dev->power.work);
+
+		spin_lock_irq(&dev->power.lock);
+
+		dev->power.request_pending = false;

Remove excessive whitespace.

Quoted 33 lines
drivers/base/power/runtime.c new file +33 -0
+	}
+
+	if (dev->power.runtime_status == RPM_SUSPENDING
+	    || dev->power.runtime_status == RPM_RESUMING) {
+		DEFINE_WAIT(wait);
+
+		/* Suspend or wake-up in progress. */
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (dev->power.runtime_status != RPM_SUSPENDING
+			    && dev->power.runtime_status != RPM_RESUMING)
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}
+
+	if (dev->power.runtime_failure)
+		goto out;
+
+	if (dev->power.idle_notification) {
+		DEFINE_WAIT(wait);
+
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (!dev->power.idle_notification)

drivers/base/power/runtime.c line 885

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}

This wait loop should be merged with the previous loop.

drivers/base/power/runtime.c line 909

Quoted 12 lines
drivers/base/power/runtime.c new file +12 -0
+void pm_runtime_init(struct device *dev)
+{
+	spin_lock_init(&dev->power.lock);
+
+	dev->power.runtime_status = RPM_SUSPENDED;
+	dev->power.idle_notification = false;
+
+	dev->power.disable_depth = 1;
+	atomic_set(&dev->power.usage_count, 0);
+
+	dev->power.runtime_failure = false;
+	dev->power.last_error = 0;

You don't have to set values to 0; they are initialized by kzalloc.

drivers/base/power/runtime.c line 922

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	dev->power.suspend_timer.expires = jiffies;
+	dev->power.suspend_timer.data = (unsigned long)dev;
+	dev->power.suspend_timer.function = pm_suspend_timer_fn;

Use setup_timer() instead of assigning these fields directly.

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+

drivers/base/power/runtime.c line 944

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

include/linux/pm_runtime.h line 20

Quoted 5 lines
include/linux/pm_runtime.h new file +2 -0
 
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(struct device *dev);

I don't like seeing these two functions included in the public header file. It's enough to put them in drivers/base/power/power.h.

Quoted 20 lines
drivers/base/power/main.c +10 -0
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object to initialize.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)

drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121

Quoted 6 lines
drivers/base/power/main.c +2 -0
@@ -105,6 +116,8 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+
+	pm_runtime_remove(dev);
 }

Calling pm_runtime_init() from device_pm_init() and pm_runtime_remove() from device_pm_remove() isn't good. If CONFIG_PM_SLEEP isn't enabled then the calls won't be compiled, even if CONFIG_PM_RUNTIME is set.

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 9 lines
drivers/base/dd.c +2 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend. Instead we should do something like this:

	/* Wait for runtime PM calls to finish and prevent new calls
	 * until the probe is done.
	 */
	pm_runtime_disable(dev);
	pm_runtime_get_noresume(dev);
	pm_runtime_enable(dev):
	ret = really_probe(dev, drv);
	pm_runtime_put_sync(dev);

Documentation/power/runtime_pm.txt

Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3 -0
 
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

Documentation/power/runtime_pm.txt line 148

Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+Additionally, the helper functions provided by the PM core obey the following
+rules:
+
+  * If ->runtime_suspend() is about to be executed or the execution of it is
+    scheduled or there's a pending request to execute it, ->runtime_idle() will
+    not be executed for the same device.

Shouldn't we allow runtime_idle when a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.

Documentation/power/runtime_pm.txt line 266

Quoted 9 lines

+4. Run-time PM Device Helper Functions

Documentation/power/runtime_pm.txt new file +7 -0
+
+The following run-time PM helper functions are defined in
+drivers/base/power/runtime.c and include/linux/pm_runtime.h:
 
+  int pm_schedule_suspend(struct device *dev, unsigned int delay);
+    - schedule the execution of ->runtime_suspend() for the device's bus type
+      in future, where 'delay' is the time to wait before queuing up a suspend
+      work item in pm_wq, in miliseconds (if 'delay' is zero, the work item is

Fix spelling of "milli". Explain that the new delay will override the old one if a suspend was already scheduled and not yet expired.

Documentation/power/runtime_pm.txt line 285

Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2 -0
+  int pm_runtime_get_sync(struct device *dev);
+    - increment the device's usage counter, run pm_rutime_resume(dev) and return

Fix spelling of "runtime". Same under pm_runtime_put_sync.

Alan Stern

#3 lore raw
Quoted 11 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki

Hi,

OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.

I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...

No problem.

Quoted 8 lines

One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.

pm_runtime_disable() gets used for several different purposes. For the usage in pm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?

Yes, we can do that.

Quoted 3 lines

In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.

OK, I'll try to put something like this in there.

Quoted 11 lines

drivers/base/power/runtime.c line 39

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static void pm_runtime_cancel_pending(struct device *dev)
+{
+	pm_runtime_deactivate_timer(dev);
+	/*
+	 * If there's a request pending, make sure its work function will return
+	 * without doing anything.
+	 */
+	if (dev->power.request_pending)
+		dev->power.request = RPM_REQ_NONE;

No need for the "if"; you can always do the assignment.

OK

Quoted 8 lines

drivers/base/power/runtime.c line 53

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_runtime_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;

Instead of assigning to retval, you could simply return these values.

I could, but I chose not to.

Quoted 11 lines

drivers/base/power/runtime.c line 61

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+	else if (dev->power.idle_notification)
+		retval = -EINPROGRESS;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0
+	    || dev->power.runtime_status == RPM_SUSPENDED
+	    || dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EAGAIN;

Do we also want to rule out RPM_RESUMING? That is,

Quoted 1 line
	    || dev->power.runtime_status != RPM_ACTIVE

Yes, we do, thanks.

Quoted 9 lines

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.

Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.

Quoted 1 line

The same thing can be done in other places.

I'm not really sure it can.

Quoted 5 lines
Quoted 3 lines

+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called via pm_wq.

Fix spelling of "function". Likewise in __pm_runtime_resume.

Will do, thanks.

Quoted 13 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+{

...

drivers/base/power/runtime.c line 218

Quoted 7 lines
drivers/base/power/runtime.c new file +7 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (parent && !parent->power.ignore_children)
+		pm_request_idle(parent);
+
+	if (notify)
+		pm_runtime_idle(dev);

Move this up before the spin_unlock_irq and call __pm_runtime_idle instead. The same sort of thing can be done in __pm_runtime_resume.

OK

Quoted 18 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_request_suspend(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		return -EINVAL;

drivers/base/power/runtime.c line 480

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+
+	if (dev->power.runtime_status == RPM_SUSPENDED)
+		retval = 1;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0)
+		retval = -EAGAIN;
+	else if (dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EINPROGRESS;
+	else if (!pm_children_suspended(dev))
+		retval = -EBUSY;

Insert:

Quoted 8 lines
	if (retval)
		return retval;

Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)

However, if it's going to return 1, it should override existing idle-notify and suspend requests. I'll add

if (retval < 0)
return retval;

Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/*
+		 * Pending resume requests take precedence over us, but we can
+		 * overtake any other pending request.
Quoted 8 lines

drivers/base/power/runtime.c line 493

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 */
+		if (dev->power.request == RPM_REQ_RESUME)
+			retval = -EAGAIN;
+		else if (dev->power.request != RPM_REQ_SUSPEND)
+			dev->power.request = retval ?
+						RPM_REQ_NONE : RPM_REQ_SUSPEND;

Now there's no need to check retval.

It is. If retval is 1, we cancel pending idle-notify and suspend requests.

Quoted 6 lines

drivers/base/power/runtime.c line 496

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+
+		if (dev->power.request == RPM_REQ_SUSPEND)
+			return 0;

Just simply:

		return retval;

OK, I'll change that.

Quoted 11 lines

Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.

drivers/base/power/runtime.c line 65

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	}
+
+	if (retval)
+		return retval;

Now this isn't needed. Similar code rearrangements can be made in __pm_request_resume.

OK

Quoted 3 lines
Quoted 1 line

+int __pm_request_resume(struct device *dev)

Should be static.

OK

Quoted 10 lines

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.

Quoted 38 lines
Quoted 38 lines
drivers/base/power/runtime.c new file +38 -0
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;
+
+	if (dev->power.runtime_status == status)
+		goto out_clear;
+
+	if (status == RPM_SUSPENDED) {
+		/* It always is possible to set the status to 'suspended'. */
+		if (parent)
+			atomic_add_unless(&parent->power.child_count, -1, 0);
+		dev->power.runtime_status = status;
+		goto out_clear;
+	}
+
+	if (parent) {
+		spin_lock_irq(&parent->power.lock);
+
+		/*
+		 * It is invalid to put an active child under a parent that is
+		 * not active, has run-time PM enabled and the
+		 * 'power.ignore_children' flag unset.
+		 */
+		if (!parent->power.disable_depth
+		    && !parent->power.ignore_children
+		    && parent->power.runtime_status != RPM_ACTIVE) {
+			error = -EBUSY;
+		} else {
+			if (dev->power.runtime_status == RPM_SUSPENDED)
+				atomic_inc(&parent->power.child_count);
+			dev->power.runtime_status = status;
+		}
+
+		spin_unlock_irq(&parent->power.lock);
+
+		if (error)
+			goto out;
Quoted 8 lines

drivers/base/power/runtime.c line 755

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	} else {
+		dev->power.runtime_status = status;
+	}
+
+ out_clear:
+	dev->power.runtime_failure = false;

Move all those assignments to dev->power.runtime_status down here.

OK

Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

If dev->power.runtime_failure, there's no need to do anything more.

Quoted 7 lines
Quoted 7 lines
drivers/base/power/runtime.c new file +7 -0
+
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		dev->power.request = RPM_REQ_NONE;
+
+		spin_unlock_irq(&dev->power.lock);
Quoted 8 lines

drivers/base/power/runtime.c line 842

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+
+		cancel_work_sync(&dev->power.work);
+
+		spin_lock_irq(&dev->power.lock);
+
+		dev->power.request_pending = false;

Remove excessive whitespace.

OK

Quoted 37 lines
Quoted 37 lines
drivers/base/power/runtime.c new file +37 -0
+	}
+
+	if (dev->power.runtime_status == RPM_SUSPENDING
+	    || dev->power.runtime_status == RPM_RESUMING) {
+		DEFINE_WAIT(wait);
+
+		/* Suspend or wake-up in progress. */
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (dev->power.runtime_status != RPM_SUSPENDING
+			    && dev->power.runtime_status != RPM_RESUMING)
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}
+
+	if (dev->power.runtime_failure)
+		goto out;
+
+	if (dev->power.idle_notification) {
+		DEFINE_WAIT(wait);
+
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (!dev->power.idle_notification)
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
Quoted 8 lines

drivers/base/power/runtime.c line 174

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}

This wait loop should be merged with the previous loop.

OK

Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+void pm_runtime_init(struct device *dev)
+{
+	spin_lock_init(&dev->power.lock);
+
+	dev->power.runtime_status = RPM_SUSPENDED;
+	dev->power.idle_notification = false;
Quoted 8 lines

drivers/base/power/runtime.c line 909

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+
+	dev->power.disable_depth = 1;
+	atomic_set(&dev->power.usage_count, 0);
+
+	dev->power.runtime_failure = false;
+	dev->power.last_error = 0;

You don't have to set values to 0; they are initialized by kzalloc.

No, I don't, but does it really hurt?

Quoted 5 lines

drivers/base/power/runtime.c line 922

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	dev->power.suspend_timer.expires = jiffies;
+	dev->power.suspend_timer.data = (unsigned long)dev;
+	dev->power.suspend_timer.function = pm_suspend_timer_fn;

Use setup_timer() instead of assigning these fields directly.

OK

Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
Quoted 8 lines

drivers/base/power/runtime.c line 944

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.

Quoted 8 lines

include/linux/pm_runtime.h line 20

Quoted 5 lines
include/linux/pm_runtime.h new file +2 -0
 
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(struct device *dev);

I don't like seeing these two functions included in the public header file. It's enough to put them in drivers/base/power/power.h.

OK

Quoted 23 lines
Quoted 23 lines
drivers/base/power/main.c +10 -0
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object to initialize.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,8 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
Quoted 8 lines

drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121

Quoted 3 lines
drivers/base/power/main.c +2 -0
+
+	pm_runtime_remove(dev);
 }

Calling pm_runtime_init() from device_pm_init() and pm_runtime_remove() from device_pm_remove() isn't good. If CONFIG_PM_SLEEP isn't enabled then the calls won't be compiled, even if CONFIG_PM_RUNTIME is set.

Right, I shouldn't have moved device_pm_init() to main.c at all.

Quoted 9 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).

Quoted 6 lines
Quoted 6 lines
drivers/base/dd.c +0 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
Quoted 7 lines

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 3 lines
drivers/base/dd.c +2 -0
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend.

I'm not sure how that's possible, but whatever.

Quoted 10 lines

Instead we should do something like this:

	/* Wait for runtime PM calls to finish and prevent new calls
	 * until the probe is done.
	 */
	pm_runtime_disable(dev);
	pm_runtime_get_noresume(dev);
	pm_runtime_enable(dev):
	ret = really_probe(dev, drv);
	pm_runtime_put_sync(dev);

Fine by me.

Quoted 10 lines

Documentation/power/runtime_pm.txt

Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3 -0
 
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.

Quoted 10 lines

Documentation/power/runtime_pm.txt line 148

Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+Additionally, the helper functions provided by the PM core obey the following
+rules:
+
+  * If ->runtime_suspend() is about to be executed or the execution of it is
+    scheduled or there's a pending request to execute it, ->runtime_idle() will
+    not be executed for the same device.

Shouldn't we allow runtime_idle when a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.

Hmm. We can.

Quoted 11 lines

Documentation/power/runtime_pm.txt line 266

Quoted 9 lines

+4. Run-time PM Device Helper Functions

Documentation/power/runtime_pm.txt new file +7 -0
+
+The following run-time PM helper functions are defined in
+drivers/base/power/runtime.c and include/linux/pm_runtime.h:
 
+  int pm_schedule_suspend(struct device *dev, unsigned int delay);
+    - schedule the execution of ->runtime_suspend() for the device's bus type
+      in future, where 'delay' is the time to wait before queuing up a suspend
+      work item in pm_wq, in miliseconds (if 'delay' is zero, the work item is

Fix spelling of "milli".

OK

Quoted 2 lines

Explain that the new delay will override the
old one if a suspend was already scheduled and not yet expired.

OK

Quoted 4 lines

Documentation/power/runtime_pm.txt line 285

Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2 -0
+  int pm_runtime_get_sync(struct device *dev);
+    - increment the device's usage counter, run pm_rutime_resume(dev) and return

Fix spelling of "runtime". Same under pm_runtime_put_sync.

OK

Thanks a lot for the comments, I'll post an updated patch addressing them in the next few days.

Best,
Rafael

#4 lore raw
In reply to Alan Stern
Quoted 11 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki

Hi,

OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.

I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...

No problem.

Quoted 8 lines

One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.

pm_runtime_disable() gets used for several different purposes. For the usage in pm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?

Yes, we can do that.

Quoted 3 lines

In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.

OK, I'll try to put something like this in there.

Quoted 11 lines

drivers/base/power/runtime.c line 39

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static void pm_runtime_cancel_pending(struct device *dev)
+{
+	pm_runtime_deactivate_timer(dev);
+	/*
+	 * If there's a request pending, make sure its work function will return
+	 * without doing anything.
+	 */
+	if (dev->power.request_pending)
+		dev->power.request = RPM_REQ_NONE;

No need for the "if"; you can always do the assignment.

OK

Quoted 8 lines

drivers/base/power/runtime.c line 53

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_runtime_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;

Instead of assigning to retval, you could simply return these values.

I could, but I chose not to.

Quoted 11 lines

drivers/base/power/runtime.c line 61

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+	else if (dev->power.idle_notification)
+		retval = -EINPROGRESS;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0
+	    || dev->power.runtime_status == RPM_SUSPENDED
+	    || dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EAGAIN;

Do we also want to rule out RPM_RESUMING? That is,

Quoted 1 line
	    || dev->power.runtime_status != RPM_ACTIVE

Yes, we do, thanks.

Quoted 9 lines

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.

Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.

Quoted 1 line

The same thing can be done in other places.

I'm not really sure it can.

Quoted 5 lines
Quoted 3 lines

+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called via pm_wq.

Fix spelling of "function". Likewise in __pm_runtime_resume.

Will do, thanks.

Quoted 13 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+{

...

drivers/base/power/runtime.c line 218

Quoted 7 lines
drivers/base/power/runtime.c new file +7 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (parent && !parent->power.ignore_children)
+		pm_request_idle(parent);
+
+	if (notify)
+		pm_runtime_idle(dev);

Move this up before the spin_unlock_irq and call __pm_runtime_idle instead. The same sort of thing can be done in __pm_runtime_resume.

OK

Quoted 18 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+static int __pm_request_suspend(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		return -EINVAL;

drivers/base/power/runtime.c line 480

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+
+	if (dev->power.runtime_status == RPM_SUSPENDED)
+		retval = 1;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0)
+		retval = -EAGAIN;
+	else if (dev->power.runtime_status == RPM_SUSPENDING)
+		retval = -EINPROGRESS;
+	else if (!pm_children_suspended(dev))
+		retval = -EBUSY;

Insert:

Quoted 8 lines
	if (retval)
		return retval;

Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)

However, if it's going to return 1, it should override existing idle-notify and suspend requests. I'll add

if (retval < 0)
return retval;

Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/*
+		 * Pending resume requests take precedence over us, but we can
+		 * overtake any other pending request.
Quoted 8 lines

drivers/base/power/runtime.c line 493

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 */
+		if (dev->power.request == RPM_REQ_RESUME)
+			retval = -EAGAIN;
+		else if (dev->power.request != RPM_REQ_SUSPEND)
+			dev->power.request = retval ?
+						RPM_REQ_NONE : RPM_REQ_SUSPEND;

Now there's no need to check retval.

It is. If retval is 1, we cancel pending idle-notify and suspend requests.

Quoted 6 lines

drivers/base/power/runtime.c line 496

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+
+		if (dev->power.request == RPM_REQ_SUSPEND)
+			return 0;

Just simply:

		return retval;

OK, I'll change that.

Quoted 11 lines

Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.

drivers/base/power/runtime.c line 65

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	}
+
+	if (retval)
+		return retval;

Now this isn't needed. Similar code rearrangements can be made in __pm_request_resume.

OK

Quoted 3 lines
Quoted 1 line

+int __pm_request_resume(struct device *dev)

Should be static.

OK

Quoted 10 lines

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.

Quoted 38 lines
Quoted 38 lines
drivers/base/power/runtime.c new file +38 -0
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;
+
+	if (dev->power.runtime_status == status)
+		goto out_clear;
+
+	if (status == RPM_SUSPENDED) {
+		/* It always is possible to set the status to 'suspended'. */
+		if (parent)
+			atomic_add_unless(&parent->power.child_count, -1, 0);
+		dev->power.runtime_status = status;
+		goto out_clear;
+	}
+
+	if (parent) {
+		spin_lock_irq(&parent->power.lock);
+
+		/*
+		 * It is invalid to put an active child under a parent that is
+		 * not active, has run-time PM enabled and the
+		 * 'power.ignore_children' flag unset.
+		 */
+		if (!parent->power.disable_depth
+		    && !parent->power.ignore_children
+		    && parent->power.runtime_status != RPM_ACTIVE) {
+			error = -EBUSY;
+		} else {
+			if (dev->power.runtime_status == RPM_SUSPENDED)
+				atomic_inc(&parent->power.child_count);
+			dev->power.runtime_status = status;
+		}
+
+		spin_unlock_irq(&parent->power.lock);
+
+		if (error)
+			goto out;
Quoted 8 lines

drivers/base/power/runtime.c line 755

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	} else {
+		dev->power.runtime_status = status;
+	}
+
+ out_clear:
+	dev->power.runtime_failure = false;

Move all those assignments to dev->power.runtime_status down here.

OK

Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

If dev->power.runtime_failure, there's no need to do anything more.

Quoted 7 lines
Quoted 7 lines
drivers/base/power/runtime.c new file +7 -0
+
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		dev->power.request = RPM_REQ_NONE;
+
+		spin_unlock_irq(&dev->power.lock);
Quoted 8 lines

drivers/base/power/runtime.c line 842

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+
+		cancel_work_sync(&dev->power.work);
+
+		spin_lock_irq(&dev->power.lock);
+
+		dev->power.request_pending = false;

Remove excessive whitespace.

OK

Quoted 37 lines
Quoted 37 lines
drivers/base/power/runtime.c new file +37 -0
+	}
+
+	if (dev->power.runtime_status == RPM_SUSPENDING
+	    || dev->power.runtime_status == RPM_RESUMING) {
+		DEFINE_WAIT(wait);
+
+		/* Suspend or wake-up in progress. */
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (dev->power.runtime_status != RPM_SUSPENDING
+			    && dev->power.runtime_status != RPM_RESUMING)
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}
+
+	if (dev->power.runtime_failure)
+		goto out;
+
+	if (dev->power.idle_notification) {
+		DEFINE_WAIT(wait);
+
+		for (;;) {
+			prepare_to_wait(&dev->power.wait_queue, &wait,
+					TASK_UNINTERRUPTIBLE);
+			if (!dev->power.idle_notification)
+				break;
+
+			spin_unlock_irq(&dev->power.lock);
+
Quoted 8 lines

drivers/base/power/runtime.c line 174

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+			schedule();
+
+			spin_lock_irq(&dev->power.lock);
+		}
+		finish_wait(&dev->power.wait_queue, &wait);
+	}

This wait loop should be merged with the previous loop.

OK

Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+void pm_runtime_init(struct device *dev)
+{
+	spin_lock_init(&dev->power.lock);
+
+	dev->power.runtime_status = RPM_SUSPENDED;
+	dev->power.idle_notification = false;
Quoted 8 lines

drivers/base/power/runtime.c line 909

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+
+	dev->power.disable_depth = 1;
+	atomic_set(&dev->power.usage_count, 0);
+
+	dev->power.runtime_failure = false;
+	dev->power.last_error = 0;

You don't have to set values to 0; they are initialized by kzalloc.

No, I don't, but does it really hurt?

Quoted 5 lines

drivers/base/power/runtime.c line 922

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	dev->power.suspend_timer.expires = jiffies;
+	dev->power.suspend_timer.data = (unsigned long)dev;
+	dev->power.suspend_timer.function = pm_suspend_timer_fn;

Use setup_timer() instead of assigning these fields directly.

OK

Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
Quoted 8 lines

drivers/base/power/runtime.c line 944

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.

Quoted 8 lines

include/linux/pm_runtime.h line 20

Quoted 5 lines
include/linux/pm_runtime.h new file +2 -0
 
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(struct device *dev);

I don't like seeing these two functions included in the public header file. It's enough to put them in drivers/base/power/power.h.

OK

Quoted 23 lines
Quoted 23 lines
drivers/base/power/main.c +10 -0
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object to initialize.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,8 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
Quoted 8 lines

drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121

Quoted 3 lines
drivers/base/power/main.c +2 -0
+
+	pm_runtime_remove(dev);
 }

Calling pm_runtime_init() from device_pm_init() and pm_runtime_remove() from device_pm_remove() isn't good. If CONFIG_PM_SLEEP isn't enabled then the calls won't be compiled, even if CONFIG_PM_RUNTIME is set.

Right, I shouldn't have moved device_pm_init() to main.c at all.

Quoted 9 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).

Quoted 6 lines
Quoted 6 lines
drivers/base/dd.c +0 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
Quoted 7 lines

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 3 lines
drivers/base/dd.c +2 -0
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend.

I'm not sure how that's possible, but whatever.

Quoted 10 lines

Instead we should do something like this:

	/* Wait for runtime PM calls to finish and prevent new calls
	 * until the probe is done.
	 */
	pm_runtime_disable(dev);
	pm_runtime_get_noresume(dev);
	pm_runtime_enable(dev):
	ret = really_probe(dev, drv);
	pm_runtime_put_sync(dev);

Fine by me.

Quoted 10 lines

Documentation/power/runtime_pm.txt

Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3 -0
 
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.

Quoted 10 lines

Documentation/power/runtime_pm.txt line 148

Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+Additionally, the helper functions provided by the PM core obey the following
+rules:
+
+  * If ->runtime_suspend() is about to be executed or the execution of it is
+    scheduled or there's a pending request to execute it, ->runtime_idle() will
+    not be executed for the same device.

Shouldn't we allow runtime_idle when a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.

Hmm. We can.

Quoted 11 lines

Documentation/power/runtime_pm.txt line 266

Quoted 9 lines

+4. Run-time PM Device Helper Functions

Documentation/power/runtime_pm.txt new file +7 -0
+
+The following run-time PM helper functions are defined in
+drivers/base/power/runtime.c and include/linux/pm_runtime.h:
 
+  int pm_schedule_suspend(struct device *dev, unsigned int delay);
+    - schedule the execution of ->runtime_suspend() for the device's bus type
+      in future, where 'delay' is the time to wait before queuing up a suspend
+      work item in pm_wq, in miliseconds (if 'delay' is zero, the work item is

Fix spelling of "milli".

OK

Quoted 2 lines

Explain that the new delay will override the
old one if a suspend was already scheduled and not yet expired.

OK

Quoted 4 lines

Documentation/power/runtime_pm.txt line 285

Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2 -0
+  int pm_runtime_get_sync(struct device *dev);
+    - increment the device's usage counter, run pm_rutime_resume(dev) and return

Fix spelling of "runtime". Same under pm_runtime_put_sync.

OK

Thanks a lot for the comments, I'll post an updated patch addressing them in the next few days.

Best,
Rafael

#5 lore raw
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.

Quoted 2 lines

Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.

No, I meant do this:

	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
		spin_unlock_irq(&dev->power.lock);
		dev->bus->pm->runtime_idle(dev);
		spin_lock_irq(&dev->power.lock);
	}

By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do, you should add "__releases(&dev->power.lock)" and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.

Quoted 3 lines
Quoted 1 line

The same thing can be done in other places.

I'm not really sure it can.

The other places aren't quite the same as this. I'll leave it to your discretion. :-)

Quoted 11 lines
Quoted 10 lines

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Quoted 2 lines

Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.

Oops, my mistake. Never mind...

Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

Quoted 1 line

If dev->power.runtime_failure, there's no need to do anything more.

Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...

Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
Quoted 8 lines

drivers/base/power/runtime.c line 944

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

Quoted 2 lines

No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.

Ah, an interesting point. Suppose the device is in RPM_ACTIVE, and then someone calls pm_runtime_disable followed by pm_runtime_set_suspended. Then the device's status would change to RPM_SUSPENDED and the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?

Quoted 10 lines
Quoted 9 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

Quoted 3 lines

If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).

I see. That's a little obscure; a comment would help. Even something as simple as:

error = -EBUSY; /* wake-up during suspend */

Quoted 12 lines
Quoted 11 lines

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 7 lines
drivers/base/dd.c +2 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend.

Quoted 1 line

I'm not sure how that's possible, but whatever.

Suppose the probe routine does:

	pm_runtime_get_sync(dev);
	/* do some work */
	pm_runtime_put_sync(dev);

There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by

	pm_runtime_get_noresume(dev);
	... probe ...
	pm_runtime_put_noidle(dev);

then there won't be any idle notifications.

Quoted 10 lines
Quoted 7 lines

Documentation/power/runtime_pm.txt

Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3 -0
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.

Okay. This needs to be rephrased. For example,

In particular, if the driver requires remote wakeup capability for proper functioning and device_may_wakeup() returns 'false' for the device, then ->runtime_suspend() should return -EBUSY.

The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check device_may_wakeup().

Alan Stern

#6 lore raw
In reply to Rafael J. Wysocki
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.

Quoted 2 lines

Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.

No, I meant do this:

	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
		spin_unlock_irq(&dev->power.lock);
		dev->bus->pm->runtime_idle(dev);
		spin_lock_irq(&dev->power.lock);
	}

By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do, you should add "__releases(&dev->power.lock)" and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.

Quoted 3 lines
Quoted 1 line

The same thing can be done in other places.

I'm not really sure it can.

The other places aren't quite the same as this. I'll leave it to your discretion. :-)

Quoted 11 lines
Quoted 10 lines

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Quoted 2 lines

Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.

Oops, my mistake. Never mind...

Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

Quoted 1 line

If dev->power.runtime_failure, there's no need to do anything more.

Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...

Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
Quoted 8 lines

drivers/base/power/runtime.c line 944

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

Quoted 2 lines

No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.

Ah, an interesting point. Suppose the device is in RPM_ACTIVE, and then someone calls pm_runtime_disable followed by pm_runtime_set_suspended. Then the device's status would change to RPM_SUSPENDED and the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?

Quoted 10 lines
Quoted 9 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

Quoted 3 lines

If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).

I see. That's a little obscure; a comment would help. Even something as simple as:

error = -EBUSY; /* wake-up during suspend */

Quoted 12 lines
Quoted 11 lines

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 7 lines
drivers/base/dd.c +2 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend.

Quoted 1 line

I'm not sure how that's possible, but whatever.

Suppose the probe routine does:

	pm_runtime_get_sync(dev);
	/* do some work */
	pm_runtime_put_sync(dev);

There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by

	pm_runtime_get_noresume(dev);
	... probe ...
	pm_runtime_put_noidle(dev);

then there won't be any idle notifications.

Quoted 10 lines
Quoted 7 lines

Documentation/power/runtime_pm.txt

Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3 -0
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.

Okay. This needs to be rephrased. For example,

In particular, if the driver requires remote wakeup capability for proper functioning and device_may_wakeup() returns 'false' for the device, then ->runtime_suspend() should return -EBUSY.

The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check device_may_wakeup().

Alan Stern

#7 lore raw
Quoted 15 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.

Quoted 2 lines

Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.

Quoted 7 lines

No, I meant do this:

	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
		spin_unlock_irq(&dev->power.lock);
		dev->bus->pm->runtime_idle(dev);
		spin_lock_irq(&dev->power.lock);
	}

Ah, OK. That makes sense.

Quoted 2 lines

By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do,

Well, I guess I should. :-)

Quoted 41 lines

you should add "__releases(&dev->power.lock)"
and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.

Quoted 3 lines
Quoted 1 line

The same thing can be done in other places.

I'm not really sure it can.

The other places aren't quite the same as this. I'll leave it to your discretion. :-)

Quoted 11 lines
Quoted 10 lines

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Quoted 2 lines

Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.

Oops, my mistake. Never mind...

Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

Quoted 5 lines
Quoted 1 line

If dev->power.runtime_failure, there's no need to do anything more.

Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...

OK, I'll drop that if ().

Quoted 20 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
Quoted 8 lines

drivers/base/power/runtime.c line 944

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

Quoted 2 lines

No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.

Quoted 6 lines

Ah, an interesting point. Suppose the device is in RPM_ACTIVE, and then someone calls pm_runtime_disable followed by pm_runtime_set_suspended. Then the device's status would change to RPM_SUSPENDED and the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?

OK, having reconsidered that, I think you're right, it should go into __pm_runtime_set_status().

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

Quoted 8 lines
Quoted 3 lines

If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).

I see. That's a little obscure; a comment would help. Even something as simple as:

error = -EBUSY; /* wake-up during suspend */

OK

Quoted 20 lines
Quoted 12 lines
Quoted 11 lines

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 7 lines
drivers/base/dd.c +2 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend.

Quoted 1 line

I'm not sure how that's possible, but whatever.

Suppose the probe routine does:

	pm_runtime_get_sync(dev);
	/* do some work */
	pm_runtime_put_sync(dev);
Quoted 8 lines

There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by

	pm_runtime_get_noresume(dev);
	... probe ...
	pm_runtime_put_noidle(dev);

then there won't be any idle notifications.

Ah, OK. So the point is that we should always idle-notify after .probe(), because that's what .probe() would most probably want to do. I guess that makes sense.

Quoted 11 lines
Quoted 10 lines
Quoted 7 lines

Documentation/power/runtime_pm.txt

Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3 -0
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.

Quoted 8 lines

Okay. This needs to be rephrased. For example,

In particular, if the driver requires remote wakeup capability for proper functioning and device_may_wakeup() returns 'false' for the device, then ->runtime_suspend() should return -EBUSY.

The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check device_may_wakeup().

OK

Thanks a lot again, I'll do my best to address the comments in the next version of the patch.

Best,
Rafael

#8 lore raw

PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

Hi,

The patch below should address all of your recent comments.

Additionally I changed a few bits that I thought could turn out to be problematic at one point.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

---
 Documentation/power/runtime_pm.txt |  382 +++++++++++++++
 drivers/base/dd.c                  |   15 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   20 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       |  939 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  102 +++-
 include/linux/pm_runtime.h         |  115 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1625 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
Index: linux-2.6/kernel/power/Kconfig
===================================================================
@@ -208,3 +208,17 @@ config APM_EMULATION
 	  random kernel OOPSes or reboots that don't seem to be related to
 	  anything, try disabling/enabling this option (or disabling/enabling
 	  APM in your BIOS).
+
+config PM_RUNTIME
+	bool "Run-time PM core functionality"
+	depends on PM
+	---help---
+	  Enable functionality allowing I/O devices to be put into energy-saving
+	  (low power) states at run time (or autosuspended) after a specified
+	  period of inactivity and woken up in response to a hardware-generated
+	  wake-up event or a driver's request.
+
+	  Hardware support is generally required for this functionality to work
+	  and the bus type drivers of the buses the devices are on are
+	  responsible for the actual handling of the autosuspend requests and
+	  wake-up events.
kernel/power/main.c +17 -0
Index: linux-2.6/kernel/power/main.c
===================================================================
@@ -11,6 +11,7 @@
 #include <linux/kobject.h>
 #include <linux/string.h>
 #include <linux/resume-trace.h>
+#include <linux/workqueue.h>
 
 #include "power.h"
 
@@ -217,8 +218,24 @@ static struct attribute_group attr_group
 	.attrs = g,
 };
 
+#ifdef CONFIG_PM_RUNTIME
+struct workqueue_struct *pm_wq;
+
+static int __init pm_start_workqueue(void)
+{
+	pm_wq = create_freezeable_workqueue("pm");
+
+	return pm_wq ? 0 : -ENOMEM;
+}
+#else
+static inline int pm_start_workqueue(void) { return 0; }
+#endif
+
 static int __init pm_init(void)
 {
+	int error = pm_start_workqueue();
+	if (error)
+		return error;
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
include/linux/pm.h +99 -3

131 lines not on this page · raw message

drivers/base/power/Makefile +1 -0
Index: linux-2.6/drivers/base/power/Makefile
===================================================================
@@ -1,5 +1,6 @@
 obj-$(CONFIG_PM)	+= sysfs.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o
+obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +939 -0

939 lines not on this page · raw message

include/linux/pm_runtime.h new file +115 -0

115 lines not on this page · raw message

drivers/base/power/main.c +19 -1
Index: linux-2.6/drivers/base/power/main.c
===================================================================
@@ -21,6 +21,7 @@
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/resume-trace.h>
 #include <linux/rwsem.h>
 #include <linux/interrupt.h>
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object being initialized.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+	pm_runtime_remove(dev);
 }
 
 /**
@@ -512,6 +524,7 @@ static void dpm_complete(pm_message_t st
 			mutex_unlock(&dpm_list_mtx);
 
 			device_complete(dev, state);
+			pm_runtime_enable(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up during suspend. */
+			error = -EBUSY;
+		else
+			error = device_prepare(dev, state);
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
 			dev->power.status = DPM_ON;
+			pm_runtime_enable(dev);
 			if (error == -EAGAIN) {
 				put_device(dev);
 				error = 0;
drivers/base/dd.c +15 -0
Index: linux-2.6/drivers/base/dd.c
===================================================================
@@ -23,6 +23,7 @@
 #include <linux/kthread.h>
 #include <linux/wait.h>
 #include <linux/async.h>
+#include <linux/pm_runtime.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -202,7 +203,17 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	/*
+	 * Wait for run-time PM calls to complete and prevent new suspend calls
+	 * until the probe is done.
+	 */
+	pm_runtime_disable(dev);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_enable(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
+	if (!ret)
+		pm_runtime_idle(dev);
 
 	return ret;
 }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }
 
drivers/base/power/power.h +24 -7
Index: linux-2.6/drivers/base/power/power.h
===================================================================
@@ -1,7 +1,14 @@
-static inline void device_pm_init(struct device *dev)
-{
-	dev->power.status = DPM_ON;
-}
+#ifdef CONFIG_PM_RUNTIME
+
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(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) {}
+
+#endif /* !CONFIG_PM_RUNTIME */
 
 #ifdef CONFIG_PM_SLEEP
 
@@ -16,23 +23,33 @@ static inline struct device *to_device(s
 	return container_of(entry, struct device, power.entry);
 }
 
+extern void device_pm_init(struct device *dev);
 extern void device_pm_add(struct device *);
 extern void device_pm_remove(struct device *);
 extern void device_pm_move_before(struct device *, struct device *);
 extern void device_pm_move_after(struct device *, struct device *);
 extern void device_pm_move_last(struct device *);
 
-#else /* CONFIG_PM_SLEEP */
+#else /* !CONFIG_PM_SLEEP */
+
+static inline void device_pm_init(struct device *dev)
+{
+	pm_runtime_init(dev);
+}
+
+static inline void device_pm_remove(struct device *dev)
+{
+	pm_runtime_remove(dev);
+}
 
 static inline void device_pm_add(struct device *dev) {}
-static inline void device_pm_remove(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
 					 struct device *devb) {}
 static inline void device_pm_move_after(struct device *deva,
 					struct device *devb) {}
 static inline void device_pm_move_last(struct device *dev) {}
 
-#endif
+#endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM
 
Documentation/power/runtime_pm.txt new file +382 -0

382 lines not on this page · raw message

#9 lore raw

Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

Quoted 6 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your recent comments.

Additionally I changed a few bits that I thought could turn out to be problematic at one point.

Looking good. I've got a few more suggestions.

It occurred to me that there's no need for a separate "runtime_failure" flag. A nonzero value of "last_error" will do just as well. If you make this change, note that it affects the documentation as well as the code.

If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.

Quoted 10 lines

+/**
+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.

Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.

Quoted 6 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +3 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{

...

drivers/base/power/runtime.c line 141

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/* Pending resume requests take precedence over us. */
+		if (dev->power.request == RPM_REQ_RESUME)
+			return -EAGAIN;
+		/* Other pending requests need to be canceled. */
+		dev->power.request = RPM_REQ_NONE;
+	}

Might as well use pm_runtime_cancel_pending since we have it:

	/* Pending resume requests take precedence over us. */
	if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)
		return -EAGAIN;

	/* Other pending requests need to be canceled. */
	pm_runtime_cancel_pending(dev);

...

drivers/base/power/runtime.c line 212

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+	if (dev->power.deferred_resume) {
+		__pm_request_resume(dev);

__pm_runtime_resume instead?

Quoted 11 lines

+/**
+ * __pm_runtime_resume - Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.

Change the last two sentences as follows: Cancel any pending requests.

drivers/base/power/runtime.c line 268

Quoted 12 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +9 -0
+int __pm_runtime_resume(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	struct device *parent = NULL;
+	int retval = 0;
+
+ repeat:
+	if (dev->power.runtime_failure)
+		return -EINVAL;

Here and in two places below, goto out_parent instead of returning directly.

...

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	if (!parent && dev->parent) {
+		/*
+		 * Increment the parent's resume counter and resume it if
+		 * necessary.

drivers/base/power/runtime.c line 320

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+		 */
+		parent = dev->parent;
+		spin_unlock_irq(&dev->power.lock);
+
+		retval = pm_runtime_get_sync(parent);
+
+		spin_lock_irq(&dev->power.lock);
+		/* We can resume if the parent's run-time PM is disabled. */
+		if (retval < 0 && retval != -EAGAIN)
+			goto out_parent;

Instead of checking retval, how about checking the parent's PM status? Also, this isn't needed if the parent is set to ignore children.

drivers/base/power/runtime.c line 439

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static int __pm_request_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0

This line should be removed.

...

drivers/base/power/runtime.c line 460

Quoted 13 lines
drivers/base/power/runtime.c new file +13 -0
+	if (dev->power.request_pending && dev->power.request != RPM_REQ_NONE) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
+	}
+
+	dev->power.request = RPM_REQ_IDLE;
+	if (dev->power.request_pending)
+		return retval;
+
+	dev->power.request_pending = true;
+	queue_work(pm_wq, &dev->power.work);

This should be done consistently with the other routines. Thus:

	if (dev->power.request_pending) {
		/* All other requests take precedence. */
		if (dev->power.request == RPM_REQ_NONE)
			dev->power.request = RPM_REQ_IDLE;
		else if (dev->power.request != RPM_REQ_IDLE)
			retval = -EAGAIN;
		return retval;
	}

	dev->power.request = RPM_REQ_IDLE;
	dev->power.request_pending = true;
	queue_work(pm_wq, &dev->power.work);
Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;

drivers/base/power/runtime.c line 738

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+	bool notify_parent = false;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;

Set "error" to a negative code?

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 8 lines
drivers/base/power/main.c +3 -1
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up during suspend. */
+			error = -EBUSY;

Or maybe "Wakeup was requested during sleep transition."

Documentation/power/runtime_pm.txt line 219

Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5 -0
+  unsigned int deferred_resume;
+    - set if ->runtime_resume() is about to be run while ->runtime_suspend() is
+      being executed for that device and it is not practical to wait for the
+      suspend to complete; means "queue up a resume request as soon as you've
+      suspended"

"start a resume" instead of "queue up a resume request"?

Quoted 1 line
Documentation/power/runtime_pm.txt new file +1 -0
+5. Run-time PM Initialization

...

Quoted 1 line

+If the defaul initial run-time PM status of the device (i.e. 'suspended')

Fix spelling of "default".

Documentation/power/runtime_pm.txt line 381

Quoted 12 lines
Documentation/power/runtime_pm.txt new file +12 -0
+reflects the actual state of the device, its bus type's or its driver's
+->probe() callback will likely need to wake it up using one of the PM core's
+helper functions described in Section 4.  In that case, pm_runtime_resume()
+should be used.  Of course, for this purpose the device's run-time PM has to be
+enabled earlier by calling pm_runtime_enable().
+
+If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their
+asynchronous counterparts, they will fail returning -EAGAIN, because the
+device's usage counter is incremented by the core before executing ->probe().
+Still, it may be desirable to suspend the device as soon as ->probe() has
+finished, so the core uses pm_runtime_idle() to invoke the device bus type's
+->runtime_idle() callback at that time, which only happens even if ->probe()

s/which only happens even/but only/

Quoted 1 line

+is successful.

Alan Stern

#10 lore raw

Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

In reply to Rafael J. Wysocki
Quoted 6 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your recent comments.

Additionally I changed a few bits that I thought could turn out to be problematic at one point.

Looking good. I've got a few more suggestions.

It occurred to me that there's no need for a separate "runtime_failure" flag. A nonzero value of "last_error" will do just as well. If you make this change, note that it affects the documentation as well as the code.

If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.

Quoted 10 lines

+/**
+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.

Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.

Quoted 6 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +3 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{

...

drivers/base/power/runtime.c line 141

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/* Pending resume requests take precedence over us. */
+		if (dev->power.request == RPM_REQ_RESUME)
+			return -EAGAIN;
+		/* Other pending requests need to be canceled. */
+		dev->power.request = RPM_REQ_NONE;
+	}

Might as well use pm_runtime_cancel_pending since we have it:

	/* Pending resume requests take precedence over us. */
	if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)
		return -EAGAIN;

	/* Other pending requests need to be canceled. */
	pm_runtime_cancel_pending(dev);

...

drivers/base/power/runtime.c line 212

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+	if (dev->power.deferred_resume) {
+		__pm_request_resume(dev);

__pm_runtime_resume instead?

Quoted 11 lines

+/**
+ * __pm_runtime_resume - Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.

Change the last two sentences as follows: Cancel any pending requests.

drivers/base/power/runtime.c line 268

Quoted 12 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +9 -0
+int __pm_runtime_resume(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	struct device *parent = NULL;
+	int retval = 0;
+
+ repeat:
+	if (dev->power.runtime_failure)
+		return -EINVAL;

Here and in two places below, goto out_parent instead of returning directly.

...

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	if (!parent && dev->parent) {
+		/*
+		 * Increment the parent's resume counter and resume it if
+		 * necessary.

drivers/base/power/runtime.c line 320

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+		 */
+		parent = dev->parent;
+		spin_unlock_irq(&dev->power.lock);
+
+		retval = pm_runtime_get_sync(parent);
+
+		spin_lock_irq(&dev->power.lock);
+		/* We can resume if the parent's run-time PM is disabled. */
+		if (retval < 0 && retval != -EAGAIN)
+			goto out_parent;

Instead of checking retval, how about checking the parent's PM status? Also, this isn't needed if the parent is set to ignore children.

drivers/base/power/runtime.c line 439

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static int __pm_request_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0

This line should be removed.

...

drivers/base/power/runtime.c line 460

Quoted 13 lines
drivers/base/power/runtime.c new file +13 -0
+	if (dev->power.request_pending && dev->power.request != RPM_REQ_NONE) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
+	}
+
+	dev->power.request = RPM_REQ_IDLE;
+	if (dev->power.request_pending)
+		return retval;
+
+	dev->power.request_pending = true;
+	queue_work(pm_wq, &dev->power.work);

This should be done consistently with the other routines. Thus:

	if (dev->power.request_pending) {
		/* All other requests take precedence. */
		if (dev->power.request == RPM_REQ_NONE)
			dev->power.request = RPM_REQ_IDLE;
		else if (dev->power.request != RPM_REQ_IDLE)
			retval = -EAGAIN;
		return retval;
	}

	dev->power.request = RPM_REQ_IDLE;
	dev->power.request_pending = true;
	queue_work(pm_wq, &dev->power.work);
Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;

drivers/base/power/runtime.c line 738

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+	bool notify_parent = false;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;

Set "error" to a negative code?

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 8 lines
drivers/base/power/main.c +3 -1
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up during suspend. */
+			error = -EBUSY;

Or maybe "Wakeup was requested during sleep transition."

Documentation/power/runtime_pm.txt line 219

Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5 -0
+  unsigned int deferred_resume;
+    - set if ->runtime_resume() is about to be run while ->runtime_suspend() is
+      being executed for that device and it is not practical to wait for the
+      suspend to complete; means "queue up a resume request as soon as you've
+      suspended"

"start a resume" instead of "queue up a resume request"?

Quoted 1 line
Documentation/power/runtime_pm.txt new file +1 -0
+5. Run-time PM Initialization

...

Quoted 1 line

+If the defaul initial run-time PM status of the device (i.e. 'suspended')

Fix spelling of "default".

Documentation/power/runtime_pm.txt line 381

Quoted 12 lines
Documentation/power/runtime_pm.txt new file +12 -0
+reflects the actual state of the device, its bus type's or its driver's
+->probe() callback will likely need to wake it up using one of the PM core's
+helper functions described in Section 4.  In that case, pm_runtime_resume()
+should be used.  Of course, for this purpose the device's run-time PM has to be
+enabled earlier by calling pm_runtime_enable().
+
+If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their
+asynchronous counterparts, they will fail returning -EAGAIN, because the
+device's usage counter is incremented by the core before executing ->probe().
+Still, it may be desirable to suspend the device as soon as ->probe() has
+finished, so the core uses pm_runtime_idle() to invoke the device bus type's
+->runtime_idle() callback at that time, which only happens even if ->probe()

s/which only happens even/but only/

Quoted 1 line

+is successful.

Alan Stern

#11 lore raw

Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

Quoted 6 lines · Alan Stern
Quoted 4 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your recent comments.

Quoted 8 lines
Quoted 2 lines

Additionally I changed a few bits that I thought could turn out to be problematic at one point.

Looking good. I've got a few more suggestions.

It occurred to me that there's no need for a separate "runtime_failure" flag. A nonzero value of "last_error" will do just as well.

Yes, good catch. I don't quite remember why I wanted the flag and a separate error field.

Quoted 2 lines

If you make this change, note that it affects the documentation as well as the code.

Sure.

Quoted 3 lines

If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.

Well, it's not very clear what to return to the caller in such a case. I guess we can return -EAGAIN.

Quoted 11 lines
Quoted 10 lines

+/**
+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.

Quoted 2 lines

Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.

OK

Quoted 19 lines
Quoted 6 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +3 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{

...

drivers/base/power/runtime.c line 141

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/* Pending resume requests take precedence over us. */
+		if (dev->power.request == RPM_REQ_RESUME)
+			return -EAGAIN;
+		/* Other pending requests need to be canceled. */
+		dev->power.request = RPM_REQ_NONE;
+	}

Might as well use pm_runtime_cancel_pending since we have it:

Quoted 6 lines
	/* Pending resume requests take precedence over us. */
	if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)
		return -EAGAIN;

	/* Other pending requests need to be canceled. */
	pm_runtime_cancel_pending(dev);

OK

Quoted 5 lines

...

drivers/base/power/runtime.c line 212

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+	if (dev->power.deferred_resume) {
+		__pm_request_resume(dev);

__pm_runtime_resume instead?

In which case we shouldn't execute the code below, IMO, but return immediately instead.

Quoted 12 lines
Quoted 11 lines

+/**
+ * __pm_runtime_resume - Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.

Quoted 1 line

Change the last two sentences as follows: Cancel any pending requests.

OK

Quoted 7 lines
Quoted 7 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +4 -0
+int __pm_runtime_resume(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	struct device *parent = NULL;
Quoted 8 lines

drivers/base/power/runtime.c line 131

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	int retval = 0;
+
+ repeat:
+	if (dev->power.runtime_failure)
+		return -EINVAL;

Here and in two places below, goto out_parent instead of returning directly.

Ah, that was a real bug. Thanks for catching it!

Quoted 17 lines

...

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	if (!parent && dev->parent) {
+		/*
+		 * Increment the parent's resume counter and resume it if
+		 * necessary.

drivers/base/power/runtime.c line 320

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+		 */
+		parent = dev->parent;
+		spin_unlock_irq(&dev->power.lock);
+
+		retval = pm_runtime_get_sync(parent);
+
+		spin_lock_irq(&dev->power.lock);
+		/* We can resume if the parent's run-time PM is disabled. */
+		if (retval < 0 && retval != -EAGAIN)
+			goto out_parent;

Instead of checking retval, how about checking the parent's PM status?

Should work.

Quoted 1 line

Also, this isn't needed if the parent is set to ignore children.

OK, I'll change that.

Quoted 11 lines

drivers/base/power/runtime.c line 439

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static int __pm_request_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0

This line should be removed.

Yeah, thanks!

Quoted 17 lines

...

drivers/base/power/runtime.c line 460

Quoted 13 lines
drivers/base/power/runtime.c new file +13 -0
+	if (dev->power.request_pending && dev->power.request != RPM_REQ_NONE) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
+	}
+
+	dev->power.request = RPM_REQ_IDLE;
+	if (dev->power.request_pending)
+		return retval;
+
+	dev->power.request_pending = true;
+	queue_work(pm_wq, &dev->power.work);

This should be done consistently with the other routines. Thus:

Quoted 12 lines
	if (dev->power.request_pending) {
		/* All other requests take precedence. */
		if (dev->power.request == RPM_REQ_NONE)
			dev->power.request = RPM_REQ_IDLE;
		else if (dev->power.request != RPM_REQ_IDLE)
			retval = -EAGAIN;
		return retval;
	}

	dev->power.request = RPM_REQ_IDLE;
	dev->power.request_pending = true;
	queue_work(pm_wq, &dev->power.work);

OK

Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	bool notify_parent = false;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
Quoted 8 lines

drivers/base/power/runtime.c line 738

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;

Set "error" to a negative code?

OK

Quoted 10 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 8 lines
drivers/base/power/main.c +3 -1
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up during suspend. */
+			error = -EBUSY;

Or maybe "Wakeup was requested during sleep transition."

Sounds better.

Quoted 7 lines

Documentation/power/runtime_pm.txt line 219

Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5 -0
+  unsigned int deferred_resume;
+    - set if ->runtime_resume() is about to be run while ->runtime_suspend() is
+      being executed for that device and it is not practical to wait for the
+      suspend to complete; means "queue up a resume request as soon as you've
+      suspended"

"start a resume" instead of "queue up a resume request"?

OK

Quoted 5 lines
Quoted 1 line
Documentation/power/runtime_pm.txt new file +1 -0
+5. Run-time PM Initialization

...

Quoted 1 line

+If the defaul initial run-time PM status of the device (i.e. 'suspended')

Fix spelling of "default".

OK

Quoted 6 lines
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+reflects the actual state of the device, its bus type's or its driver's
+->probe() callback will likely need to wake it up using one of the PM core's
+helper functions described in Section 4.  In that case, pm_runtime_resume()
+should be used.  Of course, for this purpose the device's run-time PM has to be
+enabled earlier by calling pm_runtime_enable().
+
Quoted 10 lines

Documentation/power/runtime_pm.txt line 381

Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their
+asynchronous counterparts, they will fail returning -EAGAIN, because the
+device's usage counter is incremented by the core before executing ->probe().
+Still, it may be desirable to suspend the device as soon as ->probe() has
+finished, so the core uses pm_runtime_idle() to invoke the device bus type's
+->runtime_idle() callback at that time, which only happens even if ->probe()

s/which only happens even/but only/

Quoted 1 line

+is successful.

OK

Thanks for the comments! In fact I've already updated the patch to address them, so I'll send it in a little while.

Best,
Rafael

#12 lore raw

Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

Quoted 6 lines · Rafael J. Wysocki
Quoted 3 lines

If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.

Well, it's not very clear what to return to the caller in such a case. I guess we can return -EAGAIN.

I would have said to return 0, just as if the resume had been queued. But it doesn't matter very much.

Alan Stern

#13 lore raw

Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

In reply to Rafael J. Wysocki
Quoted 6 lines · Rafael J. Wysocki
Quoted 3 lines

If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.

Well, it's not very clear what to return to the caller in such a case. I guess we can return -EAGAIN.

I would have said to return 0, just as if the resume had been queued. But it doesn't matter very much.

Alan Stern

#14 lore raw

Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

In reply to Alan Stern
Quoted 6 lines · Alan Stern
Quoted 4 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your recent comments.

Quoted 8 lines
Quoted 2 lines

Additionally I changed a few bits that I thought could turn out to be problematic at one point.

Looking good. I've got a few more suggestions.

It occurred to me that there's no need for a separate "runtime_failure" flag. A nonzero value of "last_error" will do just as well.

Yes, good catch. I don't quite remember why I wanted the flag and a separate error field.

Quoted 2 lines

If you make this change, note that it affects the documentation as well as the code.

Sure.

Quoted 3 lines

If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.

Well, it's not very clear what to return to the caller in such a case. I guess we can return -EAGAIN.

Quoted 11 lines
Quoted 10 lines

+/**
+ * __pm_runtime_suspend - Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.

Quoted 2 lines

Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.

OK

Quoted 19 lines
Quoted 6 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +3 -0
+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{

...

drivers/base/power/runtime.c line 141

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+	pm_runtime_deactivate_timer(dev);
+
+	if (dev->power.request_pending) {
+		/* Pending resume requests take precedence over us. */
+		if (dev->power.request == RPM_REQ_RESUME)
+			return -EAGAIN;
+		/* Other pending requests need to be canceled. */
+		dev->power.request = RPM_REQ_NONE;
+	}

Might as well use pm_runtime_cancel_pending since we have it:

Quoted 6 lines
	/* Pending resume requests take precedence over us. */
	if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)
		return -EAGAIN;

	/* Other pending requests need to be canceled. */
	pm_runtime_cancel_pending(dev);

OK

Quoted 5 lines

...

drivers/base/power/runtime.c line 212

Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+	if (dev->power.deferred_resume) {
+		__pm_request_resume(dev);

__pm_runtime_resume instead?

In which case we shouldn't execute the code below, IMO, but return immediately instead.

Quoted 12 lines
Quoted 11 lines

+/**
+ * __pm_runtime_resume - Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called via pm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.

Quoted 1 line

Change the last two sentences as follows: Cancel any pending requests.

OK

Quoted 7 lines
Quoted 7 lines

+ *
+ * This function must be called under dev->power.lock with interrupts disabled.
+ */

drivers/base/power/runtime.c new file +4 -0
+int __pm_runtime_resume(struct device *dev, bool from_wq)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	struct device *parent = NULL;
Quoted 8 lines

drivers/base/power/runtime.c line 131

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	int retval = 0;
+
+ repeat:
+	if (dev->power.runtime_failure)
+		return -EINVAL;

Here and in two places below, goto out_parent instead of returning directly.

Ah, that was a real bug. Thanks for catching it!

Quoted 17 lines

...

Quoted 4 lines
drivers/base/power/runtime.c new file +4 -0
+	if (!parent && dev->parent) {
+		/*
+		 * Increment the parent's resume counter and resume it if
+		 * necessary.

drivers/base/power/runtime.c line 320

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+		 */
+		parent = dev->parent;
+		spin_unlock_irq(&dev->power.lock);
+
+		retval = pm_runtime_get_sync(parent);
+
+		spin_lock_irq(&dev->power.lock);
+		/* We can resume if the parent's run-time PM is disabled. */
+		if (retval < 0 && retval != -EAGAIN)
+			goto out_parent;

Instead of checking retval, how about checking the parent's PM status?

Should work.

Quoted 1 line

Also, this isn't needed if the parent is set to ignore children.

OK, I'll change that.

Quoted 11 lines

drivers/base/power/runtime.c line 439

Quoted 9 lines
drivers/base/power/runtime.c new file +9 -0
+static int __pm_request_idle(struct device *dev)
+{
+	int retval = 0;
+
+	if (dev->power.runtime_failure)
+		retval = -EINVAL;
+	else if (atomic_read(&dev->power.usage_count) > 0
+	    || dev->power.disable_depth > 0
+	    || dev->power.timer_expires > 0

This line should be removed.

Yeah, thanks!

Quoted 17 lines

...

drivers/base/power/runtime.c line 460

Quoted 13 lines
drivers/base/power/runtime.c new file +13 -0
+	if (dev->power.request_pending && dev->power.request != RPM_REQ_NONE) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
+	}
+
+	dev->power.request = RPM_REQ_IDLE;
+	if (dev->power.request_pending)
+		return retval;
+
+	dev->power.request_pending = true;
+	queue_work(pm_wq, &dev->power.work);

This should be done consistently with the other routines. Thus:

Quoted 12 lines
	if (dev->power.request_pending) {
		/* All other requests take precedence. */
		if (dev->power.request == RPM_REQ_NONE)
			dev->power.request = RPM_REQ_IDLE;
		else if (dev->power.request != RPM_REQ_IDLE)
			retval = -EAGAIN;
		return retval;
	}

	dev->power.request = RPM_REQ_IDLE;
	dev->power.request_pending = true;
	queue_work(pm_wq, &dev->power.work);

OK

Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	bool notify_parent = false;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
Quoted 8 lines

drivers/base/power/runtime.c line 738

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->power.lock, flags);
+
+	if (!dev->power.runtime_failure && !dev->power.disable_depth)
+		goto out;

Set "error" to a negative code?

OK

Quoted 10 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 8 lines
drivers/base/power/main.c +3 -1
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up during suspend. */
+			error = -EBUSY;

Or maybe "Wakeup was requested during sleep transition."

Sounds better.

Quoted 7 lines

Documentation/power/runtime_pm.txt line 219

Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5 -0
+  unsigned int deferred_resume;
+    - set if ->runtime_resume() is about to be run while ->runtime_suspend() is
+      being executed for that device and it is not practical to wait for the
+      suspend to complete; means "queue up a resume request as soon as you've
+      suspended"

"start a resume" instead of "queue up a resume request"?

OK

Quoted 5 lines
Quoted 1 line
Documentation/power/runtime_pm.txt new file +1 -0
+5. Run-time PM Initialization

...

Quoted 1 line

+If the defaul initial run-time PM status of the device (i.e. 'suspended')

Fix spelling of "default".

OK

Quoted 6 lines
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+reflects the actual state of the device, its bus type's or its driver's
+->probe() callback will likely need to wake it up using one of the PM core's
+helper functions described in Section 4.  In that case, pm_runtime_resume()
+should be used.  Of course, for this purpose the device's run-time PM has to be
+enabled earlier by calling pm_runtime_enable().
+
Quoted 10 lines

Documentation/power/runtime_pm.txt line 381

Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6 -0
+If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their
+asynchronous counterparts, they will fail returning -EAGAIN, because the
+device's usage counter is incremented by the core before executing ->probe().
+Still, it may be desirable to suspend the device as soon as ->probe() has
+finished, so the core uses pm_runtime_idle() to invoke the device bus type's
+->runtime_idle() callback at that time, which only happens even if ->probe()

s/which only happens even/but only/

Quoted 1 line

+is successful.

OK

Thanks for the comments! In fact I've already updated the patch to address them, so I'll send it in a little while.

Best,
Rafael

#15 lore raw

PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Alan Stern

Hi,

The patch below should address all of your most recent comments.

Best,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

---
 Documentation/power/runtime_pm.txt |  377 ++++++++++++++
 drivers/base/dd.c                  |   15 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   20 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       |  944 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  115 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1624 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
Index: linux-2.6/kernel/power/Kconfig
===================================================================
@@ -208,3 +208,17 @@ config APM_EMULATION
 	  random kernel OOPSes or reboots that don't seem to be related to
 	  anything, try disabling/enabling this option (or disabling/enabling
 	  APM in your BIOS).
+
+config PM_RUNTIME
+	bool "Run-time PM core functionality"
+	depends on PM
+	---help---
+	  Enable functionality allowing I/O devices to be put into energy-saving
+	  (low power) states at run time (or autosuspended) after a specified
+	  period of inactivity and woken up in response to a hardware-generated
+	  wake-up event or a driver's request.
+
+	  Hardware support is generally required for this functionality to work
+	  and the bus type drivers of the buses the devices are on are
+	  responsible for the actual handling of the autosuspend requests and
+	  wake-up events.
kernel/power/main.c +17 -0
Index: linux-2.6/kernel/power/main.c
===================================================================
@@ -11,6 +11,7 @@
 #include <linux/kobject.h>
 #include <linux/string.h>
 #include <linux/resume-trace.h>
+#include <linux/workqueue.h>
 
 #include "power.h"
 
@@ -217,8 +218,24 @@ static struct attribute_group attr_group
 	.attrs = g,
 };
 
+#ifdef CONFIG_PM_RUNTIME
+struct workqueue_struct *pm_wq;
+
+static int __init pm_start_workqueue(void)
+{
+	pm_wq = create_freezeable_workqueue("pm");
+
+	return pm_wq ? 0 : -ENOMEM;
+}
+#else
+static inline int pm_start_workqueue(void) { return 0; }
+#endif
+
 static int __init pm_init(void)
 {
+	int error = pm_start_workqueue();
+	if (error)
+		return error;
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
include/linux/pm.h +98 -3

130 lines not on this page · raw message

drivers/base/power/Makefile +1 -0
Index: linux-2.6/drivers/base/power/Makefile
===================================================================
@@ -1,5 +1,6 @@
 obj-$(CONFIG_PM)	+= sysfs.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o
+obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +944 -0

944 lines not on this page · raw message

include/linux/pm_runtime.h new file +115 -0

115 lines not on this page · raw message

drivers/base/power/main.c +19 -1
Index: linux-2.6/drivers/base/power/main.c
===================================================================
@@ -21,6 +21,7 @@
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/resume-trace.h>
 #include <linux/rwsem.h>
 #include <linux/interrupt.h>
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object being initialized.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+	pm_runtime_remove(dev);
 }
 
 /**
@@ -512,6 +524,7 @@ static void dpm_complete(pm_message_t st
 			mutex_unlock(&dpm_list_mtx);
 
 			device_complete(dev, state);
+			pm_runtime_enable(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up requested during system sleep transition. */
+			error = -EBUSY;
+		else
+			error = device_prepare(dev, state);
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
 			dev->power.status = DPM_ON;
+			pm_runtime_enable(dev);
 			if (error == -EAGAIN) {
 				put_device(dev);
 				error = 0;
drivers/base/dd.c +15 -0
Index: linux-2.6/drivers/base/dd.c
===================================================================
@@ -23,6 +23,7 @@
 #include <linux/kthread.h>
 #include <linux/wait.h>
 #include <linux/async.h>
+#include <linux/pm_runtime.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -202,7 +203,17 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	/*
+	 * Wait for run-time PM calls to complete and prevent new suspend calls
+	 * until the probe is done.
+	 */
+	pm_runtime_disable(dev);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_enable(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
+	if (!ret)
+		pm_runtime_idle(dev);
 
 	return ret;
 }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }
 
drivers/base/power/power.h +24 -7
Index: linux-2.6/drivers/base/power/power.h
===================================================================
@@ -1,7 +1,14 @@
-static inline void device_pm_init(struct device *dev)
-{
-	dev->power.status = DPM_ON;
-}
+#ifdef CONFIG_PM_RUNTIME
+
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(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) {}
+
+#endif /* !CONFIG_PM_RUNTIME */
 
 #ifdef CONFIG_PM_SLEEP
 
@@ -16,23 +23,33 @@ static inline struct device *to_device(s
 	return container_of(entry, struct device, power.entry);
 }
 
+extern void device_pm_init(struct device *dev);
 extern void device_pm_add(struct device *);
 extern void device_pm_remove(struct device *);
 extern void device_pm_move_before(struct device *, struct device *);
 extern void device_pm_move_after(struct device *, struct device *);
 extern void device_pm_move_last(struct device *);
 
-#else /* CONFIG_PM_SLEEP */
+#else /* !CONFIG_PM_SLEEP */
+
+static inline void device_pm_init(struct device *dev)
+{
+	pm_runtime_init(dev);
+}
+
+static inline void device_pm_remove(struct device *dev)
+{
+	pm_runtime_remove(dev);
+}
 
 static inline void device_pm_add(struct device *dev) {}
-static inline void device_pm_remove(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
 					 struct device *devb) {}
 static inline void device_pm_move_after(struct device *deva,
 					struct device *devb) {}
 static inline void device_pm_move_last(struct device *dev) {}
 
-#endif
+#endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM
 
Documentation/power/runtime_pm.txt new file +377 -0

377 lines not on this page · raw message

#16 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 9 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Introduce a core framework for run-time power management of I/O devices.  Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'.  Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level.  Document all these things.

Quoted 5 lines

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.

Anyway, I'm not far from an "Acked-by", but please look at the following series:

[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807

Maybe there is some code in there that you can include in v14? Let me know!

Cheers,

/ magnus

#17 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Rafael J. Wysocki
Quoted 9 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Introduce a core framework for run-time power management of I/O devices.  Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'.  Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level.  Document all these things.

Quoted 5 lines

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.

Anyway, I'm not far from an "Acked-by", but please look at the following series:

[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807

Maybe there is some code in there that you can include in v14? Let me know!

Cheers,

/ magnus

#18 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 21 lines · Magnus Damm
Quoted 9 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Quoted 5 lines

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.

Quoted 5 lines

Anyway, I'm not far from an "Acked-by", but please look at the following series:

[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807

Maybe there is some code in there that you can include in v14? Let me know!

I will.

Thanks,
Rafael

#19 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Magnus Damm
Quoted 21 lines · Magnus Damm
Quoted 9 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Quoted 5 lines

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.

Quoted 5 lines

Anyway, I'm not far from an "Acked-by", but please look at the following series:

[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807

Maybe there is some code in there that you can include in v14? Let me know!

I will.

Thanks,
Rafael

#20 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Rafael J. Wysocki
Quoted 3 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your most recent comments.

Only two comments.

Quoted 2 lines
(quoted diff) +2 -0
+static int __pm_request_idle(struct device *dev)
+{

...

drivers/base/power/runtime.c line 455

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	if (dev->power.request_pending) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_NONE)

Replace != with ==.

Quoted 8 lines
drivers/base/power/runtime.c new file +4 -0
+			dev->power.request = RPM_REQ_IDLE;
+		else if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
drivers/base/dd.c +0 -0

...

Quoted 10 lines
drivers/base/dd.c +2 -0
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)

drivers/base/dd.c static void __device_release_driver(stru · line 343

Quoted 7 lines
drivers/base/dd.c +2 -0
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Alan Stern

#21 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 9 lines · Alan Stern
Quoted 3 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your most recent comments.

Only two comments.

Quoted 8 lines
Quoted 2 lines
(quoted diff) +2 -0
+static int __pm_request_idle(struct device *dev)
+{

...

drivers/base/power/runtime.c line 455

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	if (dev->power.request_pending) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_NONE)

Replace != with ==.

Good catch, thanks!

Quoted 17 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +4 -0
+			dev->power.request = RPM_REQ_IDLE;
+		else if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
drivers/base/dd.c +0 -0

...

Quoted 8 lines
drivers/base/dd.c +2 -0
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
Quoted 14 lines

drivers/base/dd.c static void __device_release_driver(stru · line 343

Quoted 9 lines
drivers/base/dd.c +2 -0
 		if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

before and

pm_runtime_put_noidle()

after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.

At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.

Best,
Rafael

#22 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

before and

pm_runtime_put_noidle()

Quoted 7 lines

after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.

At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:

1. The device sends a wakeup request, probably in the form of an IRQ.

2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.

3. The driver calls pm_request_resume.

4. The runtime PM core carries out the resume callback.

If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

Alan Stern

#23 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Rafael J. Wysocki
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

before and

pm_runtime_put_noidle()

Quoted 7 lines

after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.

At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:

1. The device sends a wakeup request, probably in the form of an IRQ.

2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.

3. The driver calls pm_request_resume.

4. The runtime PM core carries out the resume callback.

If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

Alan Stern

#24 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 36 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

before and

pm_runtime_put_noidle()

Quoted 7 lines

after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.

At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

Quoted 5 lines

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

Isn't it what's done in rev. 14?

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

is exactly a barrier like this. How exactly would you like to implement it instead?

Quoted 10 lines

We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:

1. The device sends a wakeup request, probably in the form of an IRQ.

2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.

Quoted 8 lines

3. The driver calls pm_request_resume.

4. The runtime PM core carries out the resume callback.

If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.

The idea is that once the system sleep transition has started, the non-runtime callbacks are in charge of handling the device.

Quoted 2 lines

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.

Best,
Rafael

#25 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

Quoted 5 lines

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

Quoted 7 lines

Isn't it what's done in rev. 14?

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

is exactly a barrier like this.

It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.

Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

Quoted 7 lines
Quoted 2 lines

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.

All right. Since it involves a race, the problem may not show up for a while.

Alan Stern

#26 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Rafael J. Wysocki
Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

Quoted 5 lines

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

Quoted 7 lines

Isn't it what's done in rev. 14?

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

is exactly a barrier like this.

It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.

Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

Quoted 7 lines
Quoted 2 lines

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.

All right. Since it involves a race, the problem may not show up for a while.

Alan Stern

#27 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 29 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

Quoted 5 lines

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

Quoted 7 lines

Isn't it what's done in rev. 14?

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

is exactly a barrier like this.

It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.

Quoted 6 lines
Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.

Quoted 10 lines
Quoted 7 lines
Quoted 2 lines

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.

All right. Since it involves a race, the problem may not show up for a while.

Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.

Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.

Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.

Best,
Rafael

#28 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.

Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.

Quoted 4 lines

Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.

The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.

Quoted 3 lines

Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.

It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.

Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.

Quoted 4 lines

Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.

If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.

In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.

The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet. For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.

Alan Stern

#29 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Rafael J. Wysocki
Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.

Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.

Quoted 4 lines

Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.

The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.

Quoted 3 lines

Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.

It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.

Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.

Quoted 4 lines

Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.

If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.

In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.

The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet. For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.

Alan Stern

#30 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Quoted 22 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.

Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.

Quoted 4 lines

Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.

Quoted 4 lines

The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.

Well, I'm not sure if that's a good idea, but also I have no good techincal arguments against it at the moment. And I'm too tired to argue. ;-)

Quoted 16 lines
Quoted 3 lines

Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.

It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.

Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.

Quoted 4 lines

Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.

Quoted 5 lines

If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.

We also should preserve the requests in case the system sleep transition fails.

Quoted 5 lines

In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.

The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet.

Agreed.

Quoted 3 lines

For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.

Yes, for now we have to assume that any device with wakeup enabled is a wakeup device.

OK, I'll post the new version of the patch shortly. Please check if the barrier mechanism is implemeted and used correctly.

Best,
Rafael

#31 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Alan Stern
Quoted 22 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.

Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.

Quoted 4 lines

Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.

Quoted 4 lines

The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.

Well, I'm not sure if that's a good idea, but also I have no good techincal arguments against it at the moment. And I'm too tired to argue. ;-)

Quoted 16 lines
Quoted 3 lines

Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.

It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.

Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.

Quoted 4 lines

Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.

Quoted 5 lines

If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.

We also should preserve the requests in case the system sleep transition fails.

Quoted 5 lines

In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.

The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet.

Agreed.

Quoted 3 lines

For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.

Yes, for now we have to assume that any device with wakeup enabled is a wakeup device.

OK, I'll post the new version of the patch shortly. Please check if the barrier mechanism is implemeted and used correctly.

Best,
Rafael

#32 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Alan Stern
Quoted 29 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

Quoted 5 lines

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

Quoted 7 lines

Isn't it what's done in rev. 14?

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

is exactly a barrier like this.

It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.

Quoted 6 lines
Quoted 2 lines

How exactly would you like to implement it
instead?

As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.

OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.

Quoted 10 lines
Quoted 7 lines
Quoted 2 lines

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.

All right. Since it involves a race, the problem may not show up for a while.

Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.

Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.

Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.

Best,
Rafael

#33 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Alan Stern
Quoted 36 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

before and

pm_runtime_put_noidle()

Quoted 7 lines

after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.

At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.

The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.

The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.

Quoted 5 lines

So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.

Isn't it what's done in rev. 14?

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

is exactly a barrier like this. How exactly would you like to implement it instead?

Quoted 10 lines

We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:

1. The device sends a wakeup request, probably in the form of an IRQ.

2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.

Quoted 8 lines

3. The driver calls pm_request_resume.

4. The runtime PM core carries out the resume callback.

If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.

The idea is that once the system sleep transition has started, the non-runtime callbacks are in charge of handling the device.

Quoted 2 lines

Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.

I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.

Best,
Rafael

#34 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Alan Stern
Quoted 9 lines · Alan Stern
Quoted 3 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your most recent comments.

Only two comments.

Quoted 8 lines
Quoted 2 lines
(quoted diff) +2 -0
+static int __pm_request_idle(struct device *dev)
+{

...

drivers/base/power/runtime.c line 455

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	if (dev->power.request_pending) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_NONE)

Replace != with ==.

Good catch, thanks!

Quoted 17 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +4 -0
+			dev->power.request = RPM_REQ_IDLE;
+		else if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
drivers/base/dd.c +0 -0

...

Quoted 8 lines
drivers/base/dd.c +2 -0
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
Quoted 14 lines

drivers/base/dd.c static void __device_release_driver(stru · line 343

Quoted 9 lines
drivers/base/dd.c +2 -0
 		if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call

pm_runtime_disable(dev);
pm_runtime_get_noresume(dev);
pm_runtime_enable(dev);

before and

pm_runtime_put_noidle()

after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.

At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.

Best,
Rafael

#35 lore raw

Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Rafael J. Wysocki
Quoted 3 lines · Rafael J. Wysocki

Hi,

The patch below should address all of your most recent comments.

Only two comments.

Quoted 2 lines
(quoted diff) +2 -0
+static int __pm_request_idle(struct device *dev)
+{

...

drivers/base/power/runtime.c line 455

Quoted 3 lines
drivers/base/power/runtime.c new file +3 -0
+	if (dev->power.request_pending) {
+		/* Any requests other then RPM_REQ_IDLE take precedence. */
+		if (dev->power.request != RPM_REQ_NONE)

Replace != with ==.

Quoted 8 lines
drivers/base/power/runtime.c new file +4 -0
+			dev->power.request = RPM_REQ_IDLE;
+		else if (dev->power.request != RPM_REQ_IDLE)
+			retval = -EAGAIN;
+		return retval;
drivers/base/dd.c +0 -0

...

Quoted 10 lines
drivers/base/dd.c +2 -0
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)

drivers/base/dd.c static void __device_release_driver(stru · line 343

Quoted 7 lines
drivers/base/dd.c +2 -0
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }

We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.

Alan Stern

#36 lore raw

PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

In reply to Alan Stern

Hi,

The patch below should address all of your most recent comments.

Best,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

---
 Documentation/power/runtime_pm.txt |  377 ++++++++++++++
 drivers/base/dd.c                  |   15 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   20 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       |  944 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  115 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1624 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
Index: linux-2.6/kernel/power/Kconfig
===================================================================
@@ -208,3 +208,17 @@ config APM_EMULATION
 	  random kernel OOPSes or reboots that don't seem to be related to
 	  anything, try disabling/enabling this option (or disabling/enabling
 	  APM in your BIOS).
+
+config PM_RUNTIME
+	bool "Run-time PM core functionality"
+	depends on PM
+	---help---
+	  Enable functionality allowing I/O devices to be put into energy-saving
+	  (low power) states at run time (or autosuspended) after a specified
+	  period of inactivity and woken up in response to a hardware-generated
+	  wake-up event or a driver's request.
+
+	  Hardware support is generally required for this functionality to work
+	  and the bus type drivers of the buses the devices are on are
+	  responsible for the actual handling of the autosuspend requests and
+	  wake-up events.
kernel/power/main.c +17 -0
Index: linux-2.6/kernel/power/main.c
===================================================================
@@ -11,6 +11,7 @@
 #include <linux/kobject.h>
 #include <linux/string.h>
 #include <linux/resume-trace.h>
+#include <linux/workqueue.h>
 
 #include "power.h"
 
@@ -217,8 +218,24 @@ static struct attribute_group attr_group
 	.attrs = g,
 };
 
+#ifdef CONFIG_PM_RUNTIME
+struct workqueue_struct *pm_wq;
+
+static int __init pm_start_workqueue(void)
+{
+	pm_wq = create_freezeable_workqueue("pm");
+
+	return pm_wq ? 0 : -ENOMEM;
+}
+#else
+static inline int pm_start_workqueue(void) { return 0; }
+#endif
+
 static int __init pm_init(void)
 {
+	int error = pm_start_workqueue();
+	if (error)
+		return error;
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
include/linux/pm.h +98 -3

130 lines not on this page · raw message

drivers/base/power/Makefile +1 -0
Index: linux-2.6/drivers/base/power/Makefile
===================================================================
@@ -1,5 +1,6 @@
 obj-$(CONFIG_PM)	+= sysfs.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o
+obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +944 -0

944 lines not on this page · raw message

include/linux/pm_runtime.h new file +115 -0

115 lines not on this page · raw message

drivers/base/power/main.c +19 -1
Index: linux-2.6/drivers/base/power/main.c
===================================================================
@@ -21,6 +21,7 @@
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/resume-trace.h>
 #include <linux/rwsem.h>
 #include <linux/interrupt.h>
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object being initialized.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+	pm_runtime_remove(dev);
 }
 
 /**
@@ -512,6 +524,7 @@ static void dpm_complete(pm_message_t st
 			mutex_unlock(&dpm_list_mtx);
 
 			device_complete(dev, state);
+			pm_runtime_enable(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up requested during system sleep transition. */
+			error = -EBUSY;
+		else
+			error = device_prepare(dev, state);
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
 			dev->power.status = DPM_ON;
+			pm_runtime_enable(dev);
 			if (error == -EAGAIN) {
 				put_device(dev);
 				error = 0;
drivers/base/dd.c +15 -0
Index: linux-2.6/drivers/base/dd.c
===================================================================
@@ -23,6 +23,7 @@
 #include <linux/kthread.h>
 #include <linux/wait.h>
 #include <linux/async.h>
+#include <linux/pm_runtime.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -202,7 +203,17 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	/*
+	 * Wait for run-time PM calls to complete and prevent new suspend calls
+	 * until the probe is done.
+	 */
+	pm_runtime_disable(dev);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_enable(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
+	if (!ret)
+		pm_runtime_idle(dev);
 
 	return ret;
 }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }
 
drivers/base/power/power.h +24 -7
Index: linux-2.6/drivers/base/power/power.h
===================================================================
@@ -1,7 +1,14 @@
-static inline void device_pm_init(struct device *dev)
-{
-	dev->power.status = DPM_ON;
-}
+#ifdef CONFIG_PM_RUNTIME
+
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(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) {}
+
+#endif /* !CONFIG_PM_RUNTIME */
 
 #ifdef CONFIG_PM_SLEEP
 
@@ -16,23 +23,33 @@ static inline struct device *to_device(s
 	return container_of(entry, struct device, power.entry);
 }
 
+extern void device_pm_init(struct device *dev);
 extern void device_pm_add(struct device *);
 extern void device_pm_remove(struct device *);
 extern void device_pm_move_before(struct device *, struct device *);
 extern void device_pm_move_after(struct device *, struct device *);
 extern void device_pm_move_last(struct device *);
 
-#else /* CONFIG_PM_SLEEP */
+#else /* !CONFIG_PM_SLEEP */
+
+static inline void device_pm_init(struct device *dev)
+{
+	pm_runtime_init(dev);
+}
+
+static inline void device_pm_remove(struct device *dev)
+{
+	pm_runtime_remove(dev);
+}
 
 static inline void device_pm_add(struct device *dev) {}
-static inline void device_pm_remove(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
 					 struct device *devb) {}
 static inline void device_pm_move_after(struct device *deva,
 					struct device *devb) {}
 static inline void device_pm_move_last(struct device *dev) {}
 
-#endif
+#endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM
 
Documentation/power/runtime_pm.txt new file +377 -0

377 lines not on this page · raw message

#37 lore raw

PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

In reply to Rafael J. Wysocki

Hi,

The patch below should address all of your recent comments.

Additionally I changed a few bits that I thought could turn out to be problematic at one point.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 12)

Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.

Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.

---
 Documentation/power/runtime_pm.txt |  382 +++++++++++++++
 drivers/base/dd.c                  |   15 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   20 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       |  939 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  102 +++-
 include/linux/pm_runtime.h         |  115 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1625 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
Index: linux-2.6/kernel/power/Kconfig
===================================================================
@@ -208,3 +208,17 @@ config APM_EMULATION
 	  random kernel OOPSes or reboots that don't seem to be related to
 	  anything, try disabling/enabling this option (or disabling/enabling
 	  APM in your BIOS).
+
+config PM_RUNTIME
+	bool "Run-time PM core functionality"
+	depends on PM
+	---help---
+	  Enable functionality allowing I/O devices to be put into energy-saving
+	  (low power) states at run time (or autosuspended) after a specified
+	  period of inactivity and woken up in response to a hardware-generated
+	  wake-up event or a driver's request.
+
+	  Hardware support is generally required for this functionality to work
+	  and the bus type drivers of the buses the devices are on are
+	  responsible for the actual handling of the autosuspend requests and
+	  wake-up events.
kernel/power/main.c +17 -0
Index: linux-2.6/kernel/power/main.c
===================================================================
@@ -11,6 +11,7 @@
 #include <linux/kobject.h>
 #include <linux/string.h>
 #include <linux/resume-trace.h>
+#include <linux/workqueue.h>
 
 #include "power.h"
 
@@ -217,8 +218,24 @@ static struct attribute_group attr_group
 	.attrs = g,
 };
 
+#ifdef CONFIG_PM_RUNTIME
+struct workqueue_struct *pm_wq;
+
+static int __init pm_start_workqueue(void)
+{
+	pm_wq = create_freezeable_workqueue("pm");
+
+	return pm_wq ? 0 : -ENOMEM;
+}
+#else
+static inline int pm_start_workqueue(void) { return 0; }
+#endif
+
 static int __init pm_init(void)
 {
+	int error = pm_start_workqueue();
+	if (error)
+		return error;
 	power_kobj = kobject_create_and_add("power", NULL);
 	if (!power_kobj)
 		return -ENOMEM;
include/linux/pm.h +99 -3

131 lines not on this page · raw message

drivers/base/power/Makefile +1 -0
Index: linux-2.6/drivers/base/power/Makefile
===================================================================
@@ -1,5 +1,6 @@
 obj-$(CONFIG_PM)	+= sysfs.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o
+obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +939 -0

939 lines not on this page · raw message

include/linux/pm_runtime.h new file +115 -0

115 lines not on this page · raw message

drivers/base/power/main.c +19 -1
Index: linux-2.6/drivers/base/power/main.c
===================================================================
@@ -21,6 +21,7 @@
 #include <linux/kallsyms.h>
 #include <linux/mutex.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/resume-trace.h>
 #include <linux/rwsem.h>
 #include <linux/interrupt.h>
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static bool transition_started;
 
 /**
+ * device_pm_init - Initialize the PM-related part of a device object
+ * @dev: Device object being initialized.
+ */
+void device_pm_init(struct device *dev)
+{
+	dev->power.status = DPM_ON;
+	pm_runtime_init(dev);
+}
+
+/**
  *	device_pm_lock - lock the list of active devices used by the PM core
  */
 void device_pm_lock(void)
@@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
 	mutex_unlock(&dpm_list_mtx);
+	pm_runtime_remove(dev);
 }
 
 /**
@@ -512,6 +524,7 @@ static void dpm_complete(pm_message_t st
 			mutex_unlock(&dpm_list_mtx);
 
 			device_complete(dev, state);
+			pm_runtime_enable(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			/* Wake-up during suspend. */
+			error = -EBUSY;
+		else
+			error = device_prepare(dev, state);
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
 			dev->power.status = DPM_ON;
+			pm_runtime_enable(dev);
 			if (error == -EAGAIN) {
 				put_device(dev);
 				error = 0;
drivers/base/dd.c +15 -0
Index: linux-2.6/drivers/base/dd.c
===================================================================
@@ -23,6 +23,7 @@
 #include <linux/kthread.h>
 #include <linux/wait.h>
 #include <linux/async.h>
+#include <linux/pm_runtime.h>
 
 #include "base.h"
 #include "power/power.h"
@@ -202,7 +203,17 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	/*
+	 * Wait for run-time PM calls to complete and prevent new suspend calls
+	 * until the probe is done.
+	 */
+	pm_runtime_disable(dev);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_enable(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
+	if (!ret)
+		pm_runtime_idle(dev);
 
 	return ret;
 }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_disable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_enable(dev);
 	}
 }
 
drivers/base/power/power.h +24 -7
Index: linux-2.6/drivers/base/power/power.h
===================================================================
@@ -1,7 +1,14 @@
-static inline void device_pm_init(struct device *dev)
-{
-	dev->power.status = DPM_ON;
-}
+#ifdef CONFIG_PM_RUNTIME
+
+extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_remove(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) {}
+
+#endif /* !CONFIG_PM_RUNTIME */
 
 #ifdef CONFIG_PM_SLEEP
 
@@ -16,23 +23,33 @@ static inline struct device *to_device(s
 	return container_of(entry, struct device, power.entry);
 }
 
+extern void device_pm_init(struct device *dev);
 extern void device_pm_add(struct device *);
 extern void device_pm_remove(struct device *);
 extern void device_pm_move_before(struct device *, struct device *);
 extern void device_pm_move_after(struct device *, struct device *);
 extern void device_pm_move_last(struct device *);
 
-#else /* CONFIG_PM_SLEEP */
+#else /* !CONFIG_PM_SLEEP */
+
+static inline void device_pm_init(struct device *dev)
+{
+	pm_runtime_init(dev);
+}
+
+static inline void device_pm_remove(struct device *dev)
+{
+	pm_runtime_remove(dev);
+}
 
 static inline void device_pm_add(struct device *dev) {}
-static inline void device_pm_remove(struct device *dev) {}
 static inline void device_pm_move_before(struct device *deva,
 					 struct device *devb) {}
 static inline void device_pm_move_after(struct device *deva,
 					struct device *devb) {}
 static inline void device_pm_move_last(struct device *dev) {}
 
-#endif
+#endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM
 
Documentation/power/runtime_pm.txt new file +382 -0

382 lines not on this page · raw message

#38 lore raw
In reply to Alan Stern
Quoted 15 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines

drivers/base/power/runtime.c line 85

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+	spin_unlock_irq(&dev->power.lock);
+
+	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle)
+		dev->bus->pm->runtime_idle(dev);
+
+	spin_lock_irq(&dev->power.lock);

Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.

Quoted 2 lines

Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.

Quoted 7 lines

No, I meant do this:

	if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
		spin_unlock_irq(&dev->power.lock);
		dev->bus->pm->runtime_idle(dev);
		spin_lock_irq(&dev->power.lock);
	}

Ah, OK. That makes sense.

Quoted 2 lines

By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do,

Well, I guess I should. :-)

Quoted 41 lines

you should add "__releases(&dev->power.lock)"
and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.

Quoted 3 lines
Quoted 1 line

The same thing can be done in other places.

I'm not really sure it can.

The other places aren't quite the same as this. I'll leave it to your discretion. :-)

Quoted 11 lines
Quoted 10 lines

drivers/base/power/runtime.c line 710

Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+int __pm_runtime_set_status(struct device *dev, unsigned int status)
+{
+	struct device *parent = dev->parent;
+	unsigned long flags;
+	int error = 0;
+
+	if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
+		return -EINVAL;

This should go inside the spinlocked area.

Quoted 2 lines

Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.

Oops, my mistake. Never mind...

Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2 -0
+int pm_runtime_disable(struct device *dev)
+{

...

drivers/base/power/runtime.c line 829

Quoted 5 lines
drivers/base/power/runtime.c new file +5 -0
+	if (dev->power.disable_depth++ > 0)
+		goto out;
+
+	if (dev->power.runtime_failure)
+		goto out;

I don't see why this is needed.

Quoted 5 lines
Quoted 1 line

If dev->power.runtime_failure, there's no need to do anything more.

Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...

OK, I'll drop that if ().

Quoted 20 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8 -0
+void pm_runtime_remove(struct device *dev)
+{
+	pm_runtime_disable(dev);
+
+	if (dev->power.runtime_status == RPM_ACTIVE) {
+		struct device *parent = dev->parent;
+
+		/*
Quoted 8 lines

drivers/base/power/runtime.c line 944

Quoted 6 lines
drivers/base/power/runtime.c new file +6 -0
+		 * Change the status back to 'suspended' to match the initial
+		 * status.
+		 */
+		pm_runtime_set_suspended(dev);
+		if (parent && !parent->power.ignore_children)
+			pm_request_idle(parent);

Shouldn't these last two lines be part of __pm_runtime_set_status()?

Quoted 2 lines

No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.

Quoted 6 lines

Ah, an interesting point. Suppose the device is in RPM_ACTIVE, and then someone calls pm_runtime_disable followed by pm_runtime_set_suspended. Then the device's status would change to RPM_SUSPENDED and the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?

OK, having reconsidered that, I think you're right, it should go into __pm_runtime_set_status().

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775

Quoted 7 lines
drivers/base/power/main.c +2 -1
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		if (pm_runtime_disable(dev) && device_may_wakeup(dev))
+			error = -EBUSY;

What's the reason for the -EBUSY error?

Quoted 8 lines
Quoted 3 lines

If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).

I see. That's a little obscure; a comment would help. Even something as simple as:

error = -EBUSY; /* wake-up during suspend */

OK

Quoted 20 lines
Quoted 12 lines
Quoted 11 lines

drivers/base/dd.c int driver_probe_device(struct device_dr · line 208

Quoted 7 lines
drivers/base/dd.c +2 -0
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_dr
 	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+	pm_runtime_get_noresume(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);

This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend.

Quoted 1 line

I'm not sure how that's possible, but whatever.

Suppose the probe routine does:

	pm_runtime_get_sync(dev);
	/* do some work */
	pm_runtime_put_sync(dev);
Quoted 8 lines

There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by

	pm_runtime_get_noresume(dev);
	... probe ...
	pm_runtime_put_noidle(dev);

then there won't be any idle notifications.

Ah, OK. So the point is that we should always idle-notify after .probe(), because that's what .probe() would most probably want to do. I guess that makes sense.

Quoted 11 lines
Quoted 10 lines
Quoted 7 lines

Documentation/power/runtime_pm.txt

Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3 -0
+2. Device Run-time PM Callbacks
 
+In particular, it is recommended that ->runtime_suspend() return -EBUSY if
+device_may_wakeup() returns 'false' for the device.

What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.

device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.

Quoted 8 lines

Okay. This needs to be rephrased. For example,

In particular, if the driver requires remote wakeup capability for proper functioning and device_may_wakeup() returns 'false' for the device, then ->runtime_suspend() should return -EBUSY.

The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check device_may_wakeup().

OK

Thanks a lot again, I'll do my best to address the comments in the next version of the patch.

Best,
Rafael