Skip to contentSkip to conversation

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

PATCH New Rafael J. Wysocki 8 participants 88 messages Aug 8, 2009 – Aug 16, 2009

Subsystems (9)
USB ACPI PCI Driver core +5 more subsystems
lore
Share & tools
lore
#0 lore raw

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

Hi,

Below is the rev. 14 of the runtime PM patch. Hopefully it addresses all of the remaining reservations.

Thanks,
Rafael

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

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 |  386 ++++++++++++++
 drivers/base/dd.c                  |   21 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   20 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       |  969 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  115 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1664 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 +969 -0

969 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 +21 -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,14 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		/*
+		 * Wait for run-time PM calls to complete and prevent new
+		 * suspend calls until the remove is done.
+		 */
+		pm_runtime_disable(dev);
+		pm_runtime_get_noresume(dev);
+		pm_runtime_enable(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +343,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_noidle(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 +386 -0

386 lines not on this page · raw message

#1 lore raw

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

Hi,

One more update. This one should address your comments from this thread
http://lkml.org/lkml/2009/8/8/113

Thanks,
Rafael

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

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 |  386 ++++++++++++++
 drivers/base/dd.c                  |   11 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   22 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       | 1011 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  114 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1697 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 +1011 -0

1,011 lines not on this page · raw message

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

114 lines not on this page · raw message

drivers/base/power/main.c +21 -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_put_noidle(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		pm_runtime_get_noresume(dev);
+		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
+			/* Wake-up requested during system sleep transition. */
+			pm_runtime_put_noidle(dev);
+			error = -EBUSY;
+		} else {
+			error = device_prepare(dev, state);
+		}
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
drivers/base/dd.c +11 -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,12 @@ 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);
+	pm_runtime_barrier(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
+	if (!ret)
+		pm_runtime_idle(dev);
 
 	return ret;
 }
@@ -306,6 +312,9 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_get_noresume(dev);
+		pm_runtime_barrier(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_noidle(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 +386 -0

386 lines not on this page · raw message

#2 lore raw

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

Hi Rafael,

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. 15)

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.

Looking good! I have a few nitpicks below, but from a functional perspective it's all good. I've tested v15 with platform device drivers for I2C, UIO and framebuffer. Before adding my "Acked-by" I also want to test the V4L capture driver, but I need to wait a few days until I can get my hands on such a hardware platform.

Thanks for folding in and fixing up the debug patch. I was able to drop most remaining patches thanks to feedback from Alan. So the only needed patch apart from this one (and the ones in your linux-next branch) is the one in this micro-series: "PM: Runtime PM v15 for Platform Devices 20090812".

Quoted 2 lines
include/linux/pm.h +0 -0

[..]

Quoted 11 lines

 struct dev_pm_info {
       pm_message_t            power_state;
-       unsigned                can_wakeup:1;
-       unsigned                should_wakeup:1;
+       unsigned int            can_wakeup:1;
+       unsigned int            should_wakeup:1;
       enum dpm_state          status;         /* Owned by the PM core */

include/linux/pm.h +1 -1
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP

       struct list_head        entry;
 #endif

Quoted 8 lines

+#ifdef CONFIG_PM_RUNTIME
+       struct timer_list       suspend_timer;
+       unsigned long           timer_expires;
+       struct work_struct      work;
+       wait_queue_head_t       wait_queue;
+       spinlock_t              lock;
+       atomic_t                usage_count;
+       atomic_t                child_count;

I suppose child_count has to be atomic?

Quoted 2 lines
drivers/base/power/runtime.c new file +0 -0

[...]

Quoted 2 lines

+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+       __releases(&dev->power.lock) __acquires(&dev->power.lock)

[...]

drivers/base/power/runtime.c line 202

Quoted 10 lines

+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
+               spin_unlock_irq(&dev->power.lock);

drivers/base/power/runtime.c new file +8 -0
+
+               retval = dev->bus->pm->runtime_suspend(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Nit: { and } above do not follow the regular coding style.

Quoted 2 lines

+int __pm_runtime_resume(struct device *dev, bool from_wq)
+       __releases(&dev->power.lock) __acquires(&dev->power.lock)

[...]

drivers/base/power/runtime.c line 369

Quoted 10 lines

+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) {
+               spin_unlock_irq(&dev->power.lock);

drivers/base/power/runtime.c new file +8 -0
+
+               retval = dev->bus->pm->runtime_resume(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Same minor issue here.

Apart from that all is fine. Thank you.

/ magnus

#3 lore raw

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

In reply to Rafael J. Wysocki

Hi Rafael,

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. 15)

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.

Looking good! I have a few nitpicks below, but from a functional perspective it's all good. I've tested v15 with platform device drivers for I2C, UIO and framebuffer. Before adding my "Acked-by" I also want to test the V4L capture driver, but I need to wait a few days until I can get my hands on such a hardware platform.

Thanks for folding in and fixing up the debug patch. I was able to drop most remaining patches thanks to feedback from Alan. So the only needed patch apart from this one (and the ones in your linux-next branch) is the one in this micro-series: "PM: Runtime PM v15 for Platform Devices 20090812".

Quoted 2 lines
include/linux/pm.h +0 -0

[..]

Quoted 11 lines

 struct dev_pm_info {
       pm_message_t            power_state;
-       unsigned                can_wakeup:1;
-       unsigned                should_wakeup:1;
+       unsigned int            can_wakeup:1;
+       unsigned int            should_wakeup:1;
       enum dpm_state          status;         /* Owned by the PM core */

include/linux/pm.h +1 -1
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP

       struct list_head        entry;
 #endif

Quoted 8 lines

+#ifdef CONFIG_PM_RUNTIME
+       struct timer_list       suspend_timer;
+       unsigned long           timer_expires;
+       struct work_struct      work;
+       wait_queue_head_t       wait_queue;
+       spinlock_t              lock;
+       atomic_t                usage_count;
+       atomic_t                child_count;

I suppose child_count has to be atomic?

Quoted 2 lines
drivers/base/power/runtime.c new file +0 -0

[...]

Quoted 2 lines

+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+       __releases(&dev->power.lock) __acquires(&dev->power.lock)

[...]

drivers/base/power/runtime.c line 202

Quoted 10 lines

+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
+               spin_unlock_irq(&dev->power.lock);

drivers/base/power/runtime.c new file +8 -0
+
+               retval = dev->bus->pm->runtime_suspend(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Nit: { and } above do not follow the regular coding style.

Quoted 2 lines

+int __pm_runtime_resume(struct device *dev, bool from_wq)
+       __releases(&dev->power.lock) __acquires(&dev->power.lock)

[...]

drivers/base/power/runtime.c line 369

Quoted 10 lines

+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) {
+               spin_unlock_irq(&dev->power.lock);

drivers/base/power/runtime.c new file +8 -0
+
+               retval = dev->bus->pm->runtime_resume(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Same minor issue here.

Apart from that all is fine. Thank you.

/ magnus

maintainer
#4 lore raw

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

Quoted 6 lines · Magnus Damm
Quoted 2 lines
drivers/base/power/runtime.c new file +0 -0

[...]

Quoted 2 lines

+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+       __releases(&dev->power.lock) __acquires(&dev->power.lock)

[...]

Quoted 12 lines

drivers/base/power/runtime.c line 202

Quoted 10 lines

+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
+               spin_unlock_irq(&dev->power.lock);

drivers/base/power/runtime.c new file +8 -0
+
+               retval = dev->bus->pm->runtime_suspend(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Nit: { and } above do not follow the regular coding style.

As a matter of fact they do. From Documentation/CodingStyle:

Do not unnecessarily use braces where a single statement will do.

if (condition)

	action();

This does not apply if one branch of a conditional statement is a single statement. Use braces in both branches.

if (condition) {
	do_this();
	do_that();
} else {
	otherwise();
}

Alan Stern

maintainer
#5 lore raw

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

In reply to Magnus Damm
Quoted 6 lines · Magnus Damm
Quoted 2 lines
drivers/base/power/runtime.c new file +0 -0

[...]

Quoted 2 lines

+int __pm_runtime_suspend(struct device *dev, bool from_wq)
+       __releases(&dev->power.lock) __acquires(&dev->power.lock)

[...]

Quoted 12 lines

drivers/base/power/runtime.c line 202

Quoted 10 lines

+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
+               spin_unlock_irq(&dev->power.lock);

drivers/base/power/runtime.c new file +8 -0
+
+               retval = dev->bus->pm->runtime_suspend(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Nit: { and } above do not follow the regular coding style.

As a matter of fact they do. From Documentation/CodingStyle:

Do not unnecessarily use braces where a single statement will do.

if (condition)

	action();

This does not apply if one branch of a conditional statement is a single statement. Use braces in both branches.

if (condition) {
	do_this();
	do_that();
} else {
	otherwise();
}

Alan Stern

#6 lore raw

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

In reply to Magnus Damm
Quoted 1 line · Magnus Damm

Hi Rafael,

Hi,

Quoted 44 lines · Rafael J. Wysocki
Quoted 9 lines

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

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.

Looking good! I have a few nitpicks below, but from a functional perspective it's all good. I've tested v15 with platform device drivers for I2C, UIO and framebuffer. Before adding my "Acked-by" I also want to test the V4L capture driver, but I need to wait a few days until I can get my hands on such a hardware platform.

Thanks for folding in and fixing up the debug patch. I was able to drop most remaining patches thanks to feedback from Alan. So the only needed patch apart from this one (and the ones in your linux-next branch) is the one in this micro-series: "PM: Runtime PM v15 for Platform Devices 20090812".

Quoted 2 lines
include/linux/pm.h +0 -0

[..]

Quoted 13 lines
 struct dev_pm_info {
       pm_message_t            power_state;
include/linux/pm.h +5 -3
-       unsigned                can_wakeup:1;
-       unsigned                should_wakeup:1;
+       unsigned int            can_wakeup:1;
+       unsigned int            should_wakeup:1;
       enum dpm_state          status;         /* Owned by the PM core */
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
       struct list_head        entry;
 #endif
+#ifdef CONFIG_PM_RUNTIME
+       struct timer_list       suspend_timer;
Quoted 8 lines

include/linux/pm.h enum dpm_state { · line 425

Quoted 6 lines
include/linux/pm.h +6 -0
+       unsigned long           timer_expires;
+       struct work_struct      work;
+       wait_queue_head_t       wait_queue;
+       spinlock_t              lock;
+       atomic_t                usage_count;
+       atomic_t                child_count;

I suppose child_count has to be atomic?

I'd say so, it's modified in a few places without locking.

Quoted 6 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +0 -0

[...]

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

[...]

Quoted 12 lines

drivers/base/power/runtime.c line 202

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
+               spin_unlock_irq(&dev->power.lock);
+
+               retval = dev->bus->pm->runtime_suspend(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Nit: { and } above do not follow the regular coding style.

Well, you've got a very good answer to this from Alan. ;-)

Thanks,
Rafael

#7 lore raw

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

In reply to Magnus Damm
Quoted 1 line · Magnus Damm

Hi Rafael,

Hi,

Quoted 44 lines · Rafael J. Wysocki
Quoted 9 lines

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

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.

Looking good! I have a few nitpicks below, but from a functional perspective it's all good. I've tested v15 with platform device drivers for I2C, UIO and framebuffer. Before adding my "Acked-by" I also want to test the V4L capture driver, but I need to wait a few days until I can get my hands on such a hardware platform.

Thanks for folding in and fixing up the debug patch. I was able to drop most remaining patches thanks to feedback from Alan. So the only needed patch apart from this one (and the ones in your linux-next branch) is the one in this micro-series: "PM: Runtime PM v15 for Platform Devices 20090812".

Quoted 2 lines
include/linux/pm.h +0 -0

[..]

Quoted 13 lines
 struct dev_pm_info {
       pm_message_t            power_state;
include/linux/pm.h +5 -3
-       unsigned                can_wakeup:1;
-       unsigned                should_wakeup:1;
+       unsigned int            can_wakeup:1;
+       unsigned int            should_wakeup:1;
       enum dpm_state          status;         /* Owned by the PM core */
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
       struct list_head        entry;
 #endif
+#ifdef CONFIG_PM_RUNTIME
+       struct timer_list       suspend_timer;
Quoted 8 lines

include/linux/pm.h enum dpm_state { · line 425

Quoted 6 lines
include/linux/pm.h +6 -0
+       unsigned long           timer_expires;
+       struct work_struct      work;
+       wait_queue_head_t       wait_queue;
+       spinlock_t              lock;
+       atomic_t                usage_count;
+       atomic_t                child_count;

I suppose child_count has to be atomic?

I'd say so, it's modified in a few places without locking.

Quoted 6 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +0 -0

[...]

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

[...]

Quoted 12 lines

drivers/base/power/runtime.c line 202

Quoted 10 lines
drivers/base/power/runtime.c new file +10 -0
+       if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
+               spin_unlock_irq(&dev->power.lock);
+
+               retval = dev->bus->pm->runtime_suspend(dev);
+
+               spin_lock_irq(&dev->power.lock);
+               dev->power.runtime_error = retval;
+       } else {
+               retval = -ENOSYS;
+       }

Nit: { and } above do not follow the regular coding style.

Well, you've got a very good answer to this from Alan. ;-)

Thanks,
Rafael

#8 lore raw

RFC 1/1 PCI: Runtime power management

In reply to Rafael J. Wysocki

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

---
 drivers/pci/pci-acpi.c   |   55 +++++++++++++++++++++++++
 drivers/pci/pci-driver.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.c        |   87 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.h        |    3 +
 include/linux/pci.h      |    3 +
 5 files changed, 248 insertions(+), 0 deletions(-)
drivers/pci/pci-acpi.c +55 -0
index ea15b05..a98a777 100644
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/pci-aspm.h>
+#include <linux/pm_runtime.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 
@@ -120,14 +121,62 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
 	return error;
 }
 
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+
+	if (!handle)
+		return -ENODEV;
+
+	status = acpi_bus_get_device(handle, &acpi_dev);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	if (enable) {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE_RUN);
+		acpi_enable_gpe(acpi_dev->wakeup.gpe_device,
+				acpi_dev->wakeup.gpe_number);
+	} else {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE);
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}
+
+
 static struct pci_platform_pm_ops acpi_pci_platform_pm = {
 	.is_manageable = acpi_pci_power_manageable,
 	.set_state = acpi_pci_set_power_state,
 	.choose_state = acpi_pci_choose_state,
 	.can_wakeup = acpi_pci_can_wakeup,
 	.sleep_wake = acpi_pci_sleep_wake,
+	.runtime_wake = acpi_pci_runtime_wake,
 };
 
+static void pci_device_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pm_runtime_resume(dev);
+}
+
+static void pci_root_bridge_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pci_bus_pme_event(pci_dev);
+}
+
 /* ACPI bus type */
 static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 {
@@ -140,6 +189,9 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_device_notify, dev);
 	return 0;
 }
 
@@ -158,6 +210,9 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_pci_rootbridge_handle(seg, bus);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_root_bridge_notify, dev);
 	return 0;
 }
 
drivers/pci/pci-driver.c +100 -0

124 lines not on this page · raw message

drivers/pci/pci.c +87 -0

111 lines not on this page · raw message

drivers/pci/pci.h +3 -0
index f73bcbe..a81aff2 100644
@@ -34,6 +34,8 @@ extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
  *
  * @sleep_wake: enables/disables the system wake up capability of given device
  *
+ * @runtime_wake: enables/disables the runtime wakeup capability of given device
+ *
  * If given platform is generally capable of power managing PCI devices, all of
  * these callbacks are mandatory.
  */
@@ -43,6 +45,7 @@ struct pci_platform_pm_ops {
 	pci_power_t (*choose_state)(struct pci_dev *dev);
 	bool (*can_wakeup)(struct pci_dev *dev);
 	int (*sleep_wake)(struct pci_dev *dev, bool enable);
+	int (*runtime_wake)(struct pci_dev *dev, bool enable);
 };
 
 extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
include/linux/pci.h +3 -0
index 115fb7b..8a3fea0 100644
@@ -734,10 +734,13 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
 void pci_pme_active(struct pci_dev *dev, bool enable);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);
+int pci_enable_runtime_wake(struct pci_dev *dev, bool enable);
 int pci_wake_from_d3(struct pci_dev *dev, bool enable);
 pci_power_t pci_target_state(struct pci_dev *dev);
 int pci_prepare_to_sleep(struct pci_dev *dev);
 int pci_back_from_sleep(struct pci_dev *dev);
+int pci_dev_pme_event(struct pci_dev *dev);
+int pci_bus_pme_event(struct pci_dev *dev);
 
 /* Functions for PCI Hotplug drivers to use */
 int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
Signature · 1 line
Matthew Garrett | [email protected]
#9 lore raw

RFC 1/1 PCI: Runtime power management

In reply to Rafael J. Wysocki

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

---
 drivers/pci/pci-acpi.c   |   55 +++++++++++++++++++++++++
 drivers/pci/pci-driver.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.c        |   87 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.h        |    3 +
 include/linux/pci.h      |    3 +
 5 files changed, 248 insertions(+), 0 deletions(-)
drivers/pci/pci-acpi.c +55 -0
index ea15b05..a98a777 100644
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/pci-aspm.h>
+#include <linux/pm_runtime.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 
@@ -120,14 +121,62 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
 	return error;
 }
 
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+
+	if (!handle)
+		return -ENODEV;
+
+	status = acpi_bus_get_device(handle, &acpi_dev);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	if (enable) {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE_RUN);
+		acpi_enable_gpe(acpi_dev->wakeup.gpe_device,
+				acpi_dev->wakeup.gpe_number);
+	} else {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE);
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}
+
+
 static struct pci_platform_pm_ops acpi_pci_platform_pm = {
 	.is_manageable = acpi_pci_power_manageable,
 	.set_state = acpi_pci_set_power_state,
 	.choose_state = acpi_pci_choose_state,
 	.can_wakeup = acpi_pci_can_wakeup,
 	.sleep_wake = acpi_pci_sleep_wake,
+	.runtime_wake = acpi_pci_runtime_wake,
 };
 
+static void pci_device_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pm_runtime_resume(dev);
+}
+
+static void pci_root_bridge_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pci_bus_pme_event(pci_dev);
+}
+
 /* ACPI bus type */
 static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 {
@@ -140,6 +189,9 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_device_notify, dev);
 	return 0;
 }
 
@@ -158,6 +210,9 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_pci_rootbridge_handle(seg, bus);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_root_bridge_notify, dev);
 	return 0;
 }
 
drivers/pci/pci-driver.c +100 -0

124 lines not on this page · raw message

drivers/pci/pci.c +87 -0

111 lines not on this page · raw message

drivers/pci/pci.h +3 -0
index f73bcbe..a81aff2 100644
@@ -34,6 +34,8 @@ extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
  *
  * @sleep_wake: enables/disables the system wake up capability of given device
  *
+ * @runtime_wake: enables/disables the runtime wakeup capability of given device
+ *
  * If given platform is generally capable of power managing PCI devices, all of
  * these callbacks are mandatory.
  */
@@ -43,6 +45,7 @@ struct pci_platform_pm_ops {
 	pci_power_t (*choose_state)(struct pci_dev *dev);
 	bool (*can_wakeup)(struct pci_dev *dev);
 	int (*sleep_wake)(struct pci_dev *dev, bool enable);
+	int (*runtime_wake)(struct pci_dev *dev, bool enable);
 };
 
 extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
include/linux/pci.h +3 -0
index 115fb7b..8a3fea0 100644
@@ -734,10 +734,13 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
 void pci_pme_active(struct pci_dev *dev, bool enable);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);
+int pci_enable_runtime_wake(struct pci_dev *dev, bool enable);
 int pci_wake_from_d3(struct pci_dev *dev, bool enable);
 pci_power_t pci_target_state(struct pci_dev *dev);
 int pci_prepare_to_sleep(struct pci_dev *dev);
 int pci_back_from_sleep(struct pci_dev *dev);
+int pci_dev_pme_event(struct pci_dev *dev);
+int pci_bus_pme_event(struct pci_dev *dev);
 
 /* Functions for PCI Hotplug drivers to use */
 int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
Signature · 1 line
Matthew Garrett | [email protected]
#10 lore raw

RFC 1/1 usb: Add support for runtime power management of the hcd

The PCI runtime power management code means that we can implement support for powering down PCI host controllers. This patchset adds core support code along with a new hcd flag (HCD_RUNTIME_PM) that allows hosts to opt in if they support this functionality. Successfully tested with Intel EHCI and UHCI, though the UHCI code may need to pay more attention to vendor-specific features.

The power savings from this are measurable but not huge - it still seems like a decent optimisation. The main problem is that BIOS bugs on some Dell laptops will kill USB if this is used, so we either default to off or add some quirks to handle that case (I have some ideas in that respect).

---
 drivers/usb/core/hcd-pci.c  |   13 +++++++++++++
 drivers/usb/core/hcd.c      |    9 +++++++++
 drivers/usb/core/hcd.h      |    1 +
 drivers/usb/host/ehci-pci.c |    2 +-
 drivers/usb/host/uhci-hcd.c |    2 +-
 5 files changed, 25 insertions(+), 2 deletions(-)
drivers/usb/core/hcd-pci.c +13 -0
index 91f2885..e86324b 100644
@@ -363,6 +363,18 @@ static int hcd_pci_restore(struct device *dev)
 	return resume_common(dev, true);
 }
 
+static int hcd_pci_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+	struct usb_device *rhdev = hcd->self.root_hub;
+
+	if (!(hcd->driver->flags & HCD_RUNTIME_PM))
+		return -EINVAL;
+
+	return 0;
+}
+
 struct dev_pm_ops usb_hcd_pci_pm_ops = {
 	.suspend	= hcd_pci_suspend,
 	.suspend_noirq	= hcd_pci_suspend_noirq,
@@ -376,6 +388,7 @@ struct dev_pm_ops usb_hcd_pci_pm_ops = {
 	.poweroff_noirq	= hcd_pci_suspend_noirq,
 	.restore_noirq	= hcd_pci_resume_noirq,
 	.restore	= hcd_pci_restore,
+	.runtime_suspend = hcd_pci_runtime_suspend,
 };
 EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
 
drivers/usb/core/hcd.c +9 -0
index 95ccfa0..a8f8784 100644
@@ -38,6 +38,7 @@
 #include <asm/unaligned.h>
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/usb.h>
 
@@ -1747,6 +1748,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
 	if (status == 0) {
 		usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
 		hcd->state = HC_STATE_SUSPENDED;
+		pm_runtime_put(hcd->self.controller);
 	} else {
 		hcd->state = old_state;
 		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
@@ -1768,6 +1770,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
 	if (hcd->state == HC_STATE_RUNNING)
 		return 0;
 
+	pm_runtime_get_sync(hcd->self.controller);
 	hcd->state = HC_STATE_RESUMING;
 	status = hcd->driver->bus_resume(hcd);
 	if (status == 0) {
@@ -1781,6 +1784,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
 		hcd->state = old_state;
 		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
 				"resume", status);
+		pm_runtime_put(hcd->self.controller);
 		if (status != -ESHUTDOWN)
 			usb_hc_died(hcd);
 	}
@@ -1968,6 +1972,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 	INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+	pm_runtime_enable(dev);
+	pm_runtime_get(dev);
+
 	hcd->driver = driver;
 	hcd->product_desc = (driver->product_desc) ? driver->product_desc :
 			"USB Host Controller";
@@ -1979,6 +1986,8 @@ static void hcd_release (struct kref *kref)
 {
 	struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
 
+	pm_runtime_put_sync(hcd->self.controller);
+
 	kfree(hcd);
 }
 
drivers/usb/core/hcd.h +1 -0
index ec5c67e..4dc12a8 100644
@@ -171,6 +171,7 @@ struct hc_driver {
 	int	flags;
 #define	HCD_MEMORY	0x0001		/* HC regs use memory (else I/O) */
 #define	HCD_LOCAL_MEM	0x0002		/* HC needs local memory */
+#define	HCD_RUNTIME_PM	0x0004		/* HC supports handling runtime PM */
 #define	HCD_USB11	0x0010		/* USB 1.1 */
 #define	HCD_USB2	0x0020		/* USB 2.0 */
 #define	HCD_USB3	0x0040		/* USB 3.0 */
drivers/usb/host/ehci-pci.c +1 -1
index c2f1b7d..9583621 100644
@@ -368,7 +368,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
 	 * generic hardware linkage
 	 */
 	.irq =			ehci_irq,
-	.flags =		HCD_MEMORY | HCD_USB2,
+	.flags =		HCD_MEMORY | HCD_USB2 | HCD_RUNTIME_PM,
 
 	/*
 	 * basic lifecycle operations
drivers/usb/host/uhci-hcd.c +1 -1
index 274751b..36a3a4a 100644
@@ -900,7 +900,7 @@ static const struct hc_driver uhci_driver = {
 
 	/* Generic hardware linkage */
 	.irq =			uhci_irq,
-	.flags =		HCD_USB11,
+	.flags =		HCD_USB11 | HCD_RUNTIME_PM,
 
 	/* Basic lifecycle operations */
 	.reset =		uhci_init,
Signature · 1 line
Matthew Garrett | [email protected]
#11 lore raw

RFC 1/1 usb: Add support for runtime power management of the hcd

In reply to Matthew Garrett

The PCI runtime power management code means that we can implement support for powering down PCI host controllers. This patchset adds core support code along with a new hcd flag (HCD_RUNTIME_PM) that allows hosts to opt in if they support this functionality. Successfully tested with Intel EHCI and UHCI, though the UHCI code may need to pay more attention to vendor-specific features.

The power savings from this are measurable but not huge - it still seems like a decent optimisation. The main problem is that BIOS bugs on some Dell laptops will kill USB if this is used, so we either default to off or add some quirks to handle that case (I have some ideas in that respect).

---
 drivers/usb/core/hcd-pci.c  |   13 +++++++++++++
 drivers/usb/core/hcd.c      |    9 +++++++++
 drivers/usb/core/hcd.h      |    1 +
 drivers/usb/host/ehci-pci.c |    2 +-
 drivers/usb/host/uhci-hcd.c |    2 +-
 5 files changed, 25 insertions(+), 2 deletions(-)
drivers/usb/core/hcd-pci.c +13 -0
index 91f2885..e86324b 100644
@@ -363,6 +363,18 @@ static int hcd_pci_restore(struct device *dev)
 	return resume_common(dev, true);
 }
 
+static int hcd_pci_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+	struct usb_device *rhdev = hcd->self.root_hub;
+
+	if (!(hcd->driver->flags & HCD_RUNTIME_PM))
+		return -EINVAL;
+
+	return 0;
+}
+
 struct dev_pm_ops usb_hcd_pci_pm_ops = {
 	.suspend	= hcd_pci_suspend,
 	.suspend_noirq	= hcd_pci_suspend_noirq,
@@ -376,6 +388,7 @@ struct dev_pm_ops usb_hcd_pci_pm_ops = {
 	.poweroff_noirq	= hcd_pci_suspend_noirq,
 	.restore_noirq	= hcd_pci_resume_noirq,
 	.restore	= hcd_pci_restore,
+	.runtime_suspend = hcd_pci_runtime_suspend,
 };
 EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
 
drivers/usb/core/hcd.c +9 -0
index 95ccfa0..a8f8784 100644
@@ -38,6 +38,7 @@
 #include <asm/unaligned.h>
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
 
 #include <linux/usb.h>
 
@@ -1747,6 +1748,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
 	if (status == 0) {
 		usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
 		hcd->state = HC_STATE_SUSPENDED;
+		pm_runtime_put(hcd->self.controller);
 	} else {
 		hcd->state = old_state;
 		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
@@ -1768,6 +1770,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
 	if (hcd->state == HC_STATE_RUNNING)
 		return 0;
 
+	pm_runtime_get_sync(hcd->self.controller);
 	hcd->state = HC_STATE_RESUMING;
 	status = hcd->driver->bus_resume(hcd);
 	if (status == 0) {
@@ -1781,6 +1784,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
 		hcd->state = old_state;
 		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
 				"resume", status);
+		pm_runtime_put(hcd->self.controller);
 		if (status != -ESHUTDOWN)
 			usb_hc_died(hcd);
 	}
@@ -1968,6 +1972,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
 	INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+	pm_runtime_enable(dev);
+	pm_runtime_get(dev);
+
 	hcd->driver = driver;
 	hcd->product_desc = (driver->product_desc) ? driver->product_desc :
 			"USB Host Controller";
@@ -1979,6 +1986,8 @@ static void hcd_release (struct kref *kref)
 {
 	struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
 
+	pm_runtime_put_sync(hcd->self.controller);
+
 	kfree(hcd);
 }
 
drivers/usb/core/hcd.h +1 -0
index ec5c67e..4dc12a8 100644
@@ -171,6 +171,7 @@ struct hc_driver {
 	int	flags;
 #define	HCD_MEMORY	0x0001		/* HC regs use memory (else I/O) */
 #define	HCD_LOCAL_MEM	0x0002		/* HC needs local memory */
+#define	HCD_RUNTIME_PM	0x0004		/* HC supports handling runtime PM */
 #define	HCD_USB11	0x0010		/* USB 1.1 */
 #define	HCD_USB2	0x0020		/* USB 2.0 */
 #define	HCD_USB3	0x0040		/* USB 3.0 */
drivers/usb/host/ehci-pci.c +1 -1
index c2f1b7d..9583621 100644
@@ -368,7 +368,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
 	 * generic hardware linkage
 	 */
 	.irq =			ehci_irq,
-	.flags =		HCD_MEMORY | HCD_USB2,
+	.flags =		HCD_MEMORY | HCD_USB2 | HCD_RUNTIME_PM,
 
 	/*
 	 * basic lifecycle operations
drivers/usb/host/uhci-hcd.c +1 -1
index 274751b..36a3a4a 100644
@@ -900,7 +900,7 @@ static const struct hc_driver uhci_driver = {
 
 	/* Generic hardware linkage */
 	.irq =			uhci_irq,
-	.flags =		HCD_USB11,
+	.flags =		HCD_USB11 | HCD_RUNTIME_PM,
 
 	/* Basic lifecycle operations */
 	.reset =		uhci_init,
Signature · 1 line
Matthew Garrett | [email protected]
#12 lore raw

Re: [linux-pm] [RFC] usb: Add support for runtime power management of the hcd

Am Donnerstag, 13. August 2009 02:35:44 schrieb Matthew Garrett:

Quoted 1 line

The power savings from this are measurable but not huge - it still seems

How large?

Quoted 4 lines

like a decent optimisation. The main problem is that BIOS bugs on some Dell laptops will kill USB if this is used, so we either default to off or add some quirks to handle that case (I have some ideas in that respect).

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct · line 1975

Quoted 5 lines
drivers/usb/core/hcd.c +1 -0
@@ -1968,6 +1972,9 @@ struct usb_hcd *usb_create_hcd (const struct
 hc_driver *driver, INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+	pm_runtime_enable(dev);

So you don't get a reference from that?

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, · line 1976

Quoted 1 line
drivers/usb/core/hcd.c +1 -0
+	pm_runtime_get(dev);

What happens if you get a runtime suspend request in between? Is this a flaw of the API?

Regards
Oliver

#13 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

Quoted 5 lines · Oliver Neukum

Am Donnerstag, 13. August 2009 02:35:44 schrieb Matthew Garrett:

Quoted 1 line

The power savings from this are measurable but not huge - it still seems

How large?

About 0.2W on an ich9 system.

Quoted 7 lines
Quoted 4 lines

like a decent optimisation. The main problem is that BIOS bugs on some Dell laptops will kill USB if this is used, so we either default to off or add some quirks to handle that case (I have some ideas in that respect).

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

They range from pragmatic to ugly. We could blacklist all Dells, though I'm trying to find out if there's a BIOS date that guarantees the system is fixed. Alternatively, it's a single-line bug in the DSDT - we could implement some kind of fixup in the ACPI parsing code. I find the latter interesting but possibly too hideous to live :)

Quoted 7 lines

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct · line 1975

Quoted 5 lines
drivers/usb/core/hcd.c +1 -0
@@ -1968,6 +1972,9 @@ struct usb_hcd *usb_create_hcd (const struct
 hc_driver *driver, INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+	pm_runtime_enable(dev);

So you don't get a reference from that?

No, but...

Quoted 4 lines

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, · line 1976

Quoted 1 line
drivers/usb/core/hcd.c +1 -0
+	pm_runtime_get(dev);

What happens if you get a runtime suspend request in between? Is this a flaw of the API?

I suspect that just swapping the order of those two lines would be fine.

Signature · 1 line
Matthew Garrett | [email protected]
#14 lore raw

Re: [linux-pm] [RFC] usb: Add support for runtime power management of the hcd

In reply to Oliver Neukum
Quoted 5 lines · Oliver Neukum

Am Donnerstag, 13. August 2009 02:35:44 schrieb Matthew Garrett:

Quoted 1 line

The power savings from this are measurable but not huge - it still seems

How large?

About 0.2W on an ich9 system.

Quoted 7 lines
Quoted 4 lines

like a decent optimisation. The main problem is that BIOS bugs on some Dell laptops will kill USB if this is used, so we either default to off or add some quirks to handle that case (I have some ideas in that respect).

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

They range from pragmatic to ugly. We could blacklist all Dells, though I'm trying to find out if there's a BIOS date that guarantees the system is fixed. Alternatively, it's a single-line bug in the DSDT - we could implement some kind of fixup in the ACPI parsing code. I find the latter interesting but possibly too hideous to live :)

Quoted 7 lines

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct · line 1975

Quoted 5 lines
drivers/usb/core/hcd.c +1 -0
@@ -1968,6 +1972,9 @@ struct usb_hcd *usb_create_hcd (const struct
 hc_driver *driver, INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+	pm_runtime_enable(dev);

So you don't get a reference from that?

No, but...

Quoted 4 lines

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, · line 1976

Quoted 1 line
drivers/usb/core/hcd.c +1 -0
+	pm_runtime_get(dev);

What happens if you get a runtime suspend request in between? Is this a flaw of the API?

I suspect that just swapping the order of those two lines would be fine.

Signature · 1 line
Matthew Garrett | [email protected]
#15 lore raw

Re: [linux-pm] [RFC] usb: Add support for runtime power management of the hcd

Am Donnerstag, 13. August 2009 14:30:34 schrieb Matthew Garrett:

Quoted 8 lines
Quoted 2 lines

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

They range from pragmatic to ugly. We could blacklist all Dells, though I'm trying to find out if there's a BIOS date that guarantees the system is fixed. Alternatively, it's a single-line bug in the DSDT - we could implement some kind of fixup in the ACPI parsing code. I find the latter interesting but possibly too hideous to live :)

Is there any indication only those BIOSes are affected?

Regards
Oliver

#16 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

Quoted 11 lines · Oliver Neukum

Am Donnerstag, 13. August 2009 14:30:34 schrieb Matthew Garrett:

Quoted 8 lines
Quoted 2 lines

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

They range from pragmatic to ugly. We could blacklist all Dells, though I'm trying to find out if there's a BIOS date that guarantees the system is fixed. Alternatively, it's a single-line bug in the DSDT - we could implement some kind of fixup in the ACPI parsing code. I find the latter interesting but possibly too hideous to live :)

Is there any indication only those BIOSes are affected?

Based on what I've looked at, other BIOSes either indicate that they don't support this or should work properly. Real life may disagree.

Signature · 1 line
Matthew Garrett | [email protected]
#17 lore raw

Re: [linux-pm] [RFC] usb: Add support for runtime power management of the hcd

In reply to Oliver Neukum
Quoted 11 lines · Oliver Neukum

Am Donnerstag, 13. August 2009 14:30:34 schrieb Matthew Garrett:

Quoted 8 lines
Quoted 2 lines

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

They range from pragmatic to ugly. We could blacklist all Dells, though I'm trying to find out if there's a BIOS date that guarantees the system is fixed. Alternatively, it's a single-line bug in the DSDT - we could implement some kind of fixup in the ACPI parsing code. I find the latter interesting but possibly too hideous to live :)

Is there any indication only those BIOSes are affected?

Based on what I've looked at, other BIOSes either indicate that they don't support this or should work properly. Real life may disagree.

Signature · 1 line
Matthew Garrett | [email protected]
#18 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

In reply to Matthew Garrett

Am Donnerstag, 13. August 2009 14:30:34 schrieb Matthew Garrett:

Quoted 8 lines
Quoted 2 lines

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

They range from pragmatic to ugly. We could blacklist all Dells, though I'm trying to find out if there's a BIOS date that guarantees the system is fixed. Alternatively, it's a single-line bug in the DSDT - we could implement some kind of fixup in the ACPI parsing code. I find the latter interesting but possibly too hideous to live :)

Is there any indication only those BIOSes are affected?

Regards
Oliver

#19 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

In reply to Matthew Garrett

Am Donnerstag, 13. August 2009 02:35:44 schrieb Matthew Garrett:

Quoted 1 line

The power savings from this are measurable but not huge - it still seems

How large?

Quoted 4 lines

like a decent optimisation. The main problem is that BIOS bugs on some Dell laptops will kill USB if this is used, so we either default to off or add some quirks to handle that case (I have some ideas in that respect).

Your earlier failures don't look promising regarding BIOSes. What do you have in mind?

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct · line 1975

Quoted 5 lines
drivers/usb/core/hcd.c +1 -0
@@ -1968,6 +1972,9 @@ struct usb_hcd *usb_create_hcd (const struct
 hc_driver *driver, INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+	pm_runtime_enable(dev);

So you don't get a reference from that?

drivers/usb/core/hcd.c struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, · line 1976

Quoted 1 line
drivers/usb/core/hcd.c +1 -0
+	pm_runtime_get(dev);

What happens if you get a runtime suspend request in between? Is this a flaw of the API?

Regards
Oliver

maintainer
#20 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

In reply to Matthew Garrett
Quoted 7 lines · Matthew Garrett
drivers/usb/core/hcd-pci.c +1 -0
@@ -363,6 +363,18 @@ static int hcd_pci_restore(struct device *dev)
 	return resume_common(dev, true);
 }
 
+static int hcd_pci_runtime_suspend(struct device *dev)

drivers/usb/core/hcd-pci.c static int hcd_pci_restore(struct device *dev) · line 376

Quoted 10 lines
drivers/usb/core/hcd-pci.c +10 -0
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+	struct usb_device *rhdev = hcd->self.root_hub;
+
+	if (!(hcd->driver->flags & HCD_RUNTIME_PM))
+		return -EINVAL;
+
+	return 0;
+}

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

Ditto for resuming.

Alan Stern

maintainer
#21 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

In reply to Matthew Garrett
Quoted 7 lines · Matthew Garrett
drivers/usb/core/hcd-pci.c +1 -0
@@ -363,6 +363,18 @@ static int hcd_pci_restore(struct device *dev)
 	return resume_common(dev, true);
 }
 
+static int hcd_pci_runtime_suspend(struct device *dev)

drivers/usb/core/hcd-pci.c static int hcd_pci_restore(struct device *dev) · line 376

Quoted 10 lines
drivers/usb/core/hcd-pci.c +10 -0
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+	struct usb_device *rhdev = hcd->self.root_hub;
+
+	if (!(hcd->driver->flags & HCD_RUNTIME_PM))
+		return -EINVAL;
+
+	return 0;
+}

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

Ditto for resuming.

Alan Stern

#22 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

Signature · 1 line
Matthew Garrett | [email protected]
#23 lore raw

Re: [RFC] usb: Add support for runtime power management of the hcd

In reply to Alan Stern
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

Signature · 1 line
Matthew Garrett | [email protected]
maintainer
#24 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 923

Quoted 12 lines · Matthew Garrett
drivers/pci/pci-driver.c +9 -0
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error;
+
+	device_set_wakeup_enable(dev, 1);

This is a userspace policy parameter. Kernel code should not alter it. Instead you should test device_may_wakeup.

Quoted 25 lines
drivers/pci/pci-driver.c +25 -0
+	error = pci_enable_runtime_wake(pci_dev, true);
+
+	if (error)
+		return -EBUSY;
+
+	if (pm && pm->runtime_suspend)
+		error = pm->runtime_suspend(dev);
+
+	if (error)
+		goto out;
+
+	error = pci_pm_suspend(dev);
+
+	if (error)
+		goto resume;
+
+	disable_irq(pci_dev->irq);
+	error = pci_pm_suspend_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		goto resume_noirq;
+
+	return 0;
+

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 958

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+resume_noirq:
+	disable_irq(pci_dev->irq);
+	pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+resume:
+	pci_pm_resume(dev);
+out:
+	pci_enable_runtime_wake(pci_dev, false);
+	return error;
+}

The goto statements and unwinding code don't match up.

Quoted 21 lines
drivers/pci/pci-driver.c +21 -0
+static int pci_pm_runtime_resume(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error = 0;
+
+	disable_irq(pci_dev->irq);
+	error = pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		return error;
+
+	error = pci_pm_resume(dev);
+
+	if (error)
+		return error;
+
+	if (pm->runtime_resume)
+		error = pm->runtime_resume(dev);
+

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 990

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+	if (error)
+		return error;
+
+	error = pci_enable_runtime_wake(pci_dev, false);
+
+	if (error)
+		return error;
+
+	return 0;
+}

Log an error message when something goes wrong?

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 1000

Quoted 9 lines
drivers/pci/pci-driver.c +9 -0
+static void pci_pm_runtime_idle(struct device *dev)
+{
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->runtime_idle)
+		pm->runtime_idle(dev);
+
+	pm_schedule_suspend(dev, 0);
+}

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

Alan Stern

maintainer
#25 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 923

Quoted 12 lines · Matthew Garrett
drivers/pci/pci-driver.c +9 -0
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error;
+
+	device_set_wakeup_enable(dev, 1);

This is a userspace policy parameter. Kernel code should not alter it. Instead you should test device_may_wakeup.

Quoted 25 lines
drivers/pci/pci-driver.c +25 -0
+	error = pci_enable_runtime_wake(pci_dev, true);
+
+	if (error)
+		return -EBUSY;
+
+	if (pm && pm->runtime_suspend)
+		error = pm->runtime_suspend(dev);
+
+	if (error)
+		goto out;
+
+	error = pci_pm_suspend(dev);
+
+	if (error)
+		goto resume;
+
+	disable_irq(pci_dev->irq);
+	error = pci_pm_suspend_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		goto resume_noirq;
+
+	return 0;
+

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 958

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+resume_noirq:
+	disable_irq(pci_dev->irq);
+	pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+resume:
+	pci_pm_resume(dev);
+out:
+	pci_enable_runtime_wake(pci_dev, false);
+	return error;
+}

The goto statements and unwinding code don't match up.

Quoted 21 lines
drivers/pci/pci-driver.c +21 -0
+static int pci_pm_runtime_resume(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error = 0;
+
+	disable_irq(pci_dev->irq);
+	error = pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		return error;
+
+	error = pci_pm_resume(dev);
+
+	if (error)
+		return error;
+
+	if (pm->runtime_resume)
+		error = pm->runtime_resume(dev);
+

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 990

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+	if (error)
+		return error;
+
+	error = pci_enable_runtime_wake(pci_dev, false);
+
+	if (error)
+		return error;
+
+	return 0;
+}

Log an error message when something goes wrong?

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 1000

Quoted 9 lines
drivers/pci/pci-driver.c +9 -0
+static void pci_pm_runtime_idle(struct device *dev)
+{
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->runtime_idle)
+		pm->runtime_idle(dev);
+
+	pm_schedule_suspend(dev, 0);
+}

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

Alan Stern

#26 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 5 lines · Alan Stern
Quoted 2 lines · Matthew Garrett
drivers/pci/pci-driver.c +0 -0
Quoted 12 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 923

Quoted 9 lines
drivers/pci/pci-driver.c +9 -0
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error;
+
+	device_set_wakeup_enable(dev, 1);

This is a userspace policy parameter. Kernel code should not alter it. Instead you should test device_may_wakeup.

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 9 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 958

Quoted 7 lines
drivers/pci/pci-driver.c +7 -0
+	enable_irq(pci_dev->irq);
+resume:
+	pci_pm_resume(dev);
+out:
+	pci_enable_runtime_wake(pci_dev, false);
+	return error;
+}

The goto statements and unwinding code don't match up.

I'll look at that.

Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 990

Quoted 5 lines
drivers/pci/pci-driver.c +5 -0
+	if (error)
+		return error;
+
+	return 0;
+}

Log an error message when something goes wrong?

Seems fair.

Quoted 6 lines
Quoted 6 lines
drivers/pci/pci-driver.c +6 -0
+static void pci_pm_runtime_idle(struct device *dev)
+{
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->runtime_idle)
+		pm->runtime_idle(dev);
Quoted 8 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 1000

Quoted 3 lines
drivers/pci/pci-driver.c +3 -0
+
+	pm_schedule_suspend(dev, 0);
+}

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

Signature · 1 line
Matthew Garrett | [email protected]
#27 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Alan Stern
Quoted 5 lines · Alan Stern
Quoted 2 lines · Matthew Garrett
drivers/pci/pci-driver.c +0 -0
Quoted 12 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 923

Quoted 9 lines
drivers/pci/pci-driver.c +9 -0
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error;
+
+	device_set_wakeup_enable(dev, 1);

This is a userspace policy parameter. Kernel code should not alter it. Instead you should test device_may_wakeup.

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 9 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 958

Quoted 7 lines
drivers/pci/pci-driver.c +7 -0
+	enable_irq(pci_dev->irq);
+resume:
+	pci_pm_resume(dev);
+out:
+	pci_enable_runtime_wake(pci_dev, false);
+	return error;
+}

The goto statements and unwinding code don't match up.

I'll look at that.

Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 990

Quoted 5 lines
drivers/pci/pci-driver.c +5 -0
+	if (error)
+		return error;
+
+	return 0;
+}

Log an error message when something goes wrong?

Seems fair.

Quoted 6 lines
Quoted 6 lines
drivers/pci/pci-driver.c +6 -0
+static void pci_pm_runtime_idle(struct device *dev)
+{
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->runtime_idle)
+		pm->runtime_idle(dev);
Quoted 8 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 1000

Quoted 3 lines
drivers/pci/pci-driver.c +3 -0
+
+	pm_schedule_suspend(dev, 0);
+}

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

Signature · 1 line
Matthew Garrett | [email protected]
#28 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

Quoted 11 lines
Quoted 4 lines

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

From the API docs:

"The action performed by a bus type's ->runtime_idle() callback is totally dependent on the bus type in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the device in that case."

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

Signature · 1 line
Matthew Garrett | [email protected]
#29 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

In reply to Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

Quoted 11 lines
Quoted 4 lines

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

From the API docs:

"The action performed by a bus type's ->runtime_idle() callback is totally dependent on the bus type in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the device in that case."

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

Signature · 1 line
Matthew Garrett | [email protected]
maintainer
#30 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

So it does; my mistake.

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 4 lines

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

You are suggesting separate flag sets for system-wide wakeup and runtime wakeup? I don't disagree, but implementing them will be problematical.

That's because it's not always possible to change a device's wakeup setting while it is suspended. Thus if a device was runtime suspended with wakeup enabled, and then we want to do a system sleep and change the device's wakeup setting to disabled, we would have to wake the device back up in order to do it.

Quoted 18 lines
Quoted 9 lines
Quoted 4 lines

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

From the API docs:

"The action performed by a bus type's ->runtime_idle() callback is totally dependent on the bus type in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the device in that case."

Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Alan Stern

maintainer
#31 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

In reply to Matthew Garrett
Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

So it does; my mistake.

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 4 lines

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

You are suggesting separate flag sets for system-wide wakeup and runtime wakeup? I don't disagree, but implementing them will be problematical.

That's because it's not always possible to change a device's wakeup setting while it is suspended. Thus if a device was runtime suspended with wakeup enabled, and then we want to do a system sleep and change the device's wakeup setting to disabled, we would have to wake the device back up in order to do it.

Quoted 18 lines
Quoted 9 lines
Quoted 4 lines

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

From the API docs:

"The action performed by a bus type's ->runtime_idle() callback is totally dependent on the bus type in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the device in that case."

Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Alan Stern

#32 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 64 lines · Alan Stern
Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

So it does; my mistake.

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 4 lines

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

You are suggesting separate flag sets for system-wide wakeup and runtime wakeup? I don't disagree, but implementing them will be problematical.

That's because it's not always possible to change a device's wakeup setting while it is suspended. Thus if a device was runtime suspended with wakeup enabled, and then we want to do a system sleep and change the device's wakeup setting to disabled, we would have to wake the device back up in order to do it.

Quoted 18 lines
Quoted 9 lines
Quoted 4 lines

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

From the API docs:

"The action performed by a bus type's ->runtime_idle() callback is totally dependent on the bus type in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the device in that case."

Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

Quoted 8 lines

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Well, I'm not against that, but what should pm_runtime_idle() do with the result returned by it? Just pass it to the caller?

Rafael

#33 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · Alan Stern
Quoted 1 line · Matthew Garrett

On Thu, Aug 13, 2009 at 11:22:44AM -0400, Alan Stern wrote:

...

Quoted 10 lines
Quoted 10 lines
Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

Quoted 6 lines
Quoted 3 lines

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Well, I'm not against that, but what should pm_runtime_idle() do with the result returned by it? Just pass it to the caller?

Hm, perhaps its better to ignore it, though.

Rafael

maintainer
#34 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · Alan Stern
Quoted 1 line · Matthew Garrett

On Thu, Aug 13, 2009 at 11:22:44AM -0400, Alan Stern wrote:

...

Quoted 10 lines
Quoted 10 lines
Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Well, I'm not against that, but what should pm_runtime_idle() do with the result returned by it? Just pass it to the caller?

Hm, perhaps its better to ignore it, though.

That's what I was going to say. The return value is intended for use by bus-level code when calling a driver-level routine.

Alan Stern

maintainer
#35 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Rafael J. Wysocki
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · Alan Stern
Quoted 1 line · Matthew Garrett

On Thu, Aug 13, 2009 at 11:22:44AM -0400, Alan Stern wrote:

...

Quoted 10 lines
Quoted 10 lines
Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Well, I'm not against that, but what should pm_runtime_idle() do with the result returned by it? Just pass it to the caller?

Hm, perhaps its better to ignore it, though.

That's what I was going to say. The return value is intended for use by bus-level code when calling a driver-level routine.

Alan Stern

#36 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Rafael J. Wysocki
Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · Alan Stern
Quoted 1 line · Matthew Garrett

On Thu, Aug 13, 2009 at 11:22:44AM -0400, Alan Stern wrote:

...

Quoted 10 lines
Quoted 10 lines
Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

Quoted 6 lines
Quoted 3 lines

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Well, I'm not against that, but what should pm_runtime_idle() do with the result returned by it? Just pass it to the caller?

Hm, perhaps its better to ignore it, though.

Rafael

#37 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Alan Stern
Quoted 64 lines · Alan Stern
Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

So it does; my mistake.

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 4 lines

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

You are suggesting separate flag sets for system-wide wakeup and runtime wakeup? I don't disagree, but implementing them will be problematical.

That's because it's not always possible to change a device's wakeup setting while it is suspended. Thus if a device was runtime suspended with wakeup enabled, and then we want to do a system sleep and change the device's wakeup setting to disabled, we would have to wake the device back up in order to do it.

Quoted 18 lines
Quoted 9 lines
Quoted 4 lines

This misses the point. The whole idea of runtime_idle is to tell you that the device is idle and might be ready to be suspended. If you're going to call pm_schedule_suspend anyway, there's no reason to invoke pm->runtime_idle.

My understanding of the API was that pm_device_put() invokes runtime_idle if the refcount hits 0. The bus layer has no idea of the refcount, and calling suspend directly from the driver would defeat the point of the system-wide recounting.

From the API docs:

"The action performed by a bus type's ->runtime_idle() callback is totally dependent on the bus type in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the device in that case."

Quoted 4 lines

Though perhaps the device level runtime_idle shouldn't be void - that way the bus can ask the driver whether its suspend conditions have been satisfied? Right now there doesn't seem to be any way for the bus to ask that.

Quoted 8 lines

If you want to get the device-level runtime_idle involved, you can make _it_ responsible for scheduling the suspend. Then the bus-level code simply has to check whether everything is okay at the bus level, and if it is, call the device-level routine.

However changing the return type wouldn't hurt anything, and it would allow the pm_schedule_suspend call to be centralized in the bus code. You could ask Rafael about it, or just send him a patch.

Well, I'm not against that, but what should pm_runtime_idle() do with the result returned by it? Just pass it to the caller?

Rafael

#38 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

In reply to Alan Stern
Quoted 34 lines · Alan Stern
Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

So it does; my mistake.

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 4 lines

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

You are suggesting separate flag sets for system-wide wakeup and runtime wakeup? I don't disagree, but implementing them will be problematical.

Quoted 5 lines

That's because it's not always possible to change a device's wakeup setting while it is suspended. Thus if a device was runtime suspended with wakeup enabled, and then we want to do a system sleep and change the device's wakeup setting to disabled, we would have to wake the device back up in order to do it.

Well, sometimes the user may want a device to be power managed at run time and not to be able to wake up the system from sleep states. For example, I'd like the USB controller in my box to be suspended at run time whenever it's not used, but surely I wouldn't like it to do system-wide wakeup, because it does that when I move the mouse which is a cordless one. Simply turning the mouse on causes the system to wake up. :-)

So, I don't think we have a choice here. If the user forbids the device to wake up the system from sleep states, we have to do what he wants, even if that means the devices has to be resumed before putting the system into a sleep state.

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

If the user space does that, we can't power manage the device at run time, so it can't do runtime wakeup as well. Otherwise, the device is power manageable at run time, which implies runtime wakeup if supported.

Thanks,
Rafael

#39 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 6 lines · Rafael J. Wysocki

Well, sometimes the user may want a device to be power managed at run time and not to be able to wake up the system from sleep states. For example, I'd like the USB controller in my box to be suspended at run time whenever it's not used, but surely I wouldn't like it to do system-wide wakeup, because it does that when I move the mouse which is a cordless one. Simply turning the mouse on causes the system to wake up. :-)

Right, so clearly my code is broken right now.

Quoted 3 lines

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient. If the worst case is scenario is that we have to resume devices in order to apply the correct policy when going into a systemwide suspend state, I think that's acceptable.

Signature · 1 line
Matthew Garrett | [email protected]
#40 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

Well, sometimes the user may want a device to be power managed at run time and not to be able to wake up the system from sleep states. For example, I'd like the USB controller in my box to be suspended at run time whenever it's not used, but surely I wouldn't like it to do system-wide wakeup, because it does that when I move the mouse which is a cordless one. Simply turning the mouse on causes the system to wake up. :-)

Right, so clearly my code is broken right now.

Quoted 3 lines

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient. If the worst case is scenario is that we have to resume devices in order to apply the correct policy when going into a systemwide suspend state, I think that's acceptable.

Signature · 1 line
Matthew Garrett | [email protected]
#41 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Rafael J. Wysocki

Well, sometimes the user may want a device to be power managed at run time and not to be able to wake up the system from sleep states. For example, I'd like the USB controller in my box to be suspended at run time whenever it's not used, but surely I wouldn't like it to do system-wide wakeup, because it does that when I move the mouse which is a cordless one. Simply turning the mouse on causes the system to wake up. :-)

Quoted 8 lines

Right, so clearly my code is broken right now.

Quoted 3 lines

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient.

Perhaps it would, but then unsetting runtime_wakeup would effectively disable runtime PM for devices that need it to be power managed at run time (probably all input devices). Also there may be situations in which user space may really want to disable runtime PM for some devices (think of broken hardware for one example).

Quoted 3 lines

If the worst case is scenario is that we have to resume devices in order to apply the correct policy when going into a systemwide suspend state, I think that's acceptable.

I agree.

Thanks,
Rafael

maintainer
#42 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 4 lines · Rafael J. Wysocki
Quoted 4 lines
Quoted 3 lines

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

Quoted 8 lines
Quoted 2 lines

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient.

Perhaps it would, but then unsetting runtime_wakeup would effectively disable runtime PM for devices that need it to be power managed at run time (probably all input devices). Also there may be situations in which user space may really want to disable runtime PM for some devices (think of broken hardware for one example).

It sounds like there are really three choices here, and the decision should largely be left up to the user:

1. don't use runtime PM,

2. allow runtime PM but disable remote wakeup,

3. allow runtime PM with remote wakeup enabled.

Now, a driver may say "I can't do my job without remote wakeup". Such a driver would refuse to do runtime_suspend in case 2. But otherwise we should follow the preference of the user.

The only remaining question is how to expose this in sysfs in a way that won't be confusing and that won't be confused with the "wakeup" attribute. One possibility is to use the "level" attribute introduced in USB; possible levels are "on" (no runtime PM) and "auto" (runtime PM allowed). Then a new "runtime_wakeup" attribute could contain nothing (if wakeup is not available), "enabled", or "disabled".

Alan Stern

maintainer
#43 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

Quoted 8 lines
Quoted 2 lines

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient.

Perhaps it would, but then unsetting runtime_wakeup would effectively disable runtime PM for devices that need it to be power managed at run time (probably all input devices). Also there may be situations in which user space may really want to disable runtime PM for some devices (think of broken hardware for one example).

It sounds like there are really three choices here, and the decision should largely be left up to the user:

1. don't use runtime PM,

2. allow runtime PM but disable remote wakeup,

3. allow runtime PM with remote wakeup enabled.

Now, a driver may say "I can't do my job without remote wakeup". Such a driver would refuse to do runtime_suspend in case 2. But otherwise we should follow the preference of the user.

The only remaining question is how to expose this in sysfs in a way that won't be confusing and that won't be confused with the "wakeup" attribute. One possibility is to use the "level" attribute introduced in USB; possible levels are "on" (no runtime PM) and "auto" (runtime PM allowed). Then a new "runtime_wakeup" attribute could contain nothing (if wakeup is not available), "enabled", or "disabled".

Alan Stern

#44 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 28 lines · Alan Stern
Quoted 4 lines · Rafael J. Wysocki
Quoted 4 lines
Quoted 3 lines

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

Quoted 8 lines
Quoted 2 lines

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient.

Perhaps it would, but then unsetting runtime_wakeup would effectively disable runtime PM for devices that need it to be power managed at run time (probably all input devices). Also there may be situations in which user space may really want to disable runtime PM for some devices (think of broken hardware for one example).

It sounds like there are really three choices here, and the decision should largely be left up to the user:

1. don't use runtime PM,

2. allow runtime PM but disable remote wakeup,

3. allow runtime PM with remote wakeup enabled.

Now, a driver may say "I can't do my job without remote wakeup". Such a driver would refuse to do runtime_suspend in case 2. But otherwise we should follow the preference of the user.

Quoted 6 lines

The only remaining question is how to expose this in sysfs in a way that won't be confusing and that won't be confused with the "wakeup" attribute. One possibility is to use the "level" attribute introduced in USB; possible levels are "on" (no runtime PM) and "auto" (runtime PM allowed). Then a new "runtime_wakeup" attribute could contain nothing (if wakeup is not available), "enabled", or "disabled".

That seems to require two flags.

runtime_forbidden - if unset, the driver decides whether or not to use runtime
PM; that could be exposed through sysfs as 'runtime' under the 'power' subdirectory with the following values:
* 'disabled' - runtime_forbidden is set by the user space
* 'on' - runtime_forbidden is unset, runtime PM is used (disable_depth == 0)
* 'off' - runtime_forbidden is unset, runtime PM is not used
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is unset.

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

Thanks,
Rafael

#45 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 8 lines · Rafael J. Wysocki

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Signature · 1 line
Matthew Garrett | [email protected]
#46 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Signature · 1 line
Matthew Garrett | [email protected]
#47 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 11 lines · Matthew Garrett
Quoted 8 lines · Rafael J. Wysocki

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

Quoted 4 lines

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Well, this was exactly my point previously. :-)

Still, although for the majority of devices 'runtime_wakeup' disabled would mean no runtime PM at all IMO, there may be devices that actually work without remote wakeup, although they support it in general.

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Thanks,
Rafael

#48 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 2 lines · Rafael J. Wysocki

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

I think in that case we'd probably just want the interface to be downed? Some of this is going to require device-specific policy, I think - for the network case we probably want something in between IF_RUNNING and IF_DOWN (IF_CARRIER, perhaps) that indicates that we want the PHY to be powered. Pushing this out to sysfs would mean we'd have a consistent interface but varying semantics, and I'm not convinced that's an especially helpful interface.

Signature · 1 line
Matthew Garrett | [email protected]
#49 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

I think in that case we'd probably just want the interface to be downed? Some of this is going to require device-specific policy, I think - for the network case we probably want something in between IF_RUNNING and IF_DOWN (IF_CARRIER, perhaps) that indicates that we want the PHY to be powered. Pushing this out to sysfs would mean we'd have a consistent interface but varying semantics, and I'm not convinced that's an especially helpful interface.

Signature · 1 line
Matthew Garrett | [email protected]
#50 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

Quoted 5 lines · Matthew Garrett
Quoted 2 lines · Rafael J. Wysocki

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Quoted 7 lines

I think in that case we'd probably just want the interface to be downed? Some of this is going to require device-specific policy, I think - for the network case we probably want something in between IF_RUNNING and IF_DOWN (IF_CARRIER, perhaps) that indicates that we want the PHY to be powered. Pushing this out to sysfs would mean we'd have a consistent interface but varying semantics, and I'm not convinced that's an especially helpful interface.

I'm not disagreeing with that.

At this point I'd like to know the Alan's opinion. I would gladly use the 'runtime_forbidden' flag only, but if we overlook something now, it's going to be difficult to fix later.

Thanks,
Rafael

maintainer
#51 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 14 lines · Rafael J. Wysocki
Quoted 5 lines · Matthew Garrett
Quoted 2 lines · Rafael J. Wysocki

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Quoted 7 lines

I think in that case we'd probably just want the interface to be downed? Some of this is going to require device-specific policy, I think - for the network case we probably want something in between IF_RUNNING and IF_DOWN (IF_CARRIER, perhaps) that indicates that we want the PHY to be powered. Pushing this out to sysfs would mean we'd have a consistent interface but varying semantics, and I'm not convinced that's an especially helpful interface.

Quoted 5 lines

I'm not disagreeing with that.

At this point I'd like to know the Alan's opinion. I would gladly use the 'runtime_forbidden' flag only, but if we overlook something now, it's going to be difficult to fix later.

The notion of "remote wakeup" is more or less the same for all devices, and it can effectively be abstracted into the core. Other notions, like IF_CARRIER, are more device-dependent. They must be handled at the bus or driver level, not in the core.

The real question is whether we should export a "may_runtime_wakeup" flag. If we don't then we prevent the use of a degraded power-management mode in devices with broken wakeup support. Perhaps that's okay, I don't know.

Which reminds me... In addition to these flags controlling what settings should be enabled, maybe we should add a flag to record whether or not remote wakeup was turned on when the device was last suspended. Although the core wouldn't use it, such a flag might be very convenient for drivers.

Alan Stern

maintainer
#52 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

In reply to Rafael J. Wysocki
Quoted 14 lines · Rafael J. Wysocki
Quoted 5 lines · Matthew Garrett
Quoted 2 lines · Rafael J. Wysocki

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Quoted 7 lines

I think in that case we'd probably just want the interface to be downed? Some of this is going to require device-specific policy, I think - for the network case we probably want something in between IF_RUNNING and IF_DOWN (IF_CARRIER, perhaps) that indicates that we want the PHY to be powered. Pushing this out to sysfs would mean we'd have a consistent interface but varying semantics, and I'm not convinced that's an especially helpful interface.

Quoted 5 lines

I'm not disagreeing with that.

At this point I'd like to know the Alan's opinion. I would gladly use the 'runtime_forbidden' flag only, but if we overlook something now, it's going to be difficult to fix later.

The notion of "remote wakeup" is more or less the same for all devices, and it can effectively be abstracted into the core. Other notions, like IF_CARRIER, are more device-dependent. They must be handled at the bus or driver level, not in the core.

The real question is whether we should export a "may_runtime_wakeup" flag. If we don't then we prevent the use of a degraded power-management mode in devices with broken wakeup support. Perhaps that's okay, I don't know.

Which reminds me... In addition to these flags controlling what settings should be enabled, maybe we should add a flag to record whether or not remote wakeup was turned on when the device was last suspended. Although the core wouldn't use it, such a flag might be very convenient for drivers.

Alan Stern

#53 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett
Quoted 5 lines · Matthew Garrett
Quoted 2 lines · Rafael J. Wysocki

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Quoted 7 lines

I think in that case we'd probably just want the interface to be downed? Some of this is going to require device-specific policy, I think - for the network case we probably want something in between IF_RUNNING and IF_DOWN (IF_CARRIER, perhaps) that indicates that we want the PHY to be powered. Pushing this out to sysfs would mean we'd have a consistent interface but varying semantics, and I'm not convinced that's an especially helpful interface.

I'm not disagreeing with that.

At this point I'd like to know the Alan's opinion. I would gladly use the 'runtime_forbidden' flag only, but if we overlook something now, it's going to be difficult to fix later.

Thanks,
Rafael

maintainer
#54 lore raw

Re: [RFC] PCI: Runtime power management

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

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Matthew, what makes you think remote wakeup is required? Lots of power-manageable devices don't support it at all (consider disk drives or display screens).

Quoted 5 lines

Well, this was exactly my point previously. :-)

Still, although for the majority of devices 'runtime_wakeup' disabled would mean no runtime PM at all IMO, there may be devices that actually work without remote wakeup, although they support it in general.

That last part is quite true. For example, we might suspend the device whenever no process has opened the device file. It would be a degraded form of power management, but better than nothing.

Quoted 2 lines

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Or if the device's support for remote wakeup is broken.

Alan Stern

maintainer
#55 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Matthew, what makes you think remote wakeup is required? Lots of power-manageable devices don't support it at all (consider disk drives or display screens).

Quoted 5 lines

Well, this was exactly my point previously. :-)

Still, although for the majority of devices 'runtime_wakeup' disabled would mean no runtime PM at all IMO, there may be devices that actually work without remote wakeup, although they support it in general.

That last part is quite true. For example, we might suspend the device whenever no process has opened the device file. It would be a degraded form of power management, but better than nothing.

Quoted 2 lines

I can even imagine a scenario where this setting might be useful, like when we don't want a network adapter to be woken up from the outside.

Or if the device's support for remote wakeup is broken.

Alan Stern

#56 lore raw

Re: [RFC] PCI: Runtime power management

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

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Matthew, what makes you think remote wakeup is required? Lots of power-manageable devices don't support it at all (consider disk drives or display screens).

Sorry, I meant in cases where remote wakeup is a sensible concept. For things like storage there's obviously no reason to require it, but for something like USB the absence of remote wakeup would effectively break the driver for most users.

Signature · 1 line
Matthew Garrett | [email protected]
#57 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

Why would you ever want runtime_wakeup to be false unless runtime_forbidden is true? Surely the point of runtime power management is to be transparent to the user, in which case remote wakeup is required?

Matthew, what makes you think remote wakeup is required? Lots of power-manageable devices don't support it at all (consider disk drives or display screens).

Sorry, I meant in cases where remote wakeup is a sensible concept. For things like storage there's obviously no reason to require it, but for something like USB the absence of remote wakeup would effectively break the driver for most users.

Signature · 1 line
Matthew Garrett | [email protected]
maintainer
#58 lore raw

Re: [RFC] PCI: Runtime power management

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

The only remaining question is how to expose this in sysfs in a way that won't be confusing and that won't be confused with the "wakeup" attribute. One possibility is to use the "level" attribute introduced in USB; possible levels are "on" (no runtime PM) and "auto" (runtime PM allowed). Then a new "runtime_wakeup" attribute could contain nothing (if wakeup is not available), "enabled", or "disabled".

That seems to require two flags.

Quoted 8 lines

runtime_forbidden - if unset, the driver decides whether or not to use runtime
PM; that could be exposed through sysfs as 'runtime' under the 'power' subdirectory with the following values:
* 'disabled' - runtime_forbidden is set by the user space
* 'on' - runtime_forbidden is unset, runtime PM is used (disable_depth == 0)
* 'off' - runtime_forbidden is unset, runtime PM is not used
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is unset.

Too confusing. The difference between "disabled" and "off" is so subtle that even I don't understand it!

All we need for this is a simple on/off switch. Or to be consistent with the terms that are already exposed to userspace for USB devices:
"auto"/"on", where "auto" means the system automatically changes power levels (runtime PM is enabled) and "on" means the device is always on (runtime PM is disabled).

And I don't like the "runtime_forbidden" name either. It's long and it's a negative, making it harder to understand. ("off" means that the device is permanently on!) What's wrong with "level", as in "power/level"? That seems very intuitive.

Quoted 8 lines

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

This is okay except for your definition of empty file. This makes it dependent on the power/level setting. The two should be independent. Thus, empty file should mean that the device doesn't support remote wakeup, in which case writes are ignored.

This scheme, like yours, requires two new bitflags. We could call them something like "may_runtime_suspend" and "may_runtime_wakeup".

Alan Stern

maintainer
#59 lore raw

Re: [linux-pm] [RFC] PCI: Runtime power management

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

The only remaining question is how to expose this in sysfs in a way that won't be confusing and that won't be confused with the "wakeup" attribute. One possibility is to use the "level" attribute introduced in USB; possible levels are "on" (no runtime PM) and "auto" (runtime PM allowed). Then a new "runtime_wakeup" attribute could contain nothing (if wakeup is not available), "enabled", or "disabled".

That seems to require two flags.

Quoted 8 lines

runtime_forbidden - if unset, the driver decides whether or not to use runtime
PM; that could be exposed through sysfs as 'runtime' under the 'power' subdirectory with the following values:
* 'disabled' - runtime_forbidden is set by the user space
* 'on' - runtime_forbidden is unset, runtime PM is used (disable_depth == 0)
* 'off' - runtime_forbidden is unset, runtime PM is not used
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is unset.

Too confusing. The difference between "disabled" and "off" is so subtle that even I don't understand it!

All we need for this is a simple on/off switch. Or to be consistent with the terms that are already exposed to userspace for USB devices:
"auto"/"on", where "auto" means the system automatically changes power levels (runtime PM is enabled) and "on" means the device is always on (runtime PM is disabled).

And I don't like the "runtime_forbidden" name either. It's long and it's a negative, making it harder to understand. ("off" means that the device is permanently on!) What's wrong with "level", as in "power/level"? That seems very intuitive.

Quoted 8 lines

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

This is okay except for your definition of empty file. This makes it dependent on the power/level setting. The two should be independent. Thus, empty file should mean that the device doesn't support remote wakeup, in which case writes are ignored.

This scheme, like yours, requires two new bitflags. We could call them something like "may_runtime_suspend" and "may_runtime_wakeup".

Alan Stern

#60 lore raw

Re: [RFC] PCI: Runtime power management

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

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

Quoted 8 lines
Quoted 2 lines

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient.

Perhaps it would, but then unsetting runtime_wakeup would effectively disable runtime PM for devices that need it to be power managed at run time (probably all input devices). Also there may be situations in which user space may really want to disable runtime PM for some devices (think of broken hardware for one example).

It sounds like there are really three choices here, and the decision should largely be left up to the user:

1. don't use runtime PM,

2. allow runtime PM but disable remote wakeup,

3. allow runtime PM with remote wakeup enabled.

Now, a driver may say "I can't do my job without remote wakeup". Such a driver would refuse to do runtime_suspend in case 2. But otherwise we should follow the preference of the user.

Quoted 6 lines

The only remaining question is how to expose this in sysfs in a way that won't be confusing and that won't be confused with the "wakeup" attribute. One possibility is to use the "level" attribute introduced in USB; possible levels are "on" (no runtime PM) and "auto" (runtime PM allowed). Then a new "runtime_wakeup" attribute could contain nothing (if wakeup is not available), "enabled", or "disabled".

That seems to require two flags.

runtime_forbidden - if unset, the driver decides whether or not to use runtime
PM; that could be exposed through sysfs as 'runtime' under the 'power' subdirectory with the following values:
* 'disabled' - runtime_forbidden is set by the user space
* 'on' - runtime_forbidden is unset, runtime PM is used (disable_depth == 0)
* 'off' - runtime_forbidden is unset, runtime PM is not used
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is unset.

runtime_wakeup - if set, the device is allowed to do remote wakeup at run time
That could be represented as 'runtime_wakeup' under 'power' with the following values:
* no value (empty file) is 'runtime' is 'disabled'
* 'enabled'
* 'disabled'
To set/unset the user space writes 'enabled'/'disabled' to it, respectively. The default is set.

Thanks,
Rafael

#61 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett
Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Rafael J. Wysocki

Well, sometimes the user may want a device to be power managed at run time and not to be able to wake up the system from sleep states. For example, I'd like the USB controller in my box to be suspended at run time whenever it's not used, but surely I wouldn't like it to do system-wide wakeup, because it does that when I move the mouse which is a cordless one. Simply turning the mouse on causes the system to wake up. :-)

Quoted 8 lines

Right, so clearly my code is broken right now.

Quoted 3 lines

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

I think even having a runtime_wakeup flag (which defaults to on) would be sufficient.

Perhaps it would, but then unsetting runtime_wakeup would effectively disable runtime PM for devices that need it to be power managed at run time (probably all input devices). Also there may be situations in which user space may really want to disable runtime PM for some devices (think of broken hardware for one example).

Quoted 3 lines

If the worst case is scenario is that we have to resume devices in order to apply the correct policy when going into a systemwide suspend state, I think that's acceptable.

I agree.

Thanks,
Rafael

#62 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Alan Stern
Quoted 34 lines · Alan Stern
Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Alan Stern

You have to call the HCD's pci_suspend method! Not to mention calling synchronize_irq and all the other stuff in hcd_pci_suspend and hcd_pci_suspend_noirq.

The bus level code does this, assuming that the driver-level code doesn't return an error.

So it does; my mistake.

Quoted 9 lines · Matthew Garrett
Quoted 6 lines · Matthew Garrett

Ugh. I'd really prefer us to assume that drivers are able to cope unless proven otherwise. Userspace policy makes sense where we don't have any idea whether something will work or not, but I'd really expect that most PCI drivers will either cope (in which case they'll have enabling code) or won't (in which case they won't). Why would we want userspace to influence this?

Quoted 4 lines

Though, thinking about it, you're right that setting this does override user policy. I think we need an additional flag to indicate that the device supports runtime wakeup and test that as well when doing device_may_wakeup().

You are suggesting separate flag sets for system-wide wakeup and runtime wakeup? I don't disagree, but implementing them will be problematical.

Quoted 5 lines

That's because it's not always possible to change a device's wakeup setting while it is suspended. Thus if a device was runtime suspended with wakeup enabled, and then we want to do a system sleep and change the device's wakeup setting to disabled, we would have to wake the device back up in order to do it.

Well, sometimes the user may want a device to be power managed at run time and not to be able to wake up the system from sleep states. For example, I'd like the USB controller in my box to be suspended at run time whenever it's not used, but surely I wouldn't like it to do system-wide wakeup, because it does that when I move the mouse which is a cordless one. Simply turning the mouse on causes the system to wake up. :-)

So, I don't think we have a choice here. If the user forbids the device to wake up the system from sleep states, we have to do what he wants, even if that means the devices has to be resumed before putting the system into a sleep state.

Why don't we add a flag indicating whether or not the device is allowed to be power managed at run time, something like runtime_forbidden, that the user space will be able to set through sysfs?

If the user space does that, we can't power manage the device at run time, so it can't do runtime wakeup as well. Otherwise, the device is power manageable at run time, which implies runtime wakeup if supported.

Thanks,
Rafael

#63 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett
Quoted 8 lines · On Thu, 13 Aug 2009 01:29:25 +0100 Matthew Garrett <[email protected]> wrote:

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

Quoted 6 lines

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

PCI bits look pretty good to me, though Rafael should take a look too. Card readers and firewire could benefit from similar treatment, maybe that would get us to .5W territory on some machines.

Signature · 1 line
Jesse Barnes, Intel Open Source Technology Center
#64 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett
Quoted 8 lines · On Thu, 13 Aug 2009 01:29:25 +0100 Matthew Garrett <[email protected]> wrote:

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

Quoted 6 lines

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

PCI bits look pretty good to me, though Rafael should take a look too. Card readers and firewire could benefit from similar treatment, maybe that would get us to .5W territory on some machines.

Signature · 1 line
Jesse Barnes, Intel Open Source Technology Center
#65 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 11 lines · Jesse Barnes
Quoted 8 lines · On Thu, 13 Aug 2009 01:29:25 +0100 Matthew Garrett <[email protected]> wrote:

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

Quoted 8 lines
Quoted 6 lines

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

PCI bits look pretty good to me, though Rafael should take a look too.

I'm going to do that shortly.

Quoted 1 line

Card readers and firewire could benefit from similar treatment,

As well us network adapters.

Quoted 1 line

maybe that would get us to .5W territory on some machines.

Hopefully. :-)

Thanks,
Rafael

#66 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Jesse Barnes
Quoted 11 lines · Jesse Barnes
Quoted 8 lines · On Thu, 13 Aug 2009 01:29:25 +0100 Matthew Garrett <[email protected]> wrote:

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

Quoted 8 lines
Quoted 6 lines

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

PCI bits look pretty good to me, though Rafael should take a look too.

I'm going to do that shortly.

Quoted 1 line

Card readers and firewire could benefit from similar treatment,

As well us network adapters.

Quoted 1 line

maybe that would get us to .5W territory on some machines.

Hopefully. :-)

Thanks,
Rafael

#67 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett

Hi,

First of all, thanks a lot for looking into this!

Quoted 8 lines · Matthew Garrett

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

Quoted 6 lines

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

Do you have any prototypes for that? I started working on it some time ago, but then I focused on the core runtime PM framework.

Quoted 21 lines
---
 drivers/pci/pci-acpi.c   |   55 +++++++++++++++++++++++++
 drivers/pci/pci-driver.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.c        |   87 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.h        |    3 +
 include/linux/pci.h      |    3 +
 5 files changed, 248 insertions(+), 0 deletions(-)
drivers/pci/pci-acpi.c +1 -0
index ea15b05..a98a777 100644
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/pci-aspm.h>
+#include <linux/pm_runtime.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 129

Quoted 9 lines
drivers/pci/pci-acpi.c +6 -0
@@ -120,14 +121,62 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
 	return error;
 }
 
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+

Hm, I'd move that into ACPI as

int acp_runtime_wake_enable(acpi_handle handle, bool enable)

in which form it could also be useful to non-PCI devices.

Quoted 12 lines
drivers/pci/pci-acpi.c +12 -0
+	if (!handle)
+		return -ENODEV;
+
+	status = acpi_bus_get_device(handle, &acpi_dev);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	if (enable) {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE_RUN);
+		acpi_enable_gpe(acpi_dev->wakeup.gpe_device,

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 151

Quoted 10 lines
drivers/pci/pci-acpi.c +10 -0
+				acpi_dev->wakeup.gpe_number);
+	} else {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE);
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}

Ah, that's the part I've always been missing!

How exactly do we figure out which GPE is a wake-up one for given device? IOW, how are the wakeup.gpe_device and wakeup.gpe_number fields populated?

Quoted 85 lines
drivers/pci/pci-acpi.c +26 -0
+
+
 static struct pci_platform_pm_ops acpi_pci_platform_pm = {
 	.is_manageable = acpi_pci_power_manageable,
 	.set_state = acpi_pci_set_power_state,
 	.choose_state = acpi_pci_choose_state,
 	.can_wakeup = acpi_pci_can_wakeup,
 	.sleep_wake = acpi_pci_sleep_wake,
+	.runtime_wake = acpi_pci_runtime_wake,
 };
 
+static void pci_device_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pm_runtime_resume(dev);
+}
+
+static void pci_root_bridge_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pci_bus_pme_event(pci_dev);
+}
+
 /* ACPI bus type */
 static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 {
@@ -140,6 +189,9 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_device_notify, dev);
 	return 0;
 }
 
@@ -158,6 +210,9 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_pci_rootbridge_handle(seg, bus);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_root_bridge_notify, dev);
 	return 0;
 }
 
drivers/pci/pci-driver.c +13 -0
index d76c4c8..1f605d8 100644
@@ -11,12 +11,14 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/mempolicy.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/cpu.h>
+#include <linux/pm_runtime.h>
 #include "pci.h"
 
 /*
@@ -910,6 +912,101 @@ static int pci_pm_restore(struct device *dev)
 
 #endif /* !CONFIG_HIBERNATION */
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error;
+
+	device_set_wakeup_enable(dev, 1);
+	error = pci_enable_runtime_wake(pci_dev, true);
+

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+	if (error)
+		return -EBUSY;
+
+	if (pm && pm->runtime_suspend)
+		error = pm->runtime_suspend(dev);
+
+	if (error)
+		goto out;
+
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 940

Quoted 5 lines
drivers/pci/pci-driver.c +5 -0
+
+	if (error)
+		goto resume;
+
+	disable_irq(pci_dev->irq);

I don't really think it's necessary to disable the interrupt here. We prevent drivers from receiving interrupts while pci_pm_suspend_noirq() is being run during system-wide power transitions to protect them from receiving "alien" interrupts they might be unable to handle, but in the runtime case I think the driver should take care of protecting itself from that.

The generic part of pci_pm_suspend_noirq() doesn't do things that require interrupts to be disabled. The driver's ->suspend_noirq() may do such things in principle, but then I'd prefer it not to be called directly from here.

IMO pci_pm_runtime_suspend() should work like a combination of pci_pm_suspend() and pci_pm_suspend_noirq(), except that instead of executing two driver callbacks it will only call one ->runtime_suspend() callback without disabling the device interrupt.

If the driver has to disable its interrupt in ->runtime_suspend(), it should do that by itself.

Of course, analogous comment apply to pci_pm_runtime_resume() below.

Quoted 50 lines
drivers/pci/pci-driver.c +50 -0
+	error = pci_pm_suspend_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		goto resume_noirq;
+
+	return 0;
+
+resume_noirq:
+	disable_irq(pci_dev->irq);
+	pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+resume:
+	pci_pm_resume(dev);
+out:
+	pci_enable_runtime_wake(pci_dev, false);
+	return error;
+}
+
+static int pci_pm_runtime_resume(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error = 0;
+
+	disable_irq(pci_dev->irq);
+	error = pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		return error;
+
+	error = pci_pm_resume(dev);
+
+	if (error)
+		return error;
+
+	if (pm->runtime_resume)
+		error = pm->runtime_resume(dev);
+
+	if (error)
+		return error;
+
+	error = pci_enable_runtime_wake(pci_dev, false);
+
+	if (error)
+		return error;
+
+	return 0;
+}

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 1000

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+
+static void pci_pm_runtime_idle(struct device *dev)
+{
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->runtime_idle)
+		pm->runtime_idle(dev);
+
+	pm_schedule_suspend(dev, 0);
+}

That has already been discussed.

Quoted 65 lines
drivers/pci/pci-driver.c +12 -0
+
+#else /* !CONFIG_PM_RUNTIME */
+
+#define pci_pm_runtime_suspend	NULL
+#define pci_pm_runtime_resume	NULL
+#define pci_pm_runtime_idle	NULL
+
+#endif
+
 struct dev_pm_ops pci_dev_pm_ops = {
 	.prepare = pci_pm_prepare,
 	.complete = pci_pm_complete,
@@ -925,6 +1022,9 @@ struct dev_pm_ops pci_dev_pm_ops = {
 	.thaw_noirq = pci_pm_thaw_noirq,
 	.poweroff_noirq = pci_pm_poweroff_noirq,
 	.restore_noirq = pci_pm_restore_noirq,
+	.runtime_suspend = pci_pm_runtime_suspend,
+	.runtime_resume = pci_pm_runtime_resume,
+	.runtime_idle = pci_pm_runtime_idle,
 };
 
 #define PCI_PM_OPS_PTR	(&pci_dev_pm_ops)
drivers/pci/pci.c +21 -0
index dbd0f94..ab3a116 100644
@@ -18,6 +18,7 @@
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
+#include <linux/pm_runtime.h>
 #include <linux/interrupt.h>
 #include <asm/dma.h>	/* isa_dma_bridge_buggy */
 #include <linux/device.h>
@@ -428,6 +429,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
 			pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
 }
 
+static inline int platform_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	return pci_platform_pm ?
+			pci_platform_pm->runtime_wake(dev, enable) : -ENODEV;
+}
+
 /**
  * pci_raw_set_power_state - Use PCI PM registers to set the power state of
  *                           given PCI device
@@ -1239,6 +1246,38 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
 }
 
 /**
+ * pci_enable_runtime_wake - enable PCI device as runtime wakeup event source
+ * @dev: PCI device affected
+ * @enable: True to enable event generation; false to disable
+ *
+ * This enables the device as a runtime wakeup event source, or disables it.
+ * This typically requires platform support.
+ *
+ * RETURN VALUE:
+ * 0 is returned on success
+ * -EINVAL is returned if device is not supposed to wake up the system
+ * -ENODEV is returned if platform cannot support runtime PM on the device
+ */
+int pci_enable_runtime_wake(struct pci_dev *dev, bool enable)
+{

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1272

Quoted 10 lines
drivers/pci/pci.c +10 -0
+	int error = 0;
+	bool pme_done = false;
+
+	if (!enable && platform_pci_can_wakeup(dev))
+		error = platform_pci_runtime_wake(dev, false);
+
+	if (!enable || pci_pme_capable(dev, PCI_D3hot)) {
+		pci_pme_active(dev, enable);
+		pme_done = true;
+	}

I don't really follow your intention here. The condition means that PME is going to be enabled unless 'enable' is set and the device is not capable of generating PMEs. However, if 'enable' is unset, we're still going to try to enable the PME, even if the device can't generate it. Shouldn't that be

if (enable && pci_pme_capable(dev, PCI_D3hot)) ?

Also, that assumes the device is going to be put into D3_hot, but do we know that for sure?

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1278

Quoted 6 lines
drivers/pci/pci.c +6 -0
+
+	if (enable && platform_pci_can_wakeup(dev))
+		error = platform_pci_runtime_wake(dev, true);
+
+	return pme_done ? 0 : error;
+}

I have no comments to the part below.

Quoted 88 lines
drivers/pci/pci.c +49 -0
+/**
  * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
  * @dev: PCI device to prepare
  * @enable: True to enable wake-up event generation; false to disable
@@ -1346,6 +1385,54 @@ int pci_back_from_sleep(struct pci_dev *dev)
 }
 
 /**
+ * pci_dev_pme_event - check if a device has a pending pme
+ *
+ * @dev: Device to handle.
+ */
+
+int pci_dev_pme_event(struct pci_dev *dev)
+{
+	u16 pmcsr;
+
+	if (!dev->pm_cap)
+		return -ENODEV;
+
+	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+
+	if (pmcsr & PCI_PM_CTRL_PME_STATUS) {
+		pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
+		pm_runtime_get(&dev->dev);
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+/**
+ * pci_bus_pme_event - search for subordinate devices with a pending
+ *		   pme and handle them
+ *
+ * @dev: Parent device to handle
+ */
+int pci_bus_pme_event(struct pci_dev *dev)
+{
+	struct pci_bus *bus;
+	struct pci_dev *pdev;
+
+	if (pci_is_root_bus(dev->bus))
+		bus = dev->bus;
+	else if (dev->subordinate)
+		bus = dev->subordinate;
+	else
+		return -ENODEV;
+
+	list_for_each_entry(pdev, &bus->devices, bus_list)
+		pci_dev_pme_event(pdev);
+
+	return 0;
+}
+
+/**
  * pci_pm_init - Initialize PM functions of given PCI device
  * @dev: PCI device to handle.
  */
drivers/pci/pci.h +3 -0
index f73bcbe..a81aff2 100644
@@ -34,6 +34,8 @@ extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
  *
  * @sleep_wake: enables/disables the system wake up capability of given device
  *
+ * @runtime_wake: enables/disables the runtime wakeup capability of given device
+ *
  * If given platform is generally capable of power managing PCI devices, all of
  * these callbacks are mandatory.
  */
@@ -43,6 +45,7 @@ struct pci_platform_pm_ops {
 	pci_power_t (*choose_state)(struct pci_dev *dev);
 	bool (*can_wakeup)(struct pci_dev *dev);
 	int (*sleep_wake)(struct pci_dev *dev, bool enable);
+	int (*runtime_wake)(struct pci_dev *dev, bool enable);
 };
 
 extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
include/linux/pci.h +0 -0
index 115fb7b..8a3fea0 100644
@@ -734,10 +734,13 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
 void pci_pme_active(struct pci_dev *dev, bool enable);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);

include/linux/pci.h pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); · line 746

Quoted 10 lines
include/linux/pci.h +3 -0
+int pci_enable_runtime_wake(struct pci_dev *dev, bool enable);
 int pci_wake_from_d3(struct pci_dev *dev, bool enable);
 pci_power_t pci_target_state(struct pci_dev *dev);
 int pci_prepare_to_sleep(struct pci_dev *dev);
 int pci_back_from_sleep(struct pci_dev *dev);
+int pci_dev_pme_event(struct pci_dev *dev);
+int pci_bus_pme_event(struct pci_dev *dev);
 
 /* Functions for PCI Hotplug drivers to use */
 int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);

Thanks,
Rafael

#68 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 2 lines · Rafael J. Wysocki

Do you have any prototypes for that? I started working on it some time ago, but then I focused on the core runtime PM framework.

The native PCIe PME code? There's some in the final patchset at
http://bugzilla.kernel.org/show_bug.cgi?id=6892 but I haven't had time to look into merging that into the current kernel. I also don't have anything to test against, which makes life more awkward.

Quoted 9 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 129

Quoted 6 lines
drivers/pci/pci-acpi.c +6 -0
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+

Hm, I'd move that into ACPI as

Quoted 3 lines

int acp_runtime_wake_enable(acpi_handle handle, bool enable)

in which form it could also be useful to non-PCI devices.

Hm. Yeah, that's not too bad an idea.

Quoted 10 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 151

Quoted 5 lines
drivers/pci/pci-acpi.c +5 -0
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}

Ah, that's the part I've always been missing!

How exactly do we figure out which GPE is a wake-up one for given device? IOW, how are the wakeup.gpe_device and wakeup.gpe_number fields populated?

There's a field in the ACPI device definition in the DSDT that defines the needed GPE and which runlevels it can resume from.

Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 1 line
drivers/pci/pci-driver.c +1 -0
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

Yes, that was how I'd planned it. An alternative would be for runtime_suspend to return a negative value if there's an error, 0 if the bus code should continue or a positive value if the runtime_suspend() call handles all of it and the bus code should just return immediately?

Quoted 7 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	disable_irq(pci_dev->irq);

I don't really think it's necessary to disable the interrupt here. We prevent drivers from receiving interrupts while pci_pm_suspend_noirq() is being run during system-wide power transitions to protect them from receiving "alien" interrupts they might be unable to handle, but in the runtime case I think the driver should take care of protecting itself from that.

That sounds fine. I didn't want to take a risk in that respect, but if we should be safe here I can just drop that.

Quoted 10 lines

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1272

Quoted 4 lines
drivers/pci/pci.c +4 -0
+	if (!enable || pci_pme_capable(dev, PCI_D3hot)) {
+		pci_pme_active(dev, enable);
+		pme_done = true;
+	}

I don't really follow your intention here. The condition means that PME is going to be enabled unless 'enable' is set and the device is not capable of generating PMEs. However, if 'enable' is unset, we're still going to try to enable the PME, even if the device can't generate it. Shouldn't that be

Hmm. That was copied from pci_enable_wake() just above, but it does seem a little bit odd. I suspect that that needs some clarification as well.

Quoted 2 lines

Also, that assumes the device is going to be put into D3_hot, but do we know that for sure?

I'd be surprised if there's any hardware that supports wakeups from D2 but not D3hot, so I just kept the code simple for now.

Signature · 1 line
Matthew Garrett | [email protected]
#69 lore raw

Re: [RFC] PCI: Runtime power management

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

Do you have any prototypes for that? I started working on it some time ago, but then I focused on the core runtime PM framework.

The native PCIe PME code? There's some in the final patchset at
http://bugzilla.kernel.org/show_bug.cgi?id=6892 but I haven't had time to look into merging that into the current kernel. I also don't have anything to test against, which makes life more awkward.

Quoted 9 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 129

Quoted 6 lines
drivers/pci/pci-acpi.c +6 -0
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+

Hm, I'd move that into ACPI as

Quoted 3 lines

int acp_runtime_wake_enable(acpi_handle handle, bool enable)

in which form it could also be useful to non-PCI devices.

Hm. Yeah, that's not too bad an idea.

Quoted 10 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 151

Quoted 5 lines
drivers/pci/pci-acpi.c +5 -0
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}

Ah, that's the part I've always been missing!

How exactly do we figure out which GPE is a wake-up one for given device? IOW, how are the wakeup.gpe_device and wakeup.gpe_number fields populated?

There's a field in the ACPI device definition in the DSDT that defines the needed GPE and which runlevels it can resume from.

Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 1 line
drivers/pci/pci-driver.c +1 -0
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

Yes, that was how I'd planned it. An alternative would be for runtime_suspend to return a negative value if there's an error, 0 if the bus code should continue or a positive value if the runtime_suspend() call handles all of it and the bus code should just return immediately?

Quoted 7 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	disable_irq(pci_dev->irq);

I don't really think it's necessary to disable the interrupt here. We prevent drivers from receiving interrupts while pci_pm_suspend_noirq() is being run during system-wide power transitions to protect them from receiving "alien" interrupts they might be unable to handle, but in the runtime case I think the driver should take care of protecting itself from that.

That sounds fine. I didn't want to take a risk in that respect, but if we should be safe here I can just drop that.

Quoted 10 lines

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1272

Quoted 4 lines
drivers/pci/pci.c +4 -0
+	if (!enable || pci_pme_capable(dev, PCI_D3hot)) {
+		pci_pme_active(dev, enable);
+		pme_done = true;
+	}

I don't really follow your intention here. The condition means that PME is going to be enabled unless 'enable' is set and the device is not capable of generating PMEs. However, if 'enable' is unset, we're still going to try to enable the PME, even if the device can't generate it. Shouldn't that be

Hmm. That was copied from pci_enable_wake() just above, but it does seem a little bit odd. I suspect that that needs some clarification as well.

Quoted 2 lines

Also, that assumes the device is going to be put into D3_hot, but do we know that for sure?

I'd be surprised if there's any hardware that supports wakeups from D2 but not D3hot, so I just kept the code simple for now.

Signature · 1 line
Matthew Garrett | [email protected]
#70 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 6 lines · Matthew Garrett
Quoted 2 lines · Rafael J. Wysocki

Do you have any prototypes for that? I started working on it some time ago, but then I focused on the core runtime PM framework.

The native PCIe PME code?

Yes.

Quoted 2 lines

There's some in the final patchset at
http://bugzilla.kernel.org/show_bug.cgi?id=6892

Well, I don't like that very much.

Quoted 2 lines

but I haven't had time to look into merging that into the current kernel. I also don't have anything to test against, which makes life more awkward.

One of my AMD-based boxes should be suitable for that.

Quoted 37 lines
Quoted 9 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 129

Quoted 6 lines
drivers/pci/pci-acpi.c +6 -0
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+

Hm, I'd move that into ACPI as

Quoted 3 lines

int acp_runtime_wake_enable(acpi_handle handle, bool enable)

in which form it could also be useful to non-PCI devices.

Hm. Yeah, that's not too bad an idea.

Quoted 10 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 151

Quoted 5 lines
drivers/pci/pci-acpi.c +5 -0
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}

Ah, that's the part I've always been missing!

How exactly do we figure out which GPE is a wake-up one for given device? IOW, how are the wakeup.gpe_device and wakeup.gpe_number fields populated?

There's a field in the ACPI device definition in the DSDT that defines the needed GPE and which runlevels it can resume from.

Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 1 line
drivers/pci/pci-driver.c +1 -0
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

Quoted 4 lines

Yes, that was how I'd planned it. An alternative would be for runtime_suspend to return a negative value if there's an error, 0 if the bus code should continue or a positive value if the runtime_suspend() call handles all of it and the bus code should just return immediately?

I just don't think that the existing suspend-resume callbacks are suitable for runtime PM.

For example, in the majority of cases the existing suspend callbacks put devices into D3_hot (or into the deepest low power state allowed by the platform and hardware), but that means 10 ms latency, also for the resume part. Do we want that for runtime PM for all drivers?

Perhaps a more suitable model would be to put devices into D1 first, if available, and then put them into D2 and D3 after specific delays? Currently the core framework doesn't provide any tools for that, but it may be worth extending it for this purpose.

Also, I think it should be impossible to use the "legacy" callbacks for runtime PM. They surely are not designed with that in mind.

Quoted 10 lines
Quoted 7 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	disable_irq(pci_dev->irq);

I don't really think it's necessary to disable the interrupt here. We prevent drivers from receiving interrupts while pci_pm_suspend_noirq() is being run during system-wide power transitions to protect them from receiving "alien" interrupts they might be unable to handle, but in the runtime case I think the driver should take care of protecting itself from that.

That sounds fine. I didn't want to take a risk in that respect, but if we should be safe here I can just drop that.

As far as the PCI PM core is concerned, we should.

Quoted 11 lines
Quoted 10 lines

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1272

Quoted 4 lines
drivers/pci/pci.c +4 -0
+	if (!enable || pci_pme_capable(dev, PCI_D3hot)) {
+		pci_pme_active(dev, enable);
+		pme_done = true;
+	}

I don't really follow your intention here. The condition means that PME is going to be enabled unless 'enable' is set and the device is not capable of generating PMEs. However, if 'enable' is unset, we're still going to try to enable the PME, even if the device can't generate it. Shouldn't that be

Quoted 2 lines

Hmm. That was copied from pci_enable_wake() just above, but it does seem a little bit odd. I suspect that that needs some clarification as well.

Ah, OK. If 'enabled' is unset, we want to disable the PME for all states, so we call pci_pm_active(dev, false) unconditionally in that case. If 'enable' is set, we only want to enable the PME if it's supported in given state. Sorry for the noise.

Quoted 5 lines
Quoted 2 lines

Also, that assumes the device is going to be put into D3_hot, but do we know that for sure?

I'd be surprised if there's any hardware that supports wakeups from D2 but not D3hot, so I just kept the code simple for now.

OK

Thanks,
Rafael

#71 lore raw

Re: [RFC] PCI: Runtime power management

Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Matthew Garrett

On Fri, Aug 14, 2009 at 11:22:27PM +0200, Rafael J. Wysocki wrote:

...

Quoted 21 lines
Quoted 8 lines
Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 1 line
drivers/pci/pci-driver.c +1 -0
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

Quoted 4 lines

Yes, that was how I'd planned it. An alternative would be for runtime_suspend to return a negative value if there's an error, 0 if the bus code should continue or a positive value if the runtime_suspend() call handles all of it and the bus code should just return immediately?

I just don't think that the existing suspend-resume callbacks are suitable for runtime PM.

For example, in the majority of cases the existing suspend callbacks put devices into D3_hot (or into the deepest low power state allowed by the platform and hardware), but that means 10 ms latency, also for the resume part. Do we want that for runtime PM for all drivers?

Quoted 7 lines

Perhaps a more suitable model would be to put devices into D1 first, if available, and then put them into D2 and D3 after specific delays? Currently the core framework doesn't provide any tools for that, but it may be worth extending it for this purpose.

Also, I think it should be impossible to use the "legacy" callbacks for runtime PM. They surely are not designed with that in mind.

To be more specific, let's go through pci_pm_suspend() and pci_pm_suspend_noirq() and see what parts of these are useful for runtime PM.

pci_legacy_suspend() shouldn't be used at run time IMO, as I said above.

For runtime PM we shouldn't even continue if pm is NULL. There's nothing like "default runtime PM", either the driver supports it, or not. We also should require the callback to be implemented and IMO that should be ->runtime_suspend().

Of course we need to invoke the callback and call pci_fixup_device() after that.

pci_legacy_suspend_late() shouldn't be used at run time.

Since I think we should require pm to be not NULL for runtime PM, the second block in pci_pm_suspend_noirq() is redundant in that case.

Now, I don't think we need the _noirq callback for runtime PM, because we've just executed the "regular" callback and I bet there are no devices requiring additional driver-specific operations after pci_fixup_device(). So ->runtime_suspend() should be sufficient.

The remaining part of pci_pm_suspend_noirq() is useful, so it can be executed by pci_pm_runtime_suspend() directly.

Thus we get

static int pci_pm_runtime_suspend(struct device *dev)

{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
	int error;

	if (!pm || !pm->runtime_suspend)
		return -ENOSYS;

	pci_dev->state_saved = false;

	error = pm->runtime_suspend(dev);
	suspend_report_result(pm->runtime_suspend, error);
	if (error)
		return error;

	pci_fixup_device(pci_fixup_suspend, pci_dev);

	if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
	    && pci_dev->current_state != PCI_UNKNOWN) {
		WARN_ONCE(pci_dev->current_state != prev,
				"PCI PM: State of device not saved by %pF\n",
				pm->runtime_suspend);
		return 0;
	}

	if (!pci_dev->state_saved) {
		pci_save_state(pci_dev);
		if (!pci_is_bridge(pci_dev))
			pci_prepare_to_sleep(pci_dev);
	}

	pci_pm_set_unknown_state(pci_dev);

	return 0;
}

Now, if the driver has a universal suspend routine, like for example r8169, it only needs to point .runtime_suspend to that one and it should work.

Thanks,
Rafael

#72 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Rafael J. Wysocki
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line · Matthew Garrett

On Fri, Aug 14, 2009 at 11:22:27PM +0200, Rafael J. Wysocki wrote:

...

Quoted 21 lines
Quoted 8 lines
Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 1 line
drivers/pci/pci-driver.c +1 -0
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

Quoted 4 lines

Yes, that was how I'd planned it. An alternative would be for runtime_suspend to return a negative value if there's an error, 0 if the bus code should continue or a positive value if the runtime_suspend() call handles all of it and the bus code should just return immediately?

I just don't think that the existing suspend-resume callbacks are suitable for runtime PM.

For example, in the majority of cases the existing suspend callbacks put devices into D3_hot (or into the deepest low power state allowed by the platform and hardware), but that means 10 ms latency, also for the resume part. Do we want that for runtime PM for all drivers?

Quoted 7 lines

Perhaps a more suitable model would be to put devices into D1 first, if available, and then put them into D2 and D3 after specific delays? Currently the core framework doesn't provide any tools for that, but it may be worth extending it for this purpose.

Also, I think it should be impossible to use the "legacy" callbacks for runtime PM. They surely are not designed with that in mind.

To be more specific, let's go through pci_pm_suspend() and pci_pm_suspend_noirq() and see what parts of these are useful for runtime PM.

pci_legacy_suspend() shouldn't be used at run time IMO, as I said above.

For runtime PM we shouldn't even continue if pm is NULL. There's nothing like "default runtime PM", either the driver supports it, or not. We also should require the callback to be implemented and IMO that should be ->runtime_suspend().

Of course we need to invoke the callback and call pci_fixup_device() after that.

pci_legacy_suspend_late() shouldn't be used at run time.

Since I think we should require pm to be not NULL for runtime PM, the second block in pci_pm_suspend_noirq() is redundant in that case.

Now, I don't think we need the _noirq callback for runtime PM, because we've just executed the "regular" callback and I bet there are no devices requiring additional driver-specific operations after pci_fixup_device(). So ->runtime_suspend() should be sufficient.

The remaining part of pci_pm_suspend_noirq() is useful, so it can be executed by pci_pm_runtime_suspend() directly.

Thus we get

static int pci_pm_runtime_suspend(struct device *dev)

{
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
	int error;

	if (!pm || !pm->runtime_suspend)
		return -ENOSYS;

	pci_dev->state_saved = false;

	error = pm->runtime_suspend(dev);
	suspend_report_result(pm->runtime_suspend, error);
	if (error)
		return error;

	pci_fixup_device(pci_fixup_suspend, pci_dev);

	if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
	    && pci_dev->current_state != PCI_UNKNOWN) {
		WARN_ONCE(pci_dev->current_state != prev,
				"PCI PM: State of device not saved by %pF\n",
				pm->runtime_suspend);
		return 0;
	}

	if (!pci_dev->state_saved) {
		pci_save_state(pci_dev);
		if (!pci_is_bridge(pci_dev))
			pci_prepare_to_sleep(pci_dev);
	}

	pci_pm_set_unknown_state(pci_dev);

	return 0;
}

Now, if the driver has a universal suspend routine, like for example r8169, it only needs to point .runtime_suspend to that one and it should work.

Thanks,
Rafael

#73 lore raw

Re: [RFC] PCI: Runtime power management

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

Do you have any prototypes for that? I started working on it some time ago, but then I focused on the core runtime PM framework.

The native PCIe PME code?

Yes.

Quoted 2 lines

There's some in the final patchset at
http://bugzilla.kernel.org/show_bug.cgi?id=6892

Well, I don't like that very much.

Quoted 2 lines

but I haven't had time to look into merging that into the current kernel. I also don't have anything to test against, which makes life more awkward.

One of my AMD-based boxes should be suitable for that.

Quoted 37 lines
Quoted 9 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 129

Quoted 6 lines
drivers/pci/pci-acpi.c +6 -0
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+

Hm, I'd move that into ACPI as

Quoted 3 lines

int acp_runtime_wake_enable(acpi_handle handle, bool enable)

in which form it could also be useful to non-PCI devices.

Hm. Yeah, that's not too bad an idea.

Quoted 10 lines

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 151

Quoted 5 lines
drivers/pci/pci-acpi.c +5 -0
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}

Ah, that's the part I've always been missing!

How exactly do we figure out which GPE is a wake-up one for given device? IOW, how are the wakeup.gpe_device and wakeup.gpe_number fields populated?

There's a field in the ACPI device definition in the DSDT that defines the needed GPE and which runlevels it can resume from.

Quoted 7 lines

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 1 line
drivers/pci/pci-driver.c +1 -0
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

Quoted 4 lines

Yes, that was how I'd planned it. An alternative would be for runtime_suspend to return a negative value if there's an error, 0 if the bus code should continue or a positive value if the runtime_suspend() call handles all of it and the bus code should just return immediately?

I just don't think that the existing suspend-resume callbacks are suitable for runtime PM.

For example, in the majority of cases the existing suspend callbacks put devices into D3_hot (or into the deepest low power state allowed by the platform and hardware), but that means 10 ms latency, also for the resume part. Do we want that for runtime PM for all drivers?

Perhaps a more suitable model would be to put devices into D1 first, if available, and then put them into D2 and D3 after specific delays? Currently the core framework doesn't provide any tools for that, but it may be worth extending it for this purpose.

Also, I think it should be impossible to use the "legacy" callbacks for runtime PM. They surely are not designed with that in mind.

Quoted 10 lines
Quoted 7 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	disable_irq(pci_dev->irq);

I don't really think it's necessary to disable the interrupt here. We prevent drivers from receiving interrupts while pci_pm_suspend_noirq() is being run during system-wide power transitions to protect them from receiving "alien" interrupts they might be unable to handle, but in the runtime case I think the driver should take care of protecting itself from that.

That sounds fine. I didn't want to take a risk in that respect, but if we should be safe here I can just drop that.

As far as the PCI PM core is concerned, we should.

Quoted 11 lines
Quoted 10 lines

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1272

Quoted 4 lines
drivers/pci/pci.c +4 -0
+	if (!enable || pci_pme_capable(dev, PCI_D3hot)) {
+		pci_pme_active(dev, enable);
+		pme_done = true;
+	}

I don't really follow your intention here. The condition means that PME is going to be enabled unless 'enable' is set and the device is not capable of generating PMEs. However, if 'enable' is unset, we're still going to try to enable the PME, even if the device can't generate it. Shouldn't that be

Quoted 2 lines

Hmm. That was copied from pci_enable_wake() just above, but it does seem a little bit odd. I suspect that that needs some clarification as well.

Ah, OK. If 'enabled' is unset, we want to disable the PME for all states, so we call pci_pm_active(dev, false) unconditionally in that case. If 'enable' is set, we only want to enable the PME if it's supported in given state. Sorry for the noise.

Quoted 5 lines
Quoted 2 lines

Also, that assumes the device is going to be put into D3_hot, but do we know that for sure?

I'd be surprised if there's any hardware that supports wakeups from D2 but not D3hot, so I just kept the code simple for now.

OK

Thanks,
Rafael

#74 lore raw

Re: [RFC] PCI: Runtime power management

In reply to Matthew Garrett

Hi,

First of all, thanks a lot for looking into this!

Quoted 8 lines · Matthew Garrett

I got a fixed BIOS from Dell and have been able to get this working now. It seems entirely happy with USB, but I'd like some sanity checks on whether I'm doing this correctly. There's certainly a couple of quirks related to setting the ACPI GPE type that would need a little bit of work in the ACPI layer, and it breaks ACPI-mediated PCI hotplug though that's easy enough to fix by just calling into the hotplug code from the core notifier.

Quoted 6 lines

This patch builds on top of Rafael's work on systemwide runtime power management. It supports suspending and resuming PCI devices at runtime, enabling platform wakeup events that allow the devices to automatically resume when appropriate. It currently requires platform support, but PCIe setups could be supported natively once native PCIe PME code has been added to the kernel.

Do you have any prototypes for that? I started working on it some time ago, but then I focused on the core runtime PM framework.

Quoted 21 lines
---
 drivers/pci/pci-acpi.c   |   55 +++++++++++++++++++++++++
 drivers/pci/pci-driver.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.c        |   87 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.h        |    3 +
 include/linux/pci.h      |    3 +
 5 files changed, 248 insertions(+), 0 deletions(-)
drivers/pci/pci-acpi.c +1 -0
index ea15b05..a98a777 100644
@@ -12,6 +12,7 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/pci-aspm.h>
+#include <linux/pm_runtime.h>
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 129

Quoted 9 lines
drivers/pci/pci-acpi.c +6 -0
@@ -120,14 +121,62 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
 	return error;
 }
 
+static int acpi_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	acpi_status status;
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	struct acpi_device *acpi_dev;
+

Hm, I'd move that into ACPI as

int acp_runtime_wake_enable(acpi_handle handle, bool enable)

in which form it could also be useful to non-PCI devices.

Quoted 12 lines
drivers/pci/pci-acpi.c +12 -0
+	if (!handle)
+		return -ENODEV;
+
+	status = acpi_bus_get_device(handle, &acpi_dev);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	if (enable) {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE_RUN);
+		acpi_enable_gpe(acpi_dev->wakeup.gpe_device,

drivers/pci/pci-acpi.c static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) · line 151

Quoted 10 lines
drivers/pci/pci-acpi.c +10 -0
+				acpi_dev->wakeup.gpe_number);
+	} else {
+		acpi_set_gpe_type(acpi_dev->wakeup.gpe_device,
+				  acpi_dev->wakeup.gpe_number,
+				  ACPI_GPE_TYPE_WAKE);
+		acpi_disable_gpe(acpi_dev->wakeup.gpe_device,
+				 acpi_dev->wakeup.gpe_number);
+	}
+	return 0;
+}

Ah, that's the part I've always been missing!

How exactly do we figure out which GPE is a wake-up one for given device? IOW, how are the wakeup.gpe_device and wakeup.gpe_number fields populated?

Quoted 85 lines
drivers/pci/pci-acpi.c +26 -0
+
+
 static struct pci_platform_pm_ops acpi_pci_platform_pm = {
 	.is_manageable = acpi_pci_power_manageable,
 	.set_state = acpi_pci_set_power_state,
 	.choose_state = acpi_pci_choose_state,
 	.can_wakeup = acpi_pci_can_wakeup,
 	.sleep_wake = acpi_pci_sleep_wake,
+	.runtime_wake = acpi_pci_runtime_wake,
 };
 
+static void pci_device_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pm_runtime_resume(dev);
+}
+
+static void pci_root_bridge_notify(acpi_handle handle, u32 event, void *data)
+{
+	struct device *dev = data;
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+
+	if (event == ACPI_NOTIFY_DEVICE_WAKE)
+		pci_bus_pme_event(pci_dev);
+}
+
 /* ACPI bus type */
 static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 {
@@ -140,6 +189,9 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_device_notify, dev);
 	return 0;
 }
 
@@ -158,6 +210,9 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle)
 	*handle = acpi_get_pci_rootbridge_handle(seg, bus);
 	if (!*handle)
 		return -ENODEV;
+
+	acpi_install_notify_handler(*handle, ACPI_SYSTEM_NOTIFY,
+				    pci_root_bridge_notify, dev);
 	return 0;
 }
 
drivers/pci/pci-driver.c +13 -0
index d76c4c8..1f605d8 100644
@@ -11,12 +11,14 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/mempolicy.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/cpu.h>
+#include <linux/pm_runtime.h>
 #include "pci.h"
 
 /*
@@ -910,6 +912,101 @@ static int pci_pm_restore(struct device *dev)
 
 #endif /* !CONFIG_HIBERNATION */
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error;
+
+	device_set_wakeup_enable(dev, 1);
+	error = pci_enable_runtime_wake(pci_dev, true);
+

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 935

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+	if (error)
+		return -EBUSY;
+
+	if (pm && pm->runtime_suspend)
+		error = pm->runtime_suspend(dev);
+
+	if (error)
+		goto out;
+
+	error = pci_pm_suspend(dev);

This has a chance to be confusing IMO. pci_pm_suspend() calls the driver's ->suspend() routine, which is specific to suspend to RAM. So, this means that drivers are supposed to implement ->runtime_suspend() only if they want to do something _in_ _addition_ to the things done by ->suspend() and ->suspend_noirq().

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 940

Quoted 5 lines
drivers/pci/pci-driver.c +5 -0
+
+	if (error)
+		goto resume;
+
+	disable_irq(pci_dev->irq);

I don't really think it's necessary to disable the interrupt here. We prevent drivers from receiving interrupts while pci_pm_suspend_noirq() is being run during system-wide power transitions to protect them from receiving "alien" interrupts they might be unable to handle, but in the runtime case I think the driver should take care of protecting itself from that.

The generic part of pci_pm_suspend_noirq() doesn't do things that require interrupts to be disabled. The driver's ->suspend_noirq() may do such things in principle, but then I'd prefer it not to be called directly from here.

IMO pci_pm_runtime_suspend() should work like a combination of pci_pm_suspend() and pci_pm_suspend_noirq(), except that instead of executing two driver callbacks it will only call one ->runtime_suspend() callback without disabling the device interrupt.

If the driver has to disable its interrupt in ->runtime_suspend(), it should do that by itself.

Of course, analogous comment apply to pci_pm_runtime_resume() below.

Quoted 50 lines
drivers/pci/pci-driver.c +50 -0
+	error = pci_pm_suspend_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		goto resume_noirq;
+
+	return 0;
+
+resume_noirq:
+	disable_irq(pci_dev->irq);
+	pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+resume:
+	pci_pm_resume(dev);
+out:
+	pci_enable_runtime_wake(pci_dev, false);
+	return error;
+}
+
+static int pci_pm_runtime_resume(struct device *dev)
+{
+	struct pci_dev *pci_dev = to_pci_dev(dev);
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+	int error = 0;
+
+	disable_irq(pci_dev->irq);
+	error = pci_pm_resume_noirq(dev);
+	enable_irq(pci_dev->irq);
+
+	if (error)
+		return error;
+
+	error = pci_pm_resume(dev);
+
+	if (error)
+		return error;
+
+	if (pm->runtime_resume)
+		error = pm->runtime_resume(dev);
+
+	if (error)
+		return error;
+
+	error = pci_enable_runtime_wake(pci_dev, false);
+
+	if (error)
+		return error;
+
+	return 0;
+}

drivers/pci/pci-driver.c static int pci_pm_restore(struct device *dev) · line 1000

Quoted 10 lines
drivers/pci/pci-driver.c +10 -0
+
+static void pci_pm_runtime_idle(struct device *dev)
+{
+	struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm && pm->runtime_idle)
+		pm->runtime_idle(dev);
+
+	pm_schedule_suspend(dev, 0);
+}

That has already been discussed.

Quoted 65 lines
drivers/pci/pci-driver.c +12 -0
+
+#else /* !CONFIG_PM_RUNTIME */
+
+#define pci_pm_runtime_suspend	NULL
+#define pci_pm_runtime_resume	NULL
+#define pci_pm_runtime_idle	NULL
+
+#endif
+
 struct dev_pm_ops pci_dev_pm_ops = {
 	.prepare = pci_pm_prepare,
 	.complete = pci_pm_complete,
@@ -925,6 +1022,9 @@ struct dev_pm_ops pci_dev_pm_ops = {
 	.thaw_noirq = pci_pm_thaw_noirq,
 	.poweroff_noirq = pci_pm_poweroff_noirq,
 	.restore_noirq = pci_pm_restore_noirq,
+	.runtime_suspend = pci_pm_runtime_suspend,
+	.runtime_resume = pci_pm_runtime_resume,
+	.runtime_idle = pci_pm_runtime_idle,
 };
 
 #define PCI_PM_OPS_PTR	(&pci_dev_pm_ops)
drivers/pci/pci.c +21 -0
index dbd0f94..ab3a116 100644
@@ -18,6 +18,7 @@
 #include <linux/log2.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
+#include <linux/pm_runtime.h>
 #include <linux/interrupt.h>
 #include <asm/dma.h>	/* isa_dma_bridge_buggy */
 #include <linux/device.h>
@@ -428,6 +429,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable)
 			pci_platform_pm->sleep_wake(dev, enable) : -ENODEV;
 }
 
+static inline int platform_pci_runtime_wake(struct pci_dev *dev, bool enable)
+{
+	return pci_platform_pm ?
+			pci_platform_pm->runtime_wake(dev, enable) : -ENODEV;
+}
+
 /**
  * pci_raw_set_power_state - Use PCI PM registers to set the power state of
  *                           given PCI device
@@ -1239,6 +1246,38 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
 }
 
 /**
+ * pci_enable_runtime_wake - enable PCI device as runtime wakeup event source
+ * @dev: PCI device affected
+ * @enable: True to enable event generation; false to disable
+ *
+ * This enables the device as a runtime wakeup event source, or disables it.
+ * This typically requires platform support.
+ *
+ * RETURN VALUE:
+ * 0 is returned on success
+ * -EINVAL is returned if device is not supposed to wake up the system
+ * -ENODEV is returned if platform cannot support runtime PM on the device
+ */
+int pci_enable_runtime_wake(struct pci_dev *dev, bool enable)
+{

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1272

Quoted 10 lines
drivers/pci/pci.c +10 -0
+	int error = 0;
+	bool pme_done = false;
+
+	if (!enable && platform_pci_can_wakeup(dev))
+		error = platform_pci_runtime_wake(dev, false);
+
+	if (!enable || pci_pme_capable(dev, PCI_D3hot)) {
+		pci_pme_active(dev, enable);
+		pme_done = true;
+	}

I don't really follow your intention here. The condition means that PME is going to be enabled unless 'enable' is set and the device is not capable of generating PMEs. However, if 'enable' is unset, we're still going to try to enable the PME, even if the device can't generate it. Shouldn't that be

if (enable && pci_pme_capable(dev, PCI_D3hot)) ?

Also, that assumes the device is going to be put into D3_hot, but do we know that for sure?

drivers/pci/pci.c int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) · line 1278

Quoted 6 lines
drivers/pci/pci.c +6 -0
+
+	if (enable && platform_pci_can_wakeup(dev))
+		error = platform_pci_runtime_wake(dev, true);
+
+	return pme_done ? 0 : error;
+}

I have no comments to the part below.

Quoted 88 lines
drivers/pci/pci.c +49 -0
+/**
  * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
  * @dev: PCI device to prepare
  * @enable: True to enable wake-up event generation; false to disable
@@ -1346,6 +1385,54 @@ int pci_back_from_sleep(struct pci_dev *dev)
 }
 
 /**
+ * pci_dev_pme_event - check if a device has a pending pme
+ *
+ * @dev: Device to handle.
+ */
+
+int pci_dev_pme_event(struct pci_dev *dev)
+{
+	u16 pmcsr;
+
+	if (!dev->pm_cap)
+		return -ENODEV;
+
+	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
+
+	if (pmcsr & PCI_PM_CTRL_PME_STATUS) {
+		pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
+		pm_runtime_get(&dev->dev);
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+/**
+ * pci_bus_pme_event - search for subordinate devices with a pending
+ *		   pme and handle them
+ *
+ * @dev: Parent device to handle
+ */
+int pci_bus_pme_event(struct pci_dev *dev)
+{
+	struct pci_bus *bus;
+	struct pci_dev *pdev;
+
+	if (pci_is_root_bus(dev->bus))
+		bus = dev->bus;
+	else if (dev->subordinate)
+		bus = dev->subordinate;
+	else
+		return -ENODEV;
+
+	list_for_each_entry(pdev, &bus->devices, bus_list)
+		pci_dev_pme_event(pdev);
+
+	return 0;
+}
+
+/**
  * pci_pm_init - Initialize PM functions of given PCI device
  * @dev: PCI device to handle.
  */
drivers/pci/pci.h +3 -0
index f73bcbe..a81aff2 100644
@@ -34,6 +34,8 @@ extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
  *
  * @sleep_wake: enables/disables the system wake up capability of given device
  *
+ * @runtime_wake: enables/disables the runtime wakeup capability of given device
+ *
  * If given platform is generally capable of power managing PCI devices, all of
  * these callbacks are mandatory.
  */
@@ -43,6 +45,7 @@ struct pci_platform_pm_ops {
 	pci_power_t (*choose_state)(struct pci_dev *dev);
 	bool (*can_wakeup)(struct pci_dev *dev);
 	int (*sleep_wake)(struct pci_dev *dev, bool enable);
+	int (*runtime_wake)(struct pci_dev *dev, bool enable);
 };
 
 extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
include/linux/pci.h +0 -0
index 115fb7b..8a3fea0 100644
@@ -734,10 +734,13 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
 void pci_pme_active(struct pci_dev *dev, bool enable);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);

include/linux/pci.h pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); · line 746

Quoted 10 lines
include/linux/pci.h +3 -0
+int pci_enable_runtime_wake(struct pci_dev *dev, bool enable);
 int pci_wake_from_d3(struct pci_dev *dev, bool enable);
 pci_power_t pci_target_state(struct pci_dev *dev);
 int pci_prepare_to_sleep(struct pci_dev *dev);
 int pci_back_from_sleep(struct pci_dev *dev);
+int pci_dev_pme_event(struct pci_dev *dev);
+int pci_bus_pme_event(struct pci_dev *dev);
 
 /* Functions for PCI Hotplug drivers to use */
 int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);

Thanks,
Rafael

#75 lore raw

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

In reply to Rafael J. Wysocki

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

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 |  378 +++++++++++++
 drivers/base/dd.c                  |   11 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   22 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       | 1011 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  114 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1689 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 +1011 -0

1,011 lines not on this page · raw message

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

114 lines not on this page · raw message

drivers/base/power/main.c +21 -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_put_noidle(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		pm_runtime_get_noresume(dev);
+		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
+			/* Wake-up requested during system sleep transition. */
+			pm_runtime_put_noidle(dev);
+			error = -EBUSY;
+		} else {
+			error = device_prepare(dev, state);
+		}
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
drivers/base/dd.c +11 -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,10 @@ 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);
+	pm_runtime_barrier(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_sync(dev);
 
 	return ret;
 }
@@ -245,7 +249,9 @@ int device_attach(struct device *dev)
 			ret = 0;
 		}
 	} else {
+		pm_runtime_get_noresume(dev);
 		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+		pm_runtime_put_sync(dev);
 	}
 	up(&dev->sem);
 	return ret;
@@ -306,6 +312,9 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_get_noresume(dev);
+		pm_runtime_barrier(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_sync(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 +378 -0

378 lines not on this page · raw message

#76 lore raw

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

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. 16)

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.

If there are no more outstanding issues with this, any chance of having this rolled in to a topic branch along with Magnus's platform bus support? It would be nice if these could spend some time in -next, especially as it seems there are no longer any glaring issues with the patch series.

#77 lore raw

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

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. 16)

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.

If there are no more outstanding issues with this, any chance of having this rolled in to a topic branch along with Magnus's platform bus support? It would be nice if these could spend some time in -next, especially as it seems there are no longer any glaring issues with the patch series.

#78 lore raw

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

Quoted 14 lines · Paul Mundt
Quoted 13 lines · Rafael J. Wysocki

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

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.

Quoted 7 lines
Quoted 1 line

If there are no more outstanding issues with this, any chance of having this rolled in to a topic branch along with Magnus's platform bus support? It would be nice if these could spend some time in -next, especially as it seems there are no longer any glaring issues with the patch series.

I completely agree and I hope there are no major issues with this patch any more.

If there are no objections, I'm going to put the patch into my linux-next branch shortly, from where it will go to the for-linus branch. I'm going to do that with the Magnus' patch too.

Thanks,
Rafael

#79 lore raw

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

In reply to Paul Mundt
Quoted 14 lines · Paul Mundt
Quoted 13 lines · Rafael J. Wysocki

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

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.

Quoted 7 lines
Quoted 1 line

If there are no more outstanding issues with this, any chance of having this rolled in to a topic branch along with Magnus's platform bus support? It would be nice if these could spend some time in -next, especially as it seems there are no longer any glaring issues with the patch series.

I completely agree and I hope there are no major issues with this patch any more.

If there are no objections, I'm going to put the patch into my linux-next branch shortly, from where it will go to the for-linus branch. I'm going to do that with the Magnus' patch too.

Thanks,
Rafael

Acked-by on v1 1/1
#80 lore raw

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

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. 16)

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 now tested v16 with my SuperH Mobile platform device Runtime PM implementation. All seems fine and my patches for patches for drivers such as i2c master, fbdev, v4l2 and uio behave as expected.

The patch "PM: Runtime PM v15 - Platform Device Bus Support" works well with v16 and does not need any update.

Cheers,

/ magnus

Acked-by on v1 1/1
#81 lore raw

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

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. 16)

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 now tested v16 with my SuperH Mobile platform device Runtime PM implementation. All seems fine and my patches for patches for drivers such as i2c master, fbdev, v4l2 and uio behave as expected.

The patch "PM: Runtime PM v15 - Platform Device Bus Support" works well with v16 and does not need any update.

Cheers,

/ magnus

#82 lore raw

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

Quoted 16 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. 16)

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.

Quoted 8 lines

I've now tested v16 with my SuperH Mobile platform device Runtime PM implementation. All seems fine and my patches for patches for drivers such as i2c master, fbdev, v4l2 and uio behave as expected.

The patch "PM: Runtime PM v15 - Platform Device Bus Support" works well with v16 and does not need any update.

Thanks for the ACK, but I have one more update to the patch. Namely, I've decided to change the return value of the ->runtime_idle() callback to int as a result of the recent discussion on the PCI runtime PM patch from Matthew.

I'll post the updated patch in a little while.

Thanks,
Rafael

#83 lore raw

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

In reply to Magnus Damm
Quoted 16 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. 16)

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.

Quoted 8 lines

I've now tested v16 with my SuperH Mobile platform device Runtime PM implementation. All seems fine and my patches for patches for drivers such as i2c master, fbdev, v4l2 and uio behave as expected.

The patch "PM: Runtime PM v15 - Platform Device Bus Support" works well with v16 and does not need any update.

Thanks for the ACK, but I have one more update to the patch. Namely, I've decided to change the return value of the ->runtime_idle() callback to int as a result of the recent discussion on the PCI runtime PM patch from Matthew.

I'll post the updated patch in a little while.

Thanks,
Rafael

#84 lore raw

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

In reply to Rafael J. Wysocki

Hi,

The only difference between this one and rev. 16 is that now the runtime_idle() callback is defined to return int, as suggested by Matthew in the "PCI: Runtime power management" thread.

Thanks,
Rafael

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

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 |  378 +++++++++++++
 drivers/base/dd.c                  |   11 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   22 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       | 1011 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  114 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1689 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 +1011 -0

1,011 lines not on this page · raw message

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

114 lines not on this page · raw message

drivers/base/power/main.c +21 -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_put_noidle(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		pm_runtime_get_noresume(dev);
+		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
+			/* Wake-up requested during system sleep transition. */
+			pm_runtime_put_noidle(dev);
+			error = -EBUSY;
+		} else {
+			error = device_prepare(dev, state);
+		}
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
drivers/base/dd.c +11 -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,10 @@ 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);
+	pm_runtime_barrier(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_sync(dev);
 
 	return ret;
 }
@@ -245,7 +249,9 @@ int device_attach(struct device *dev)
 			ret = 0;
 		}
 	} else {
+		pm_runtime_get_noresume(dev);
 		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+		pm_runtime_put_sync(dev);
 	}
 	up(&dev->sem);
 	return ret;
@@ -306,6 +312,9 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_get_noresume(dev);
+		pm_runtime_barrier(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_sync(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 +378 -0

378 lines not on this page · raw message

#85 lore raw

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

In reply to Rafael J. Wysocki

Hi,

The only difference between this one and rev. 16 is that now the runtime_idle() callback is defined to return int, as suggested by Matthew in the "PCI: Runtime power management" thread.

Thanks,
Rafael

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

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 |  378 +++++++++++++
 drivers/base/dd.c                  |   11 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   22 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       | 1011 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  114 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1689 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 +1011 -0

1,011 lines not on this page · raw message

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

114 lines not on this page · raw message

drivers/base/power/main.c +21 -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_put_noidle(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		pm_runtime_get_noresume(dev);
+		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
+			/* Wake-up requested during system sleep transition. */
+			pm_runtime_put_noidle(dev);
+			error = -EBUSY;
+		} else {
+			error = device_prepare(dev, state);
+		}
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
drivers/base/dd.c +11 -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,10 @@ 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);
+	pm_runtime_barrier(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_sync(dev);
 
 	return ret;
 }
@@ -245,7 +249,9 @@ int device_attach(struct device *dev)
 			ret = 0;
 		}
 	} else {
+		pm_runtime_get_noresume(dev);
 		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+		pm_runtime_put_sync(dev);
 	}
 	up(&dev->sem);
 	return ret;
@@ -306,6 +312,9 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_get_noresume(dev);
+		pm_runtime_barrier(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_sync(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 +378 -0

378 lines not on this page · raw message

#86 lore raw

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

In reply to Rafael J. Wysocki

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

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 |  378 +++++++++++++
 drivers/base/dd.c                  |   11 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   22 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       | 1011 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  114 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1689 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 +1011 -0

1,011 lines not on this page · raw message

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

114 lines not on this page · raw message

drivers/base/power/main.c +21 -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_put_noidle(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		pm_runtime_get_noresume(dev);
+		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
+			/* Wake-up requested during system sleep transition. */
+			pm_runtime_put_noidle(dev);
+			error = -EBUSY;
+		} else {
+			error = device_prepare(dev, state);
+		}
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
drivers/base/dd.c +11 -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,10 @@ 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);
+	pm_runtime_barrier(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_sync(dev);
 
 	return ret;
 }
@@ -245,7 +249,9 @@ int device_attach(struct device *dev)
 			ret = 0;
 		}
 	} else {
+		pm_runtime_get_noresume(dev);
 		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
+		pm_runtime_put_sync(dev);
 	}
 	up(&dev->sem);
 	return ret;
@@ -306,6 +312,9 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_get_noresume(dev);
+		pm_runtime_barrier(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_sync(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 +378 -0

378 lines not on this page · raw message

#87 lore raw

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

In reply to Rafael J. Wysocki

Hi,

One more update. This one should address your comments from this thread
http://lkml.org/lkml/2009/8/8/113

Thanks,
Rafael

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

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 |  386 ++++++++++++++
 drivers/base/dd.c                  |   11 
 drivers/base/power/Makefile        |    1 
 drivers/base/power/main.c          |   22 
 drivers/base/power/power.h         |   31 -
 drivers/base/power/runtime.c       | 1011 +++++++++++++++++++++++++++++++++++++
 include/linux/pm.h                 |  101 +++
 include/linux/pm_runtime.h         |  114 ++++
 kernel/power/Kconfig               |   14 
 kernel/power/main.c                |   17 
 10 files changed, 1697 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 +1011 -0

1,011 lines not on this page · raw message

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

114 lines not on this page · raw message

drivers/base/power/main.c +21 -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_put_noidle(dev);
 
 			mutex_lock(&dpm_list_mtx);
 		}
@@ -757,7 +770,14 @@ static int dpm_prepare(pm_message_t stat
 		dev->power.status = DPM_PREPARING;
 		mutex_unlock(&dpm_list_mtx);
 
-		error = device_prepare(dev, state);
+		pm_runtime_get_noresume(dev);
+		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
+			/* Wake-up requested during system sleep transition. */
+			pm_runtime_put_noidle(dev);
+			error = -EBUSY;
+		} else {
+			error = device_prepare(dev, state);
+		}
 
 		mutex_lock(&dpm_list_mtx);
 		if (error) {
drivers/base/dd.c +11 -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,12 @@ 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);
+	pm_runtime_barrier(dev);
 	ret = really_probe(dev, drv);
+	pm_runtime_put_noidle(dev);
+	if (!ret)
+		pm_runtime_idle(dev);
 
 	return ret;
 }
@@ -306,6 +312,9 @@ static void __device_release_driver(stru
 
 	drv = dev->driver;
 	if (drv) {
+		pm_runtime_get_noresume(dev);
+		pm_runtime_barrier(dev);
+
 		driver_sysfs_remove(dev);
 
 		if (dev->bus)
@@ -324,6 +333,8 @@ static void __device_release_driver(stru
 			blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
 						     BUS_NOTIFY_UNBOUND_DRIVER,
 						     dev);
+
+		pm_runtime_put_noidle(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 +386 -0

386 lines not on this page · raw message