PM: Introduce core framework for run-time PM of I/O devices (rev. 11)
Subsystems (4)
PATCH RESEND 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 11)
Hi,
OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 11)
Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.
Special thanks to Alan Stern for his help with the design and to Magnus Damm for testing feedback.
--- Documentation/power/runtime_pm.txt | 340 +++++++++++++ drivers/base/dd.c | 7 drivers/base/power/Makefile | 1 drivers/base/power/main.c | 20 drivers/base/power/power.h | 11 drivers/base/power/runtime.c | 946 +++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 102 +++ include/linux/pm_runtime.h | 111 ++++ kernel/power/Kconfig | 14 kernel/power/main.c | 17 10 files changed, 1558 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
@@ -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
@@ -11,6 +11,7 @@#include <linux/kobject.h> #include <linux/string.h> #include <linux/resume-trace.h> +#include <linux/workqueue.h> #include "power.h"
@@ -217,8 +218,24 @@ static struct attribute_group attr_group .attrs = g, }; +#ifdef CONFIG_PM_RUNTIME +struct workqueue_struct *pm_wq; + +static int __init pm_start_workqueue (void ) +{ + pm_wq =create_freezeable_workqueue ("pm" ); + +return pm_wq ?0 : -ENOMEM ; +} +#else +static inline int pm_start_workqueue (void ) {return 0 ; } +#endif +static int __init pm_init (void ) { +int error =pm_start_workqueue (); +if (error) +return error; power_kobj =kobject_create_and_add ("power" ,NULL );if (!power_kobj)return -ENOMEM ;
include/linux/pm.h +99 -3
131 lines not on this page · raw message
drivers/base/power/Makefile +1 -0
@@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +946 -0
946 lines not on this page · raw message
include/linux/pm_runtime.h new file +111 -0
111 lines not on this page · raw message
drivers/base/power/main.c +19 -1
@@ -21,6 +21,7 @@#include <linux/kallsyms.h> #include <linux/mutex.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/resume-trace.h> #include <linux/rwsem.h> #include <linux/interrupt.h>
@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** +* device_pm_init - Initialize the PM-related part of a device object +* @dev: Device object to initialize. +*/ +void device_pm_init (struct device *dev) +{ + dev->power.status =DPM_ON ; +pm_runtime_init (dev); +} + +/** * device_pm_lock - lock the list of active devices used by the PM core */ void device_pm_lock (void )
@@ -105,6 +116,8 @@ void device_pm_remove(struct device *devmutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx); + +pm_runtime_remove (dev); }/**
@@ -512,6 +525,7 @@ static void dpm_complete(pm_message_t stmutex_unlock (&dpm_list_mtx);device_complete (dev, state); +pm_runtime_enable (dev);mutex_lock (&dpm_list_mtx); }
@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ; +else + error =device_prepare (dev, state);mutex_lock (&dpm_list_mtx);if (error) { dev->power.status =DPM_ON ; +pm_runtime_enable (dev);if (error == -EAGAIN ) {put_device (dev); error =0 ;
drivers/base/dd.c +7 -0
@@ -23,6 +23,7 @@#include <linux/kthread.h> #include <linux/wait.h> #include <linux/async.h> +#include <linux/pm_runtime.h> #include "base.h" #include "power/power.h"
@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);return ret; }
@@ -306,6 +309,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
@@ -324,6 +329,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
drivers/base/power/power.h +4 -7
@@ -1,8 +1,3 @@ -static inline void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -} -#ifdef CONFIG_PM_SLEEP /*
@@ -16,14 +11,16 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } +extern void device_pm_init (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);extern void device_pm_move_after (struct device *,struct device *);extern void device_pm_move_last (struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ +static inline void device_pm_init (struct device *dev) {}static inline void device_pm_add (struct device *dev) {}static inline void device_pm_remove (struct device *dev) {} static inline void device_pm_move_before(struct device *deva,
@@ -32,7 +29,7 @@ static inline void device_pm_move_after( struct device *devb) {}static inline void device_pm_move_last (struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM
Documentation/power/runtime_pm.txt new file +340 -0
340 lines not on this page · raw message
Quoted 6 lines · Rafael J. Wysocki
Hi,
OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.
I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...
One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.
pm_runtime_disable() gets used for several different purposes. For the usage in pm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?
In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.
drivers/base/power/runtime.c line 39
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static void pm_runtime_cancel_pending (struct device *dev) +{ +pm_runtime_deactivate_timer (dev); +/* +* If there's a request pending, make sure its work function will return +* without doing anything. +*/ +if (dev->power.request_pending) + dev->power.request =RPM_REQ_NONE ;
No need for the "if"; you can always do the assignment.
drivers/base/power/runtime.c line 53
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_runtime_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ;
Instead of assigning to retval, you could simply return these values.
drivers/base/power/runtime.c line 61
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+else if (dev->power.idle_notification) + retval = -EINPROGRESS ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires >0 + || dev->power.runtime_status ==RPM_SUSPENDED + || dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EAGAIN ;
Do we also want to rule out RPM_RESUMING? That is,
|| dev->power.runtime_status != RPM_ACTIVE
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);
Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails. The same thing can be done in other places.
Quoted 3 lines
+ *
__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called viapm_wq.
Fix spelling of "function". Likewise in __pm_runtime_resume.
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) +{
...
drivers/base/power/runtime.c line 218
Quoted 7 lines
drivers/base/power/runtime.c new file +7
-0+spin_unlock_irq (&dev->power.lock); + +if (parent && !parent->power.ignore_children) +pm_request_idle (parent); + +if (notify) +pm_runtime_idle (dev);
Move this up before the spin_unlock_irq and call __pm_runtime_idle instead. The same sort of thing can be done in __pm_runtime_resume.
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_request_suspend (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) +return -EINVAL ;
drivers/base/power/runtime.c line 480
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+ +if (dev->power.runtime_status ==RPM_SUSPENDED ) + retval =1 ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 ) + retval = -EAGAIN ; +else if (dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EINPROGRESS ; +else if (!pm_children_suspended (dev)) + retval = -EBUSY ;
Insert:
if (retval) return retval;
Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)
drivers/base/power/runtime.c line 493
Quoted 12 lines
drivers/base/power/runtime.c new file +12
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* +* Pending resume requests take precedence over us, but we can +* overtake any other pending request. +*/ +if (dev->power.request ==RPM_REQ_RESUME ) + retval = -EAGAIN ; +else if (dev->power.request !=RPM_REQ_SUSPEND ) + dev->power.request = retval ? +RPM_REQ_NONE :RPM_REQ_SUSPEND ;
Now there's no need to check retval.
drivers/base/power/runtime.c line 496
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ +if (dev->power.request ==RPM_REQ_SUSPEND ) +return 0 ;
Just simply:
return retval;
Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.
drivers/base/power/runtime.c line 65
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+ } + +if (retval) +return retval;
Now this isn't needed. Similar code rearrangements can be made in __pm_request_resume.
Quoted 1 line
+int
__pm_request_resume(struct device *dev)
Should be static.
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;
This should go inside the spinlocked area.
Quoted 34 lines
drivers/base/power/runtime.c new file +34
-0+spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out; + +if (dev->power.runtime_status == status) + goto out_clear; + +if (status ==RPM_SUSPENDED ) { +/* It always is possible to set the status to 'suspended'. */ +if (parent) +atomic_add_unless (&parent->power.child_count,-1 ,0 ); + dev->power.runtime_status = status; + goto out_clear; + } + +if (parent) { +spin_lock_irq (&parent->power.lock); + +/* +* It is invalid to put an active child under a parent that is +* not active, has run-time PM enabled and the +* 'power.ignore_children' flag unset. +*/ +if (!parent->power.disable_depth + && !parent->power.ignore_children + && parent->power.runtime_status !=RPM_ACTIVE ) { + error = -EBUSY ; + }else { +if (dev->power.runtime_status ==RPM_SUSPENDED ) +atomic_inc (&parent->power.child_count); + dev->power.runtime_status = status; + } +
drivers/base/power/runtime.c line 755
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+spin_unlock_irq (&parent->power.lock); + +if (error) + goto out; + }else { + dev->power.runtime_status = status; + } + + out_clear: + dev->power.runtime_failure = false;
Move all those assignments to dev->power.runtime_status down here.
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{
...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;
I don't see why this is needed.
drivers/base/power/runtime.c line 842
Quoted 13 lines
drivers/base/power/runtime.c new file +13
-0+ +pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { + dev->power.request =RPM_REQ_NONE ; + +spin_unlock_irq (&dev->power.lock); + +cancel_work_sync (&dev->power.work); + +spin_lock_irq (&dev->power.lock); + + dev->power.request_pending = false;
Remove excessive whitespace.
Quoted 33 lines
drivers/base/power/runtime.c new file +33
-0+ } + +if (dev->power.runtime_status ==RPM_SUSPENDING + || dev->power.runtime_status ==RPM_RESUMING ) { +DEFINE_WAIT (wait); + +/* Suspend or wake-up in progress. */ +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (dev->power.runtime_status !=RPM_SUSPENDING + && dev->power.runtime_status !=RPM_RESUMING ) +break ; + +spin_unlock_irq (&dev->power.lock); + +schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + } + +if (dev->power.runtime_failure) + goto out; + +if (dev->power.idle_notification) { +DEFINE_WAIT (wait); + +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (!dev->power.idle_notification)
drivers/base/power/runtime.c line 885
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+break ; + +spin_unlock_irq (&dev->power.lock); + +schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + }
This wait loop should be merged with the previous loop.
drivers/base/power/runtime.c line 909
Quoted 12 lines
drivers/base/power/runtime.c new file +12
-0+void pm_runtime_init (struct device *dev) +{ +spin_lock_init (&dev->power.lock); + + dev->power.runtime_status =RPM_SUSPENDED ; + dev->power.idle_notification = false; + + dev->power.disable_depth =1 ; +atomic_set (&dev->power.usage_count,0 ); + + dev->power.runtime_failure = false; + dev->power.last_error =0 ;
You don't have to set values to 0; they are initialized by kzalloc.
drivers/base/power/runtime.c line 922
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ dev->power.suspend_timer.expires = jiffies; + dev->power.suspend_timer.data = (unsigned long )dev; + dev->power.suspend_timer.function = pm_suspend_timer_fn;
Use setup_timer() instead of assigning these fields directly.
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); +
drivers/base/power/runtime.c line 944
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/* +* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);
Shouldn't these last two lines be part of __pm_runtime_set_status()?
include/linux/pm_runtime.h line 20
Quoted 5 lines
include/linux/pm_runtime.h new file +2
-0+extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev);
I don't like seeing these two functions included in the public header file. It's enough to put them in drivers/base/power/power.h.
Quoted 20 lines
drivers/base/power/main.c +10
-0@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** +* device_pm_init - Initialize the PM-related part of a device object +* @dev: Device object to initialize. +*/ +void device_pm_init (struct device *dev) +{ + dev->power.status =DPM_ON ; +pm_runtime_init (dev); +} + +/** * device_pm_lock - lock the list of active devices used by the PM core */ void device_pm_lock (void )
drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121
Quoted 6 lines
drivers/base/power/main.c +2
-0@@ -105,6 +116,8 @@ void device_pm_remove(struct device *devmutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx); + +pm_runtime_remove (dev); }
Calling pm_runtime_init() from device_pm_init() and pm_runtime_remove() from device_pm_remove() isn't good. If CONFIG_PM_SLEEP isn't enabled then the calls won't be compiled, even if CONFIG_PM_RUNTIME is set.
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;
What's the reason for the -EBUSY error?
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 9 lines
drivers/base/dd.c +2
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);
This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend. Instead we should do something like this:
/* Wait for runtime PM calls to finish and prevent new calls * until the probe is done. */ pm_runtime_disable(dev); pm_runtime_get_noresume(dev); pm_runtime_enable(dev): ret = really_probe(dev, drv); pm_runtime_put_sync(dev);
Documentation/power/runtime_pm.txt
Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.
What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
Documentation/power/runtime_pm.txt line 148
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+Additionally, the helper functions provided by the PM core obey the following +rules: + + * If ->runtime_suspend() is about to be executed or the execution of it is + scheduled or there's a pending request to execute it, ->runtime_idle() will + not be executed for the same device.
Shouldn't we allow runtime_idle when a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.
Documentation/power/runtime_pm.txt line 266
Quoted 9 lines
+4. Run-time PM Device Helper Functions
Documentation/power/runtime_pm.txt new file +7
-0+ +The following run-time PM helper functions are defined in +drivers/base/power/runtime.c and include/linux/pm_runtime.h: + int pm_schedule_suspend(struct device *dev, unsigned int delay); + - schedule the execution of ->runtime_suspend() for the device's bus type + in future, where 'delay' is the time to wait before queuing up a suspend + work item in pm_wq, in miliseconds (if 'delay' is zero, the work item is
Fix spelling of "milli". Explain that the new delay will override the old one if a suspend was already scheduled and not yet expired.
Documentation/power/runtime_pm.txt line 285
Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2
-0+ int pm_runtime_get_sync(struct device *dev); + - increment the device's usage counter, run pm_rutime_resume(dev) and return
Fix spelling of "runtime". Same under pm_runtime_put_sync.
Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Hi,
OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.
I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...
One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.
pm_runtime_disable() gets used for several different purposes. For the usage in pm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?
In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.
drivers/base/power/runtime.c line 39
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static void pm_runtime_cancel_pending (struct device *dev) +{ +pm_runtime_deactivate_timer (dev); +/* +* If there's a request pending, make sure its work function will return +* without doing anything. +*/ +if (dev->power.request_pending) + dev->power.request =RPM_REQ_NONE ;
No need for the "if"; you can always do the assignment.
drivers/base/power/runtime.c line 53
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_runtime_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ;
Instead of assigning to retval, you could simply return these values.
drivers/base/power/runtime.c line 61
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+else if (dev->power.idle_notification) + retval = -EINPROGRESS ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires >0 + || dev->power.runtime_status ==RPM_SUSPENDED + || dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EAGAIN ;
Do we also want to rule out RPM_RESUMING? That is,
|| dev->power.runtime_status != RPM_ACTIVE
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);
Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails. The same thing can be done in other places.
Quoted 3 lines
+ *
__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called viapm_wq.
Fix spelling of "function". Likewise in __pm_runtime_resume.
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) +{
...
drivers/base/power/runtime.c line 218
Quoted 7 lines
drivers/base/power/runtime.c new file +7
-0+spin_unlock_irq (&dev->power.lock); + +if (parent && !parent->power.ignore_children) +pm_request_idle (parent); + +if (notify) +pm_runtime_idle (dev);
Move this up before the spin_unlock_irq and call __pm_runtime_idle instead. The same sort of thing can be done in __pm_runtime_resume.
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_request_suspend (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) +return -EINVAL ;
drivers/base/power/runtime.c line 480
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+ +if (dev->power.runtime_status ==RPM_SUSPENDED ) + retval =1 ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 ) + retval = -EAGAIN ; +else if (dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EINPROGRESS ; +else if (!pm_children_suspended (dev)) + retval = -EBUSY ;
Insert:
if (retval) return retval;
Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)
drivers/base/power/runtime.c line 493
Quoted 12 lines
drivers/base/power/runtime.c new file +12
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* +* Pending resume requests take precedence over us, but we can +* overtake any other pending request. +*/ +if (dev->power.request ==RPM_REQ_RESUME ) + retval = -EAGAIN ; +else if (dev->power.request !=RPM_REQ_SUSPEND ) + dev->power.request = retval ? +RPM_REQ_NONE :RPM_REQ_SUSPEND ;
Now there's no need to check retval.
drivers/base/power/runtime.c line 496
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ +if (dev->power.request ==RPM_REQ_SUSPEND ) +return 0 ;
Just simply:
return retval;
Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.
drivers/base/power/runtime.c line 65
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+ } + +if (retval) +return retval;
Now this isn't needed. Similar code rearrangements can be made in __pm_request_resume.
Quoted 1 line
+int
__pm_request_resume(struct device *dev)
Should be static.
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;
This should go inside the spinlocked area.
Quoted 34 lines
drivers/base/power/runtime.c new file +34
-0+spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out; + +if (dev->power.runtime_status == status) + goto out_clear; + +if (status ==RPM_SUSPENDED ) { +/* It always is possible to set the status to 'suspended'. */ +if (parent) +atomic_add_unless (&parent->power.child_count,-1 ,0 ); + dev->power.runtime_status = status; + goto out_clear; + } + +if (parent) { +spin_lock_irq (&parent->power.lock); + +/* +* It is invalid to put an active child under a parent that is +* not active, has run-time PM enabled and the +* 'power.ignore_children' flag unset. +*/ +if (!parent->power.disable_depth + && !parent->power.ignore_children + && parent->power.runtime_status !=RPM_ACTIVE ) { + error = -EBUSY ; + }else { +if (dev->power.runtime_status ==RPM_SUSPENDED ) +atomic_inc (&parent->power.child_count); + dev->power.runtime_status = status; + } +
drivers/base/power/runtime.c line 755
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+spin_unlock_irq (&parent->power.lock); + +if (error) + goto out; + }else { + dev->power.runtime_status = status; + } + + out_clear: + dev->power.runtime_failure = false;
Move all those assignments to dev->power.runtime_status down here.
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{
...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;
I don't see why this is needed.
drivers/base/power/runtime.c line 842
Quoted 13 lines
drivers/base/power/runtime.c new file +13
-0+ +pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { + dev->power.request =RPM_REQ_NONE ; + +spin_unlock_irq (&dev->power.lock); + +cancel_work_sync (&dev->power.work); + +spin_lock_irq (&dev->power.lock); + + dev->power.request_pending = false;
Remove excessive whitespace.
Quoted 33 lines
drivers/base/power/runtime.c new file +33
-0+ } + +if (dev->power.runtime_status ==RPM_SUSPENDING + || dev->power.runtime_status ==RPM_RESUMING ) { +DEFINE_WAIT (wait); + +/* Suspend or wake-up in progress. */ +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (dev->power.runtime_status !=RPM_SUSPENDING + && dev->power.runtime_status !=RPM_RESUMING ) +break ; + +spin_unlock_irq (&dev->power.lock); + +schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + } + +if (dev->power.runtime_failure) + goto out; + +if (dev->power.idle_notification) { +DEFINE_WAIT (wait); + +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (!dev->power.idle_notification)
drivers/base/power/runtime.c line 885
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+break ; + +spin_unlock_irq (&dev->power.lock); + +schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + }
This wait loop should be merged with the previous loop.
drivers/base/power/runtime.c line 909
Quoted 12 lines
drivers/base/power/runtime.c new file +12
-0+void pm_runtime_init (struct device *dev) +{ +spin_lock_init (&dev->power.lock); + + dev->power.runtime_status =RPM_SUSPENDED ; + dev->power.idle_notification = false; + + dev->power.disable_depth =1 ; +atomic_set (&dev->power.usage_count,0 ); + + dev->power.runtime_failure = false; + dev->power.last_error =0 ;
You don't have to set values to 0; they are initialized by kzalloc.
drivers/base/power/runtime.c line 922
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ dev->power.suspend_timer.expires = jiffies; + dev->power.suspend_timer.data = (unsigned long )dev; + dev->power.suspend_timer.function = pm_suspend_timer_fn;
Use setup_timer() instead of assigning these fields directly.
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); +
drivers/base/power/runtime.c line 944
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/* +* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);
Shouldn't these last two lines be part of __pm_runtime_set_status()?
include/linux/pm_runtime.h line 20
Quoted 5 lines
include/linux/pm_runtime.h new file +2
-0+extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev);
I don't like seeing these two functions included in the public header file. It's enough to put them in drivers/base/power/power.h.
Quoted 20 lines
drivers/base/power/main.c +10
-0@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** +* device_pm_init - Initialize the PM-related part of a device object +* @dev: Device object to initialize. +*/ +void device_pm_init (struct device *dev) +{ + dev->power.status =DPM_ON ; +pm_runtime_init (dev); +} + +/** * device_pm_lock - lock the list of active devices used by the PM core */ void device_pm_lock (void )
drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121
Quoted 6 lines
drivers/base/power/main.c +2
-0@@ -105,6 +116,8 @@ void device_pm_remove(struct device *devmutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx); + +pm_runtime_remove (dev); }
Calling pm_runtime_init() from device_pm_init() and pm_runtime_remove() from device_pm_remove() isn't good. If CONFIG_PM_SLEEP isn't enabled then the calls won't be compiled, even if CONFIG_PM_RUNTIME is set.
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;
What's the reason for the -EBUSY error?
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 9 lines
drivers/base/dd.c +2
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);
This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use put_noidle because it might subvert the driver's attempt to autosuspend. Instead we should do something like this:
/* Wait for runtime PM calls to finish and prevent new calls * until the probe is done. */ pm_runtime_disable(dev); pm_runtime_get_noresume(dev); pm_runtime_enable(dev): ret = really_probe(dev, drv); pm_runtime_put_sync(dev);
Documentation/power/runtime_pm.txt
Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.
What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
Documentation/power/runtime_pm.txt line 148
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+Additionally, the helper functions provided by the PM core obey the following +rules: + + * If ->runtime_suspend() is about to be executed or the execution of it is + scheduled or there's a pending request to execute it, ->runtime_idle() will + not be executed for the same device.
Shouldn't we allow runtime_idle when a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.
Documentation/power/runtime_pm.txt line 266
Quoted 9 lines
+4. Run-time PM Device Helper Functions
Documentation/power/runtime_pm.txt new file +7
-0+ +The following run-time PM helper functions are defined in +drivers/base/power/runtime.c and include/linux/pm_runtime.h: + int pm_schedule_suspend(struct device *dev, unsigned int delay); + - schedule the execution of ->runtime_suspend() for the device's bus type + in future, where 'delay' is the time to wait before queuing up a suspend + work item in pm_wq, in miliseconds (if 'delay' is zero, the work item is
Fix spelling of "milli". Explain that the new delay will override the old one if a suspend was already scheduled and not yet expired.
Documentation/power/runtime_pm.txt line 285
Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2
-0+ int pm_runtime_get_sync(struct device *dev); + - increment the device's usage counter, run pm_rutime_resume(dev) and return
Fix spelling of "runtime". Same under pm_runtime_put_sync.
Alan Stern
Quoted 11 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Hi,
OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.
I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...
No problem.
Quoted 8 lines
One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.
pm_runtime_disable()gets used for several different purposes. For the usage inpm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?
Yes, we can do that.
Quoted 3 lines
In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.
OK, I'll try to put something like this in there.
Quoted 11 lines
drivers/base/power/runtime.c line 39
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static void pm_runtime_cancel_pending (struct device *dev) +{ +pm_runtime_deactivate_timer (dev); +/* +* If there's a request pending, make sure its work function will return +* without doing anything. +*/ +if (dev->power.request_pending) + dev->power.request =RPM_REQ_NONE ;No need for the "if"; you can always do the assignment.
OK
Quoted 8 lines
drivers/base/power/runtime.c line 53
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_runtime_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ;Instead of assigning to retval, you could simply return these values.
I could, but I chose not to.
Quoted 11 lines
drivers/base/power/runtime.c line 61
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+else if (dev->power.idle_notification) + retval = -EINPROGRESS ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires >0 + || dev->power.runtime_status ==RPM_SUSPENDED + || dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EAGAIN ;Do we also want to rule out
RPM_RESUMING? That is,
Quoted 1 line
|| dev->power.runtime_status != RPM_ACTIVE
Yes, we do, thanks.
Quoted 9 lines
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.
Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.
Quoted 1 line
The same thing can be done in other places.
I'm not really sure it can.
Quoted 5 lines
Quoted 3 lines
+ *
__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called viapm_wq.Fix spelling of "function". Likewise in
__pm_runtime_resume.
Will do, thanks.
Quoted 13 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) +{...
drivers/base/power/runtime.c line 218
Quoted 7 lines
drivers/base/power/runtime.c new file +7
-0+spin_unlock_irq (&dev->power.lock); + +if (parent && !parent->power.ignore_children) +pm_request_idle (parent); + +if (notify) +pm_runtime_idle (dev);Move this up before the
spin_unlock_irqand call__pm_runtime_idleinstead. The same sort of thing can be done in__pm_runtime_resume.
OK
Quoted 18 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_request_suspend (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) +return -EINVAL ;drivers/base/power/runtime.c line 480
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+ +if (dev->power.runtime_status ==RPM_SUSPENDED ) + retval =1 ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 ) + retval = -EAGAIN ; +else if (dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EINPROGRESS ; +else if (!pm_children_suspended (dev)) + retval = -EBUSY ;Insert:
Quoted 8 lines
if (retval) return retval;Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)
However, if it's going to return 1, it should override existing idle-notify and suspend requests. I'll add
if (retval < 0)
return retval;
Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* +* Pending resume requests take precedence over us, but we can +* overtake any other pending request.
Quoted 8 lines
drivers/base/power/runtime.c line 493
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+*/ +if (dev->power.request ==RPM_REQ_RESUME ) + retval = -EAGAIN ; +else if (dev->power.request !=RPM_REQ_SUSPEND ) + dev->power.request = retval ? +RPM_REQ_NONE :RPM_REQ_SUSPEND ;Now there's no need to check retval.
It is. If retval is 1, we cancel pending idle-notify and suspend requests.
Quoted 6 lines
drivers/base/power/runtime.c line 496
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ +if (dev->power.request ==RPM_REQ_SUSPEND ) +return 0 ;Just simply:
return retval;
OK, I'll change that.
Quoted 11 lines
Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.
drivers/base/power/runtime.c line 65
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+ } + +if (retval) +return retval;Now this isn't needed. Similar code rearrangements can be made in
__pm_request_resume.
OK
Quoted 3 lines
Quoted 1 line
+int
__pm_request_resume(struct device *dev)Should be static.
OK
Quoted 10 lines
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;This should go inside the spinlocked area.
Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.
Quoted 38 lines
Quoted 38 lines
drivers/base/power/runtime.c new file +38
-0+spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out; + +if (dev->power.runtime_status == status) + goto out_clear; + +if (status ==RPM_SUSPENDED ) { +/* It always is possible to set the status to 'suspended'. */ +if (parent) +atomic_add_unless (&parent->power.child_count,-1 ,0 ); + dev->power.runtime_status = status; + goto out_clear; + } + +if (parent) { +spin_lock_irq (&parent->power.lock); + +/* +* It is invalid to put an active child under a parent that is +* not active, has run-time PM enabled and the +* 'power.ignore_children' flag unset. +*/ +if (!parent->power.disable_depth + && !parent->power.ignore_children + && parent->power.runtime_status !=RPM_ACTIVE ) { + error = -EBUSY ; + }else { +if (dev->power.runtime_status ==RPM_SUSPENDED ) +atomic_inc (&parent->power.child_count); + dev->power.runtime_status = status; + } + +spin_unlock_irq (&parent->power.lock); + +if (error) + goto out;
Quoted 8 lines
drivers/base/power/runtime.c line 755
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+ }else { + dev->power.runtime_status = status; + } + + out_clear: + dev->power.runtime_failure = false;Move all those assignments to
dev->power.runtime_statusdown here.
OK
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;I don't see why this is needed.
If dev->power.runtime_failure, there's no need to do anything more.
Quoted 7 lines
Quoted 7 lines
drivers/base/power/runtime.c new file +7
-0+ +pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { + dev->power.request =RPM_REQ_NONE ; + +spin_unlock_irq (&dev->power.lock);
Quoted 8 lines
drivers/base/power/runtime.c line 842
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+ +cancel_work_sync (&dev->power.work); + +spin_lock_irq (&dev->power.lock); + + dev->power.request_pending = false;Remove excessive whitespace.
OK
Quoted 37 lines
Quoted 37 lines
drivers/base/power/runtime.c new file +37
-0+ } + +if (dev->power.runtime_status ==RPM_SUSPENDING + || dev->power.runtime_status ==RPM_RESUMING ) { +DEFINE_WAIT (wait); + +/* Suspend or wake-up in progress. */ +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (dev->power.runtime_status !=RPM_SUSPENDING + && dev->power.runtime_status !=RPM_RESUMING ) +break ; + +spin_unlock_irq (&dev->power.lock); + +schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + } + +if (dev->power.runtime_failure) + goto out; + +if (dev->power.idle_notification) { +DEFINE_WAIT (wait); + +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (!dev->power.idle_notification) +break ; + +spin_unlock_irq (&dev->power.lock); +
Quoted 8 lines
drivers/base/power/runtime.c line 174
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + }This wait loop should be merged with the previous loop.
OK
Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+void pm_runtime_init (struct device *dev) +{ +spin_lock_init (&dev->power.lock); + + dev->power.runtime_status =RPM_SUSPENDED ; + dev->power.idle_notification = false;
Quoted 8 lines
drivers/base/power/runtime.c line 909
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+ + dev->power.disable_depth =1 ; +atomic_set (&dev->power.usage_count,0 ); + + dev->power.runtime_failure = false; + dev->power.last_error =0 ;You don't have to set values to 0; they are initialized by kzalloc.
No, I don't, but does it really hurt?
Quoted 5 lines
drivers/base/power/runtime.c line 922
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ dev->power.suspend_timer.expires = jiffies; + dev->power.suspend_timer.data = (unsigned long )dev; + dev->power.suspend_timer.function = pm_suspend_timer_fn;Use
setup_timer()instead of assigning these fields directly.
OK
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); + +if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/*
Quoted 8 lines
drivers/base/power/runtime.c line 944
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);Shouldn't these last two lines be part of
__pm_runtime_set_status()?
No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.
Quoted 8 lines
include/linux/pm_runtime.h line 20
Quoted 5 lines
include/linux/pm_runtime.h new file +2
-0+extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev);I don't like seeing these two functions included in the public header file. It's enough to put them in
drivers/base/power/power.h.
OK
Quoted 23 lines
Quoted 23 lines
drivers/base/power/main.c +10
-0@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** +* device_pm_init - Initialize the PM-related part of a device object +* @dev: Device object to initialize. +*/ +void device_pm_init (struct device *dev) +{ + dev->power.status =DPM_ON ; +pm_runtime_init (dev); +} + +/** * device_pm_lock - lock the list of active devices used by the PM core */ void device_pm_lock (void )@@ -105,6 +116,8 @@ void device_pm_remove(struct device *devmutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx);
Quoted 8 lines
drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121
Quoted 3 lines
drivers/base/power/main.c +2
-0+ +pm_runtime_remove (dev); }Calling
pm_runtime_init()fromdevice_pm_init()andpm_runtime_remove()fromdevice_pm_remove()isn't good. IfCONFIG_PM_SLEEPisn't enabled then the calls won't be compiled, even ifCONFIG_PM_RUNTIMEis set.
Right, I shouldn't have moved device_pm_init() to main.c at all.
Quoted 9 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;What's the reason for the
-EBUSYerror?
If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).
Quoted 6 lines
Quoted 6 lines
drivers/base/dd.c +0
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name);
Quoted 7 lines
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 3 lines
drivers/base/dd.c +2
-0+pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use
put_noidlebecause it might subvert the driver's attempt to autosuspend.
I'm not sure how that's possible, but whatever.
Quoted 10 lines
Instead we should do something like this:
/* Wait for runtime PM calls to finish and prevent new calls * until the probe is done. */ pm_runtime_disable(dev); pm_runtime_get_noresume(dev); pm_runtime_enable(dev): ret = really_probe(dev, drv); pm_runtime_put_sync(dev);
Fine by me.
Quoted 10 lines
Documentation/power/runtime_pm.txt
Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.
Quoted 10 lines
Documentation/power/runtime_pm.txt line 148
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+Additionally, the helper functions provided by the PM core obey the following +rules: + + * If ->runtime_suspend() is about to be executed or the execution of it is + scheduled or there's a pending request to execute it, ->runtime_idle() will + not be executed for the same device.Shouldn't we allow
runtime_idlewhen a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.
Hmm. We can.
Quoted 11 lines
Documentation/power/runtime_pm.txt line 266
Quoted 9 lines
+4. Run-time PM Device Helper Functions
Documentation/power/runtime_pm.txt new file +7
-0+ +The following run-time PM helper functions are defined in +drivers/base/power/runtime.c and include/linux/pm_runtime.h: + int pm_schedule_suspend(struct device *dev, unsigned int delay); + - schedule the execution of ->runtime_suspend() for the device's bus type + in future, where 'delay' is the time to wait before queuing up a suspend + work item in pm_wq, in miliseconds (if 'delay' is zero, the work item isFix spelling of "milli".
OK
Quoted 2 lines
Explain that the new delay will override the
old one if a suspend was already scheduled and not yet expired.
OK
Quoted 4 lines
Documentation/power/runtime_pm.txt line 285
Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2
-0+ int pm_runtime_get_sync(struct device *dev); + - increment the device's usage counter, run pm_rutime_resume(dev) and returnFix spelling of "runtime". Same under
pm_runtime_put_sync.
OK
Thanks a lot for the comments, I'll post an updated patch addressing them in the next few days.
Best,
Rafael
Quoted 11 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Hi,
OK, if this is to go into 2.6.32, the last moment for putting it into linux-next is now. If you have any objections, remarks, etc. please let me know or I'm going to put this one into the linux-next branch of the suspend-2.6 tree in the next couple of days.
I'm sorry I haven't been keeping on top of all your work on this. Lots of other stuff has been going on in the meantime...
No problem.
Quoted 8 lines
One the whole this all looks very good. It's basically ready to be merged. There are a couple of minor issues remaining plus a bunch of unimportant implementation details.
pm_runtime_disable()gets used for several different purposes. For the usage inpm_runtime_remove(), it's silly to carry out a pending resume request. Should we add an argument saying whether or not to do so?
Yes, we can do that.
Quoted 3 lines
In the documentation, it would be nice to have a section listing the default runtime PM settings and explaining what a driver should do to activate runtime PM on a newly-registered device.
OK, I'll try to put something like this in there.
Quoted 11 lines
drivers/base/power/runtime.c line 39
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static void pm_runtime_cancel_pending (struct device *dev) +{ +pm_runtime_deactivate_timer (dev); +/* +* If there's a request pending, make sure its work function will return +* without doing anything. +*/ +if (dev->power.request_pending) + dev->power.request =RPM_REQ_NONE ;No need for the "if"; you can always do the assignment.
OK
Quoted 8 lines
drivers/base/power/runtime.c line 53
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_runtime_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ;Instead of assigning to retval, you could simply return these values.
I could, but I chose not to.
Quoted 11 lines
drivers/base/power/runtime.c line 61
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+else if (dev->power.idle_notification) + retval = -EINPROGRESS ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires >0 + || dev->power.runtime_status ==RPM_SUSPENDED + || dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EAGAIN ;Do we also want to rule out
RPM_RESUMING? That is,
Quoted 1 line
|| dev->power.runtime_status != RPM_ACTIVE
Yes, we do, thanks.
Quoted 9 lines
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.
Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.
Quoted 1 line
The same thing can be done in other places.
I'm not really sure it can.
Quoted 5 lines
Quoted 3 lines
+ *
__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the funtion has been called viapm_wq.Fix spelling of "function". Likewise in
__pm_runtime_resume.
Will do, thanks.
Quoted 13 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) +{...
drivers/base/power/runtime.c line 218
Quoted 7 lines
drivers/base/power/runtime.c new file +7
-0+spin_unlock_irq (&dev->power.lock); + +if (parent && !parent->power.ignore_children) +pm_request_idle (parent); + +if (notify) +pm_runtime_idle (dev);Move this up before the
spin_unlock_irqand call__pm_runtime_idleinstead. The same sort of thing can be done in__pm_runtime_resume.
OK
Quoted 18 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+static int __pm_request_suspend (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) +return -EINVAL ;drivers/base/power/runtime.c line 480
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+ +if (dev->power.runtime_status ==RPM_SUSPENDED ) + retval =1 ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 ) + retval = -EAGAIN ; +else if (dev->power.runtime_status ==RPM_SUSPENDING ) + retval = -EINPROGRESS ; +else if (!pm_children_suspended (dev)) + retval = -EBUSY ;Insert:
Quoted 8 lines
if (retval) return retval;Or else change the assignments to "return" statements. Yes, we agreed that a suspend request should override an existing idle-notify request. But if the new request fails then it shouldn't override anything. (Of course, if it fails for any of the reasons here then there can't be a pending idle-notify request anyway.)
However, if it's going to return 1, it should override existing idle-notify and suspend requests. I'll add
if (retval < 0)
return retval;
Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* +* Pending resume requests take precedence over us, but we can +* overtake any other pending request.
Quoted 8 lines
drivers/base/power/runtime.c line 493
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+*/ +if (dev->power.request ==RPM_REQ_RESUME ) + retval = -EAGAIN ; +else if (dev->power.request !=RPM_REQ_SUSPEND ) + dev->power.request = retval ? +RPM_REQ_NONE :RPM_REQ_SUSPEND ;Now there's no need to check retval.
It is. If retval is 1, we cancel pending idle-notify and suspend requests.
Quoted 6 lines
drivers/base/power/runtime.c line 496
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ +if (dev->power.request ==RPM_REQ_SUSPEND ) +return 0 ;Just simply:
return retval;
OK, I'll change that.
Quoted 11 lines
Some of these cases can't happen. For instance, if we reach here then the status can't be SUSPENDED or SUSPENDING, so there can't be a pending resume request.
drivers/base/power/runtime.c line 65
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+ } + +if (retval) +return retval;Now this isn't needed. Similar code rearrangements can be made in
__pm_request_resume.
OK
Quoted 3 lines
Quoted 1 line
+int
__pm_request_resume(struct device *dev)Should be static.
OK
Quoted 10 lines
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;This should go inside the spinlocked area.
Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.
Quoted 38 lines
Quoted 38 lines
drivers/base/power/runtime.c new file +38
-0+spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out; + +if (dev->power.runtime_status == status) + goto out_clear; + +if (status ==RPM_SUSPENDED ) { +/* It always is possible to set the status to 'suspended'. */ +if (parent) +atomic_add_unless (&parent->power.child_count,-1 ,0 ); + dev->power.runtime_status = status; + goto out_clear; + } + +if (parent) { +spin_lock_irq (&parent->power.lock); + +/* +* It is invalid to put an active child under a parent that is +* not active, has run-time PM enabled and the +* 'power.ignore_children' flag unset. +*/ +if (!parent->power.disable_depth + && !parent->power.ignore_children + && parent->power.runtime_status !=RPM_ACTIVE ) { + error = -EBUSY ; + }else { +if (dev->power.runtime_status ==RPM_SUSPENDED ) +atomic_inc (&parent->power.child_count); + dev->power.runtime_status = status; + } + +spin_unlock_irq (&parent->power.lock); + +if (error) + goto out;
Quoted 8 lines
drivers/base/power/runtime.c line 755
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+ }else { + dev->power.runtime_status = status; + } + + out_clear: + dev->power.runtime_failure = false;Move all those assignments to
dev->power.runtime_statusdown here.
OK
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;I don't see why this is needed.
If dev->power.runtime_failure, there's no need to do anything more.
Quoted 7 lines
Quoted 7 lines
drivers/base/power/runtime.c new file +7
-0+ +pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { + dev->power.request =RPM_REQ_NONE ; + +spin_unlock_irq (&dev->power.lock);
Quoted 8 lines
drivers/base/power/runtime.c line 842
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+ +cancel_work_sync (&dev->power.work); + +spin_lock_irq (&dev->power.lock); + + dev->power.request_pending = false;Remove excessive whitespace.
OK
Quoted 37 lines
Quoted 37 lines
drivers/base/power/runtime.c new file +37
-0+ } + +if (dev->power.runtime_status ==RPM_SUSPENDING + || dev->power.runtime_status ==RPM_RESUMING ) { +DEFINE_WAIT (wait); + +/* Suspend or wake-up in progress. */ +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (dev->power.runtime_status !=RPM_SUSPENDING + && dev->power.runtime_status !=RPM_RESUMING ) +break ; + +spin_unlock_irq (&dev->power.lock); + +schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + } + +if (dev->power.runtime_failure) + goto out; + +if (dev->power.idle_notification) { +DEFINE_WAIT (wait); + +for (;;) { +prepare_to_wait (&dev->power.wait_queue, &wait, +TASK_UNINTERRUPTIBLE ); +if (!dev->power.idle_notification) +break ; + +spin_unlock_irq (&dev->power.lock); +
Quoted 8 lines
drivers/base/power/runtime.c line 174
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+schedule (); + +spin_lock_irq (&dev->power.lock); + } +finish_wait (&dev->power.wait_queue, &wait); + }This wait loop should be merged with the previous loop.
OK
Quoted 6 lines
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+void pm_runtime_init (struct device *dev) +{ +spin_lock_init (&dev->power.lock); + + dev->power.runtime_status =RPM_SUSPENDED ; + dev->power.idle_notification = false;
Quoted 8 lines
drivers/base/power/runtime.c line 909
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+ + dev->power.disable_depth =1 ; +atomic_set (&dev->power.usage_count,0 ); + + dev->power.runtime_failure = false; + dev->power.last_error =0 ;You don't have to set values to 0; they are initialized by kzalloc.
No, I don't, but does it really hurt?
Quoted 5 lines
drivers/base/power/runtime.c line 922
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+ dev->power.suspend_timer.expires = jiffies; + dev->power.suspend_timer.data = (unsigned long )dev; + dev->power.suspend_timer.function = pm_suspend_timer_fn;Use
setup_timer()instead of assigning these fields directly.
OK
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); + +if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/*
Quoted 8 lines
drivers/base/power/runtime.c line 944
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);Shouldn't these last two lines be part of
__pm_runtime_set_status()?
No. It is valid to call __pm_runtime_set_status() when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.
Quoted 8 lines
include/linux/pm_runtime.h line 20
Quoted 5 lines
include/linux/pm_runtime.h new file +2
-0+extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev);I don't like seeing these two functions included in the public header file. It's enough to put them in
drivers/base/power/power.h.
OK
Quoted 23 lines
Quoted 23 lines
drivers/base/power/main.c +10
-0@@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** +* device_pm_init - Initialize the PM-related part of a device object +* @dev: Device object to initialize. +*/ +void device_pm_init (struct device *dev) +{ + dev->power.status =DPM_ON ; +pm_runtime_init (dev); +} + +/** * device_pm_lock - lock the list of active devices used by the PM core */ void device_pm_lock (void )@@ -105,6 +116,8 @@ void device_pm_remove(struct device *devmutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx);
Quoted 8 lines
drivers/base/power/main.c void device_pm_remove(struct device *dev · line 121
Quoted 3 lines
drivers/base/power/main.c +2
-0+ +pm_runtime_remove (dev); }Calling
pm_runtime_init()fromdevice_pm_init()andpm_runtime_remove()fromdevice_pm_remove()isn't good. IfCONFIG_PM_SLEEPisn't enabled then the calls won't be compiled, even ifCONFIG_PM_RUNTIMEis set.
Right, I shouldn't have moved device_pm_init() to main.c at all.
Quoted 9 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;What's the reason for the
-EBUSYerror?
If this is a wake-up device and pm_runtime_disable(dev) returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with -EBUSY (wake-up event during suspend).
Quoted 6 lines
Quoted 6 lines
drivers/base/dd.c +0
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name);
Quoted 7 lines
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 3 lines
drivers/base/dd.c +2
-0+pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use
put_noidlebecause it might subvert the driver's attempt to autosuspend.
I'm not sure how that's possible, but whatever.
Quoted 10 lines
Instead we should do something like this:
/* Wait for runtime PM calls to finish and prevent new calls * until the probe is done. */ pm_runtime_disable(dev); pm_runtime_get_noresume(dev); pm_runtime_enable(dev): ret = really_probe(dev, drv); pm_runtime_put_sync(dev);
Fine by me.
Quoted 10 lines
Documentation/power/runtime_pm.txt
Quoted 7 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
device_may_wakeup(dev) == false means wake-up is disabled for dev, so suspending it might not be a good idea.
Quoted 10 lines
Documentation/power/runtime_pm.txt line 148
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+Additionally, the helper functions provided by the PM core obey the following +rules: + + * If ->runtime_suspend() is about to be executed or the execution of it is + scheduled or there's a pending request to execute it, ->runtime_idle() will + not be executed for the same device.Shouldn't we allow
runtime_idlewhen a suspend is scheduled? The idle handler might decide to suspend right away instead of waiting for the timer to expire.
Hmm. We can.
Quoted 11 lines
Documentation/power/runtime_pm.txt line 266
Quoted 9 lines
+4. Run-time PM Device Helper Functions
Documentation/power/runtime_pm.txt new file +7
-0+ +The following run-time PM helper functions are defined in +drivers/base/power/runtime.c and include/linux/pm_runtime.h: + int pm_schedule_suspend(struct device *dev, unsigned int delay); + - schedule the execution of ->runtime_suspend() for the device's bus type + in future, where 'delay' is the time to wait before queuing up a suspend + work item in pm_wq, in miliseconds (if 'delay' is zero, the work item isFix spelling of "milli".
OK
Quoted 2 lines
Explain that the new delay will override the
old one if a suspend was already scheduled and not yet expired.
OK
Quoted 4 lines
Documentation/power/runtime_pm.txt line 285
Quoted 2 lines
Documentation/power/runtime_pm.txt new file +2
-0+ int pm_runtime_get_sync(struct device *dev); + - increment the device's usage counter, run pm_rutime_resume(dev) and returnFix spelling of "runtime". Same under
pm_runtime_put_sync.
OK
Thanks a lot for the comments, I'll post an updated patch addressing them in the next few days.
Best,
Rafael
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.
Quoted 2 lines
Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.
No, I meant do this:
if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock);
dev->bus->pm->runtime_idle(dev);
spin_lock_irq(&dev->power.lock);
}By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do, you should add "__releases(&dev->power.lock)" and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.
Quoted 3 lines
Quoted 1 line
The same thing can be done in other places.
I'm not really sure it can.
The other places aren't quite the same as this. I'll leave it to your discretion. :-)
Quoted 11 lines
Quoted 10 lines
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;This should go inside the spinlocked area.
Quoted 2 lines
Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.
Oops, my mistake. Never mind...
Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;I don't see why this is needed.
Quoted 1 line
If
dev->power.runtime_failure, there's no need to do anything more.
Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); + +if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/* Quoted 8 lines
drivers/base/power/runtime.c line 944
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);Shouldn't these last two lines be part of
__pm_runtime_set_status()?
Quoted 2 lines
No. It is valid to call
__pm_runtime_set_status()when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.
Ah, an interesting point. Suppose the device is in RPM_ACTIVE, and then someone calls pm_runtime_disable followed by pm_runtime_set_suspended. Then the device's status would change to RPM_SUSPENDED and the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?
Quoted 10 lines
Quoted 9 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;What's the reason for the
-EBUSYerror?
Quoted 3 lines
If this is a wake-up device and
pm_runtime_disable(dev)returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with-EBUSY(wake-up event during suspend).
I see. That's a little obscure; a comment would help. Even something as simple as:
error = -EBUSY; /* wake-up during suspend */
Quoted 12 lines
Quoted 11 lines
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 7 lines
drivers/base/dd.c +2
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use
put_noidlebecause it might subvert the driver's attempt to autosuspend.
Quoted 1 line
I'm not sure how that's possible, but whatever.
Suppose the probe routine does:
pm_runtime_get_sync(dev); /* do some work */ pm_runtime_put_sync(dev);
There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by
pm_runtime_get_noresume(dev); ... probe ... pm_runtime_put_noidle(dev);
then there won't be any idle notifications.
Quoted 10 lines
Quoted 7 lines
Documentation/power/runtime_pm.txt
Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
device_may_wakeup(dev)== false means wake-up is disabled for dev, so suspending it might not be a good idea.
Okay. This needs to be rephrased. For example,
In particular, if the driver requires remote wakeup capability for proper functioning and device_may_wakeup() returns 'false' for the device, then ->runtime_suspend() should return -EBUSY.
The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check device_may_wakeup().
Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.
Quoted 2 lines
Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.
No, I meant do this:
if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) {
spin_unlock_irq(&dev->power.lock);
dev->bus->pm->runtime_idle(dev);
spin_lock_irq(&dev->power.lock);
}By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do, you should add "__releases(&dev->power.lock)" and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.
Quoted 3 lines
Quoted 1 line
The same thing can be done in other places.
I'm not really sure it can.
The other places aren't quite the same as this. I'll leave it to your discretion. :-)
Quoted 11 lines
Quoted 10 lines
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;This should go inside the spinlocked area.
Quoted 2 lines
Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.
Oops, my mistake. Never mind...
Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;I don't see why this is needed.
Quoted 1 line
If
dev->power.runtime_failure, there's no need to do anything more.
Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); + +if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/* Quoted 8 lines
drivers/base/power/runtime.c line 944
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);Shouldn't these last two lines be part of
__pm_runtime_set_status()?
Quoted 2 lines
No. It is valid to call
__pm_runtime_set_status()when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.
Ah, an interesting point. Suppose the device is in RPM_ACTIVE, and then someone calls pm_runtime_disable followed by pm_runtime_set_suspended. Then the device's status would change to RPM_SUSPENDED and the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?
Quoted 10 lines
Quoted 9 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;What's the reason for the
-EBUSYerror?
Quoted 3 lines
If this is a wake-up device and
pm_runtime_disable(dev)returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with-EBUSY(wake-up event during suspend).
I see. That's a little obscure; a comment would help. Even something as simple as:
error = -EBUSY; /* wake-up during suspend */
Quoted 12 lines
Quoted 11 lines
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 7 lines
drivers/base/dd.c +2
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use
put_noidlebecause it might subvert the driver's attempt to autosuspend.
Quoted 1 line
I'm not sure how that's possible, but whatever.
Suppose the probe routine does:
pm_runtime_get_sync(dev); /* do some work */ pm_runtime_put_sync(dev);
There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by
pm_runtime_get_noresume(dev); ... probe ... pm_runtime_put_noidle(dev);
then there won't be any idle notifications.
Quoted 10 lines
Quoted 7 lines
Documentation/power/runtime_pm.txt
Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
device_may_wakeup(dev)== false means wake-up is disabled for dev, so suspending it might not be a good idea.
Okay. This needs to be rephrased. For example,
In particular, if the driver requires remote wakeup capability for proper functioning and device_may_wakeup() returns 'false' for the device, then ->runtime_suspend() should return -EBUSY.
The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check device_may_wakeup().
Alan Stern
Quoted 15 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.
Quoted 2 lines
Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.
Quoted 7 lines
No, I meant do this:
if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) { spin_unlock_irq(&dev->power.lock); dev->bus->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock); }
Ah, OK. That makes sense.
Quoted 2 lines
By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do,
Well, I guess I should. :-)
Quoted 41 lines
you should add "
__releases(&dev->power.lock)"
and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.Quoted 3 lines
Quoted 1 line
The same thing can be done in other places.
I'm not really sure it can.
The other places aren't quite the same as this. I'll leave it to your discretion. :-)
Quoted 11 lines
Quoted 10 lines
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;This should go inside the spinlocked area.
Quoted 2 lines
Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.
Oops, my mistake. Never mind...
Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;I don't see why this is needed.
Quoted 5 lines
Quoted 1 line
If
dev->power.runtime_failure, there's no need to do anything more.Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...
OK, I'll drop that if ().
Quoted 20 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); + +if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/* Quoted 8 lines
drivers/base/power/runtime.c line 944
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);Shouldn't these last two lines be part of
__pm_runtime_set_status()?Quoted 2 lines
No. It is valid to call
__pm_runtime_set_status()when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.
Quoted 6 lines
Ah, an interesting point. Suppose the device is in
RPM_ACTIVE, and then someone callspm_runtime_disablefollowed bypm_runtime_set_suspended. Then the device's status would change toRPM_SUSPENDEDand the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?
OK, having reconsidered that, I think you're right, it should go into __pm_runtime_set_status().
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;What's the reason for the
-EBUSYerror?
Quoted 8 lines
Quoted 3 lines
If this is a wake-up device and
pm_runtime_disable(dev)returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with-EBUSY(wake-up event during suspend).I see. That's a little obscure; a comment would help. Even something as simple as:
error =
-EBUSY; /* wake-up during suspend */
OK
Quoted 20 lines
Quoted 12 lines
Quoted 11 lines
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 7 lines
drivers/base/dd.c +2
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use
put_noidlebecause it might subvert the driver's attempt to autosuspend.Quoted 1 line
I'm not sure how that's possible, but whatever.
Suppose the probe routine does:
pm_runtime_get_sync(dev); /* do some work */ pm_runtime_put_sync(dev);
Quoted 8 lines
There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by
pm_runtime_get_noresume(dev); ... probe ... pm_runtime_put_noidle(dev);then there won't be any idle notifications.
Ah, OK. So the point is that we should always idle-notify after .probe(), because that's what .probe() would most probably want to do. I guess that makes sense.
Quoted 11 lines
Quoted 10 lines
Quoted 7 lines
Documentation/power/runtime_pm.txt
Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
device_may_wakeup(dev)== false means wake-up is disabled for dev, so suspending it might not be a good idea.
Quoted 8 lines
Okay. This needs to be rephrased. For example,
In particular, if the driver requires remote wakeup capability for proper functioning and
device_may_wakeup()returns 'false' for the device, then ->runtime_suspend() should return-EBUSY.The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check
device_may_wakeup().
OK
Thanks a lot again, I'll do my best to address the comments in the next version of the patch.
Best,
Rafael
PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
Hi,
The patch below should address all of your recent comments.
Additionally I changed a few bits that I thought could turn out to be problematic at one point.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.
Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.
--- Documentation/power/runtime_pm.txt | 382 +++++++++++++++ drivers/base/dd.c | 15 drivers/base/power/Makefile | 1 drivers/base/power/main.c | 20 drivers/base/power/power.h | 31 - drivers/base/power/runtime.c | 939 +++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 102 +++- include/linux/pm_runtime.h | 115 ++++ kernel/power/Kconfig | 14 kernel/power/main.c | 17 10 files changed, 1625 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
@@ -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
@@ -11,6 +11,7 @@#include <linux/kobject.h> #include <linux/string.h> #include <linux/resume-trace.h> +#include <linux/workqueue.h> #include "power.h"
@@ -217,8 +218,24 @@ static struct attribute_group attr_group .attrs = g, }; +#ifdef CONFIG_PM_RUNTIME +struct workqueue_struct *pm_wq; + +static int __init pm_start_workqueue (void ) +{ + pm_wq =create_freezeable_workqueue ("pm" ); + +return pm_wq ?0 : -ENOMEM ; +} +#else +static inline int pm_start_workqueue (void ) {return 0 ; } +#endif +static int __init pm_init (void ) { +int error =pm_start_workqueue (); +if (error) +return error; power_kobj =kobject_create_and_add ("power" ,NULL );if (!power_kobj)return -ENOMEM ;
include/linux/pm.h +99 -3
131 lines not on this page · raw message
drivers/base/power/Makefile +1 -0
@@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +939 -0
939 lines not on this page · raw message
include/linux/pm_runtime.h new file +115 -0
115 lines not on this page · raw message
drivers/base/power/main.c +19 -1
@@ -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 *devmutex_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 stmutex_unlock (&dpm_list_mtx);device_complete (dev, state); +pm_runtime_enable (dev);mutex_lock (&dpm_list_mtx); }
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up during suspend. */ + error = -EBUSY ; +else + error =device_prepare (dev, state);mutex_lock (&dpm_list_mtx);if (error) { dev->power.status =DPM_ON ; +pm_runtime_enable (dev);if (error == -EAGAIN ) {put_device (dev); error =0 ;
drivers/base/dd.c +15 -0
@@ -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_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +/* +* Wait for run-time PM calls to complete and prevent new suspend calls +* until the probe is done. +*/ +pm_runtime_disable (dev); +pm_runtime_get_noresume (dev); +pm_runtime_enable (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev); +if (!ret) +pm_runtime_idle (dev);return ret; }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
drivers/base/power/power.h +24 -7
@@ -1,7 +1,14 @@ -static inline void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -} +#ifdef CONFIG_PM_RUNTIME + +extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev); + +#else /* !CONFIG_PM_RUNTIME */ + +static inline void pm_runtime_init (struct device *dev) {} +static inline void pm_runtime_remove (struct device *dev) {} + +#endif /* !CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP
@@ -16,23 +23,33 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } +extern void device_pm_init (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);extern void device_pm_move_after (struct device *,struct device *);extern void device_pm_move_last (struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_pm_init (struct device *dev) +{ +pm_runtime_init (dev); +} + +static inline void device_pm_remove (struct device *dev) +{ +pm_runtime_remove (dev); +}static inline void device_pm_add (struct device *dev) {} -static inline void device_pm_remove (struct device *dev) {}static inline void device_pm_move_before (struct device *deva,struct device *devb) {}static inline void device_pm_move_after (struct device *deva,struct device *devb) {}static inline void device_pm_move_last (struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM
Documentation/power/runtime_pm.txt new file +382 -0
382 lines not on this page · raw message
Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
Quoted 6 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your recent comments.
Additionally I changed a few bits that I thought could turn out to be problematic at one point.
Looking good. I've got a few more suggestions.
It occurred to me that there's no need for a separate "runtime_failure" flag. A nonzero value of "last_error" will do just as well. If you make this change, note that it affects the documentation as well as the code.
If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.
Quoted 10 lines
+/**
+ *__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.
Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.
Quoted 6 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +3
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{
...
drivers/base/power/runtime.c line 141
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* Pending resume requests take precedence over us. */ +if (dev->power.request ==RPM_REQ_RESUME ) +return -EAGAIN ; +/* Other pending requests need to be canceled. */ + dev->power.request =RPM_REQ_NONE ; + }
Might as well use pm_runtime_cancel_pending since we have it:
/* Pending resume requests take precedence over us. */ if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME) return -EAGAIN; /* Other pending requests need to be canceled. */ pm_runtime_cancel_pending(dev);
...
drivers/base/power/runtime.c line 212
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+if (dev->power.deferred_resume) { +__pm_request_resume (dev);
__pm_runtime_resume instead?
Quoted 11 lines
+/**
+ *__pm_runtime_resume- Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.
Change the last two sentences as follows: Cancel any pending requests.
drivers/base/power/runtime.c line 268
Quoted 12 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +9
-0+int __pm_runtime_resume (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ +struct device *parent =NULL ; +int retval =0 ; + + repeat: +if (dev->power.runtime_failure) +return -EINVAL ;
Here and in two places below, goto out_parent instead of returning directly.
...
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+if (!parent && dev->parent) { +/* +* Increment the parent's resume counter and resume it if +* necessary.
drivers/base/power/runtime.c line 320
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+*/ + parent = dev->parent; +spin_unlock_irq (&dev->power.lock); + + retval =pm_runtime_get_sync (parent); + +spin_lock_irq (&dev->power.lock); +/* We can resume if the parent's run-time PM is disabled. */ +if (retval <0 && retval != -EAGAIN ) + goto out_parent;
Instead of checking retval, how about checking the parent's PM status? Also, this isn't needed if the parent is set to ignore children.
drivers/base/power/runtime.c line 439
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static int __pm_request_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires > 0
This line should be removed.
...
drivers/base/power/runtime.c line 460
Quoted 13 lines
drivers/base/power/runtime.c new file +13
-0+if (dev->power.request_pending && dev->power.request !=RPM_REQ_NONE ) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_IDLE ) + retval = -EAGAIN ; +return retval; + } + + dev->power.request =RPM_REQ_IDLE ; +if (dev->power.request_pending) +return retval; + + dev->power.request_pending = true; +queue_work (pm_wq, &dev->power.work);
This should be done consistently with the other routines. Thus:
if (dev->power.request_pending) {
/* All other requests take precedence. */
if (dev->power.request == RPM_REQ_NONE)
dev->power.request = RPM_REQ_IDLE;
else if (dev->power.request != RPM_REQ_IDLE)
retval = -EAGAIN;
return retval;
}
dev->power.request = RPM_REQ_IDLE;
dev->power.request_pending = true;
queue_work(pm_wq, &dev->power.work);Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags;
drivers/base/power/runtime.c line 738
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+bool notify_parent = false; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ; + +spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out;
Set "error" to a negative code?
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 8 lines
drivers/base/power/main.c +3
-1@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up during suspend. */ + error = -EBUSY ;
Or maybe "Wakeup was requested during sleep transition."
Documentation/power/runtime_pm.txt line 219
Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5
-0+ unsigned int deferred_resume; + - set if ->runtime_resume() is about to be run while ->runtime_suspend() is + being executed for that device and it is not practical to wait for the + suspend to complete; means "queue up a resume request as soon as you've + suspended"
"start a resume" instead of "queue up a resume request"?
Quoted 1 line
Documentation/power/runtime_pm.txt new file +1
-0+5. Run-time PM Initialization
...
Quoted 1 line
+If the defaul initial run-time PM status of the device (i.e. 'suspended')
Fix spelling of "default".
Documentation/power/runtime_pm.txt line 381
Quoted 12 lines
Documentation/power/runtime_pm.txt new file +12
-0+reflects the actual state of the device, its bus type's or its driver's +->probe() callback will likely need to wake it up using one of the PM core's +helper functions described in Section 4. In that case, pm_runtime_resume() +should be used. Of course, for this purpose the device's run-time PM has to be +enabled earlier by calling pm_runtime_enable(). + +If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their +asynchronous counterparts, they will fail returning -EAGAIN, because the +device's usage counter is incremented by the core before executing ->probe(). +Still, it may be desirable to suspend the device as soon as ->probe() has +finished, so the core uses pm_runtime_idle() to invoke the device bus type's +->runtime_idle() callback at that time, which only happens even if ->probe()
s/which only happens even/but only/
Quoted 1 line
+is successful.
Alan Stern
Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
In reply to Rafael J. WysockiQuoted 6 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your recent comments.
Additionally I changed a few bits that I thought could turn out to be problematic at one point.
Looking good. I've got a few more suggestions.
It occurred to me that there's no need for a separate "runtime_failure" flag. A nonzero value of "last_error" will do just as well. If you make this change, note that it affects the documentation as well as the code.
If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.
Quoted 10 lines
+/**
+ *__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.
Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.
Quoted 6 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +3
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{
...
drivers/base/power/runtime.c line 141
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* Pending resume requests take precedence over us. */ +if (dev->power.request ==RPM_REQ_RESUME ) +return -EAGAIN ; +/* Other pending requests need to be canceled. */ + dev->power.request =RPM_REQ_NONE ; + }
Might as well use pm_runtime_cancel_pending since we have it:
/* Pending resume requests take precedence over us. */ if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME) return -EAGAIN; /* Other pending requests need to be canceled. */ pm_runtime_cancel_pending(dev);
...
drivers/base/power/runtime.c line 212
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+if (dev->power.deferred_resume) { +__pm_request_resume (dev);
__pm_runtime_resume instead?
Quoted 11 lines
+/**
+ *__pm_runtime_resume- Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.
Change the last two sentences as follows: Cancel any pending requests.
drivers/base/power/runtime.c line 268
Quoted 12 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +9
-0+int __pm_runtime_resume (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ +struct device *parent =NULL ; +int retval =0 ; + + repeat: +if (dev->power.runtime_failure) +return -EINVAL ;
Here and in two places below, goto out_parent instead of returning directly.
...
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+if (!parent && dev->parent) { +/* +* Increment the parent's resume counter and resume it if +* necessary.
drivers/base/power/runtime.c line 320
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+*/ + parent = dev->parent; +spin_unlock_irq (&dev->power.lock); + + retval =pm_runtime_get_sync (parent); + +spin_lock_irq (&dev->power.lock); +/* We can resume if the parent's run-time PM is disabled. */ +if (retval <0 && retval != -EAGAIN ) + goto out_parent;
Instead of checking retval, how about checking the parent's PM status? Also, this isn't needed if the parent is set to ignore children.
drivers/base/power/runtime.c line 439
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static int __pm_request_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires > 0
This line should be removed.
...
drivers/base/power/runtime.c line 460
Quoted 13 lines
drivers/base/power/runtime.c new file +13
-0+if (dev->power.request_pending && dev->power.request !=RPM_REQ_NONE ) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_IDLE ) + retval = -EAGAIN ; +return retval; + } + + dev->power.request =RPM_REQ_IDLE ; +if (dev->power.request_pending) +return retval; + + dev->power.request_pending = true; +queue_work (pm_wq, &dev->power.work);
This should be done consistently with the other routines. Thus:
if (dev->power.request_pending) {
/* All other requests take precedence. */
if (dev->power.request == RPM_REQ_NONE)
dev->power.request = RPM_REQ_IDLE;
else if (dev->power.request != RPM_REQ_IDLE)
retval = -EAGAIN;
return retval;
}
dev->power.request = RPM_REQ_IDLE;
dev->power.request_pending = true;
queue_work(pm_wq, &dev->power.work);Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags;
drivers/base/power/runtime.c line 738
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+bool notify_parent = false; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ; + +spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out;
Set "error" to a negative code?
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 8 lines
drivers/base/power/main.c +3
-1@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up during suspend. */ + error = -EBUSY ;
Or maybe "Wakeup was requested during sleep transition."
Documentation/power/runtime_pm.txt line 219
Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5
-0+ unsigned int deferred_resume; + - set if ->runtime_resume() is about to be run while ->runtime_suspend() is + being executed for that device and it is not practical to wait for the + suspend to complete; means "queue up a resume request as soon as you've + suspended"
"start a resume" instead of "queue up a resume request"?
Quoted 1 line
Documentation/power/runtime_pm.txt new file +1
-0+5. Run-time PM Initialization
...
Quoted 1 line
+If the defaul initial run-time PM status of the device (i.e. 'suspended')
Fix spelling of "default".
Documentation/power/runtime_pm.txt line 381
Quoted 12 lines
Documentation/power/runtime_pm.txt new file +12
-0+reflects the actual state of the device, its bus type's or its driver's +->probe() callback will likely need to wake it up using one of the PM core's +helper functions described in Section 4. In that case, pm_runtime_resume() +should be used. Of course, for this purpose the device's run-time PM has to be +enabled earlier by calling pm_runtime_enable(). + +If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their +asynchronous counterparts, they will fail returning -EAGAIN, because the +device's usage counter is incremented by the core before executing ->probe(). +Still, it may be desirable to suspend the device as soon as ->probe() has +finished, so the core uses pm_runtime_idle() to invoke the device bus type's +->runtime_idle() callback at that time, which only happens even if ->probe()
s/which only happens even/but only/
Quoted 1 line
+is successful.
Alan Stern
Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
Quoted 6 lines · Alan Stern
Quoted 4 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your recent comments.
Quoted 8 lines
Quoted 2 lines
Additionally I changed a few bits that I thought could turn out to be problematic at one point.
Looking good. I've got a few more suggestions.
It occurred to me that there's no need for a separate "
runtime_failure" flag. A nonzero value of "last_error" will do just as well.
Yes, good catch. I don't quite remember why I wanted the flag and a separate error field.
Quoted 2 lines
If you make this change, note that it affects the documentation as well as the code.
Sure.
Quoted 3 lines
If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.
Well, it's not very clear what to return to the caller in such a case. I guess we can return -EAGAIN.
Quoted 11 lines
Quoted 10 lines
+/**
+ *__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.
Quoted 2 lines
Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.
OK
Quoted 19 lines
Quoted 6 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +3
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{...
drivers/base/power/runtime.c line 141
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* Pending resume requests take precedence over us. */ +if (dev->power.request ==RPM_REQ_RESUME ) +return -EAGAIN ; +/* Other pending requests need to be canceled. */ + dev->power.request =RPM_REQ_NONE ; + }Might as well use
pm_runtime_cancel_pendingsince we have it:
Quoted 6 lines
/* Pending resume requests take precedence over us. */ if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME) return -EAGAIN; /* Other pending requests need to be canceled. */ pm_runtime_cancel_pending(dev);
OK
Quoted 5 lines
...
drivers/base/power/runtime.c line 212
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+if (dev->power.deferred_resume) { +__pm_request_resume (dev);
__pm_runtime_resumeinstead?
In which case we shouldn't execute the code below, IMO, but return immediately instead.
Quoted 12 lines
Quoted 11 lines
+/**
+ *__pm_runtime_resume- Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.
Quoted 1 line
Change the last two sentences as follows: Cancel any pending requests.
OK
Quoted 7 lines
Quoted 7 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +4
-0+int __pm_runtime_resume (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ +struct device *parent =NULL ;
Quoted 8 lines
drivers/base/power/runtime.c line 131
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+int retval =0 ; + + repeat: +if (dev->power.runtime_failure) +return -EINVAL ;Here and in two places below, goto
out_parentinstead of returning directly.
Ah, that was a real bug. Thanks for catching it!
Quoted 17 lines
...
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+if (!parent && dev->parent) { +/* +* Increment the parent's resume counter and resume it if +* necessary. drivers/base/power/runtime.c line 320
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+*/ + parent = dev->parent; +spin_unlock_irq (&dev->power.lock); + + retval =pm_runtime_get_sync (parent); + +spin_lock_irq (&dev->power.lock); +/* We can resume if the parent's run-time PM is disabled. */ +if (retval <0 && retval != -EAGAIN ) + goto out_parent;Instead of checking retval, how about checking the parent's PM status?
Should work.
Quoted 1 line
Also, this isn't needed if the parent is set to ignore children.
OK, I'll change that.
Quoted 11 lines
drivers/base/power/runtime.c line 439
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static int __pm_request_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires > 0This line should be removed.
Yeah, thanks!
Quoted 17 lines
...
drivers/base/power/runtime.c line 460
Quoted 13 lines
drivers/base/power/runtime.c new file +13
-0+if (dev->power.request_pending && dev->power.request !=RPM_REQ_NONE ) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_IDLE ) + retval = -EAGAIN ; +return retval; + } + + dev->power.request =RPM_REQ_IDLE ; +if (dev->power.request_pending) +return retval; + + dev->power.request_pending = true; +queue_work (pm_wq, &dev->power.work);This should be done consistently with the other routines. Thus:
Quoted 12 lines
if (dev->power.request_pending) {
/* All other requests take precedence. */
if (dev->power.request == RPM_REQ_NONE)
dev->power.request = RPM_REQ_IDLE;
else if (dev->power.request != RPM_REQ_IDLE)
retval = -EAGAIN;
return retval;
}
dev->power.request = RPM_REQ_IDLE;
dev->power.request_pending = true;
queue_work(pm_wq, &dev->power.work);OK
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +bool notify_parent = false; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED )
Quoted 8 lines
drivers/base/power/runtime.c line 738
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+return -EINVAL ; + +spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out;Set "error" to a negative code?
OK
Quoted 10 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 8 lines
drivers/base/power/main.c +3
-1@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up during suspend. */ + error = -EBUSY ;Or maybe "Wakeup was requested during sleep transition."
Sounds better.
Quoted 7 lines
Documentation/power/runtime_pm.txt line 219
Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5
-0+ unsigned int deferred_resume; + - set if ->runtime_resume() is about to be run while ->runtime_suspend() is + being executed for that device and it is not practical to wait for the + suspend to complete; means "queue up a resume request as soon as you've + suspended""start a resume" instead of "queue up a resume request"?
OK
Quoted 5 lines
Quoted 1 line
Documentation/power/runtime_pm.txt new file +1
-0+5. Run-time PM Initialization...
Quoted 1 line
+If the defaul initial run-time PM status of the device (i.e. 'suspended')
Fix spelling of "default".
OK
Quoted 6 lines
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+reflects the actual state of the device, its bus type's or its driver's +->probe() callback will likely need to wake it up using one of the PM core's +helper functions described in Section 4. In that case, pm_runtime_resume() +should be used. Of course, for this purpose the device's run-time PM has to be +enabled earlier by calling pm_runtime_enable(). +
Quoted 10 lines
Documentation/power/runtime_pm.txt line 381
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their +asynchronous counterparts, they will fail returning -EAGAIN, because the +device's usage counter is incremented by the core before executing ->probe(). +Still, it may be desirable to suspend the device as soon as ->probe() has +finished, so the core uses pm_runtime_idle() to invoke the device bus type's +->runtime_idle() callback at that time, which only happens even if ->probe()s/which only happens even/but only/
Quoted 1 line
+is successful.
OK
Thanks for the comments! In fact I've already updated the patch to address them, so I'll send it in a little while.
Best,
Rafael
Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
Quoted 6 lines · Rafael J. Wysocki
Quoted 3 lines
If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.
Well, it's not very clear what to return to the caller in such a case. I guess we can return
-EAGAIN.
I would have said to return 0, just as if the resume had been queued. But it doesn't matter very much.
Alan Stern
Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
In reply to Rafael J. WysockiQuoted 6 lines · Rafael J. Wysocki
Quoted 3 lines
If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.
Well, it's not very clear what to return to the caller in such a case. I guess we can return
-EAGAIN.
I would have said to return 0, just as if the resume had been queued. But it doesn't matter very much.
Alan Stern
Re: [PATCH update] PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
In reply to Alan SternQuoted 6 lines · Alan Stern
Quoted 4 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your recent comments.
Quoted 8 lines
Quoted 2 lines
Additionally I changed a few bits that I thought could turn out to be problematic at one point.
Looking good. I've got a few more suggestions.
It occurred to me that there's no need for a separate "
runtime_failure" flag. A nonzero value of "last_error" will do just as well.
Yes, good catch. I don't quite remember why I wanted the flag and a separate error field.
Quoted 2 lines
If you make this change, note that it affects the documentation as well as the code.
Sure.
Quoted 3 lines
If we defer a resume request while a suspend is in progress, then when the suspend finishes should the resume be carried out immediately rather than queued? I don't see any reason why not.
Well, it's not very clear what to return to the caller in such a case. I guess we can return -EAGAIN.
Quoted 11 lines
Quoted 10 lines
+/**
+ *__pm_runtime_suspend- Carry out run-time suspend of given device.
+ * @dev: Device to suspend.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be suspended and run the ->runtime_suspend() callback
+ * provided by its bus type. If another suspend has been started earlier, wait
+ * for it to finish. If there's an idle notification pending, cancel it. If
+ * there's a suspend request scheduled while this function is running and @sync
+ * is 'true', cancel that request.
Quoted 2 lines
Change the last two sentences as follows: If an idle notification or suspend request is pending or scheduled, cancel it.
OK
Quoted 19 lines
Quoted 6 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +3
-0+int __pm_runtime_suspend (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{...
drivers/base/power/runtime.c line 141
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+pm_runtime_deactivate_timer (dev); + +if (dev->power.request_pending) { +/* Pending resume requests take precedence over us. */ +if (dev->power.request ==RPM_REQ_RESUME ) +return -EAGAIN ; +/* Other pending requests need to be canceled. */ + dev->power.request =RPM_REQ_NONE ; + }Might as well use
pm_runtime_cancel_pendingsince we have it:
Quoted 6 lines
/* Pending resume requests take precedence over us. */ if (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME) return -EAGAIN; /* Other pending requests need to be canceled. */ pm_runtime_cancel_pending(dev);
OK
Quoted 5 lines
...
drivers/base/power/runtime.c line 212
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+if (dev->power.deferred_resume) { +__pm_request_resume (dev);
__pm_runtime_resumeinstead?
In which case we shouldn't execute the code below, IMO, but return immediately instead.
Quoted 12 lines
Quoted 11 lines
+/**
+ *__pm_runtime_resume- Carry out run-time resume of given device.
+ * @dev: Device to resume.
+ * @from_wq: If set, the function has been called viapm_wq.
+ *
+ * Check if the device can be woken up and run the ->runtime_resume() callback
+ * provided by its bus type. If another resume has been started earlier, wait
+ * for it to finish. If there's a suspend running in parallel with this
+ * function, wait for it to finish and resume the device. If there's a suspend
+ * request or idle notification pending, cancel it. If there's a resume request
+ * scheduled while this function is running, cancel that request.
Quoted 1 line
Change the last two sentences as follows: Cancel any pending requests.
OK
Quoted 7 lines
Quoted 7 lines
+ *
+ * This function must be called underdev->power.lockwith interrupts disabled.
+ */drivers/base/power/runtime.c new file +4
-0+int __pm_runtime_resume (struct device *dev,bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ +struct device *parent =NULL ;
Quoted 8 lines
drivers/base/power/runtime.c line 131
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+int retval =0 ; + + repeat: +if (dev->power.runtime_failure) +return -EINVAL ;Here and in two places below, goto
out_parentinstead of returning directly.
Ah, that was a real bug. Thanks for catching it!
Quoted 17 lines
...
Quoted 4 lines
drivers/base/power/runtime.c new file +4
-0+if (!parent && dev->parent) { +/* +* Increment the parent's resume counter and resume it if +* necessary. drivers/base/power/runtime.c line 320
Quoted 10 lines
drivers/base/power/runtime.c new file +10
-0+*/ + parent = dev->parent; +spin_unlock_irq (&dev->power.lock); + + retval =pm_runtime_get_sync (parent); + +spin_lock_irq (&dev->power.lock); +/* We can resume if the parent's run-time PM is disabled. */ +if (retval <0 && retval != -EAGAIN ) + goto out_parent;Instead of checking retval, how about checking the parent's PM status?
Should work.
Quoted 1 line
Also, this isn't needed if the parent is set to ignore children.
OK, I'll change that.
Quoted 11 lines
drivers/base/power/runtime.c line 439
Quoted 9 lines
drivers/base/power/runtime.c new file +9
-0+static int __pm_request_idle (struct device *dev) +{ +int retval =0 ; + +if (dev->power.runtime_failure) + retval = -EINVAL ; +else if (atomic_read (&dev->power.usage_count) >0 + || dev->power.disable_depth >0 + || dev->power.timer_expires > 0This line should be removed.
Yeah, thanks!
Quoted 17 lines
...
drivers/base/power/runtime.c line 460
Quoted 13 lines
drivers/base/power/runtime.c new file +13
-0+if (dev->power.request_pending && dev->power.request !=RPM_REQ_NONE ) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_IDLE ) + retval = -EAGAIN ; +return retval; + } + + dev->power.request =RPM_REQ_IDLE ; +if (dev->power.request_pending) +return retval; + + dev->power.request_pending = true; +queue_work (pm_wq, &dev->power.work);This should be done consistently with the other routines. Thus:
Quoted 12 lines
if (dev->power.request_pending) {
/* All other requests take precedence. */
if (dev->power.request == RPM_REQ_NONE)
dev->power.request = RPM_REQ_IDLE;
else if (dev->power.request != RPM_REQ_IDLE)
retval = -EAGAIN;
return retval;
}
dev->power.request = RPM_REQ_IDLE;
dev->power.request_pending = true;
queue_work(pm_wq, &dev->power.work);OK
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +bool notify_parent = false; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED )
Quoted 8 lines
drivers/base/power/runtime.c line 738
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+return -EINVAL ; + +spin_lock_irqsave (&dev->power.lock, flags); + +if (!dev->power.runtime_failure && !dev->power.disable_depth) + goto out;Set "error" to a negative code?
OK
Quoted 10 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 8 lines
drivers/base/power/main.c +3
-1@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up during suspend. */ + error = -EBUSY ;Or maybe "Wakeup was requested during sleep transition."
Sounds better.
Quoted 7 lines
Documentation/power/runtime_pm.txt line 219
Quoted 5 lines
Documentation/power/runtime_pm.txt new file +5
-0+ unsigned int deferred_resume; + - set if ->runtime_resume() is about to be run while ->runtime_suspend() is + being executed for that device and it is not practical to wait for the + suspend to complete; means "queue up a resume request as soon as you've + suspended""start a resume" instead of "queue up a resume request"?
OK
Quoted 5 lines
Quoted 1 line
Documentation/power/runtime_pm.txt new file +1
-0+5. Run-time PM Initialization...
Quoted 1 line
+If the defaul initial run-time PM status of the device (i.e. 'suspended')
Fix spelling of "default".
OK
Quoted 6 lines
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+reflects the actual state of the device, its bus type's or its driver's +->probe() callback will likely need to wake it up using one of the PM core's +helper functions described in Section 4. In that case, pm_runtime_resume() +should be used. Of course, for this purpose the device's run-time PM has to be +enabled earlier by calling pm_runtime_enable(). +
Quoted 10 lines
Documentation/power/runtime_pm.txt line 381
Quoted 6 lines
Documentation/power/runtime_pm.txt new file +6
-0+If ->probe() calls pm_runtime_suspend() or pm_runtime_idle(), or their +asynchronous counterparts, they will fail returning -EAGAIN, because the +device's usage counter is incremented by the core before executing ->probe(). +Still, it may be desirable to suspend the device as soon as ->probe() has +finished, so the core uses pm_runtime_idle() to invoke the device bus type's +->runtime_idle() callback at that time, which only happens even if ->probe()s/which only happens even/but only/
Quoted 1 line
+is successful.
OK
Thanks for the comments! In fact I've already updated the patch to address them, so I'll send it in a little while.
Best,
Rafael
PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Alan SternHi,
The patch below should address all of your most recent comments.
Best,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.
Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.
--- Documentation/power/runtime_pm.txt | 377 ++++++++++++++ drivers/base/dd.c | 15 drivers/base/power/Makefile | 1 drivers/base/power/main.c | 20 drivers/base/power/power.h | 31 - drivers/base/power/runtime.c | 944 +++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 101 +++ include/linux/pm_runtime.h | 115 ++++ kernel/power/Kconfig | 14 kernel/power/main.c | 17 10 files changed, 1624 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
@@ -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
@@ -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
@@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +944 -0
944 lines not on this page · raw message
include/linux/pm_runtime.h new file +115 -0
115 lines not on this page · raw message
drivers/base/power/main.c +19 -1
@@ -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 *devmutex_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 stmutex_unlock (&dpm_list_mtx);device_complete (dev, state); +pm_runtime_enable (dev);mutex_lock (&dpm_list_mtx); }
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up requested during system sleep transition. */ + error = -EBUSY ; +else + error =device_prepare (dev, state);mutex_lock (&dpm_list_mtx);if (error) { dev->power.status =DPM_ON ; +pm_runtime_enable (dev);if (error == -EAGAIN ) {put_device (dev); error =0 ;
drivers/base/dd.c +15 -0
@@ -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_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +/* +* Wait for run-time PM calls to complete and prevent new suspend calls +* until the probe is done. +*/ +pm_runtime_disable (dev); +pm_runtime_get_noresume (dev); +pm_runtime_enable (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev); +if (!ret) +pm_runtime_idle (dev);return ret; }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
drivers/base/power/power.h +24 -7
@@ -1,7 +1,14 @@ -static inline void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -} +#ifdef CONFIG_PM_RUNTIME + +extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev); + +#else /* !CONFIG_PM_RUNTIME */ + +static inline void pm_runtime_init (struct device *dev) {} +static inline void pm_runtime_remove (struct device *dev) {} + +#endif /* !CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP
@@ -16,23 +23,33 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } +extern void device_pm_init (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);extern void device_pm_move_after (struct device *,struct device *);extern void device_pm_move_last (struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_pm_init (struct device *dev) +{ +pm_runtime_init (dev); +} + +static inline void device_pm_remove (struct device *dev) +{ +pm_runtime_remove (dev); +}static inline void device_pm_add (struct device *dev) {} -static inline void device_pm_remove (struct device *dev) {}static inline void device_pm_move_before (struct device *deva,struct device *devb) {}static inline void device_pm_move_after (struct device *deva,struct device *devb) {}static inline void device_pm_move_last (struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM
Documentation/power/runtime_pm.txt new file +377 -0
377 lines not on this page · raw message
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct
dev_pm_info' and device run-time PM callbacks to 'structdev_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.
Signed-off-by Rafael J. Wysocki <[email protected]>
I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.
Anyway, I'm not far from an "Acked-by", but please look at the following series:
[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807
Maybe there is some code in there that you can include in v14? Let me know!
Cheers,
/ magnus
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Rafael J. WysockiQuoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct
dev_pm_info' and device run-time PM callbacks to 'structdev_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.
Signed-off-by Rafael J. Wysocki <[email protected]>
I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.
Anyway, I'm not far from an "Acked-by", but please look at the following series:
[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807
Maybe there is some code in there that you can include in v14? Let me know!
Cheers,
/ magnus
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 21 lines · Magnus Damm
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct
dev_pm_info' and device run-time PM callbacks to 'structdev_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.
Signed-off-by Rafael J. Wysocki <[email protected]>I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.
Quoted 5 lines
Anyway, I'm not far from an "Acked-by", but please look at the following series:
[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807
Maybe there is some code in there that you can include in v14? Let me know!
I will.
Thanks,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Magnus DammQuoted 21 lines · Magnus Damm
Quoted 9 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct
dev_pm_info' and device run-time PM callbacks to 'structdev_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.
Signed-off-by Rafael J. Wysocki <[email protected]>I've rebased my series of patches to v13. My platform device Runtime PM code for SuperH works well with drivers for i2c, uio and fbdev with v13. Thank you. I'll post a SuperH specific patch set some time next week.
Quoted 5 lines
Anyway, I'm not far from an "Acked-by", but please look at the following series:
[PATCH 00/05] PM: Runtime PM v13 for Platform Devices 20090807
Maybe there is some code in there that you can include in v14? Let me know!
I will.
Thanks,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Rafael J. WysockiQuoted 3 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your most recent comments.
Only two comments.
Quoted 2 lines
(quoted diff) +2
-0+static int __pm_request_idle(struct device *dev) +{
...
drivers/base/power/runtime.c line 455
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+if (dev->power.request_pending) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_NONE )
Replace != with ==.
Quoted 8 lines
drivers/base/power/runtime.c new file +4
-0+ dev->power.request =RPM_REQ_IDLE ; + else if (dev->power.request != RPM_REQ_IDLE) + retval = -EAGAIN ; +return retval;drivers/base/dd.c +0
-0
...
Quoted 10 lines
drivers/base/dd.c +2
-0@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
drivers/base/dd.c static void __device_release_driver(stru · line 343
Quoted 7 lines
drivers/base/dd.c +2
-0@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 9 lines · Alan Stern
Quoted 3 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your most recent comments.
Only two comments.
Quoted 8 lines
Quoted 2 lines
(quoted diff) +2
-0+static int __pm_request_idle(struct device *dev) +{...
drivers/base/power/runtime.c line 455
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+if (dev->power.request_pending) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_NONE )Replace != with ==.
Good catch, thanks!
Quoted 17 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +4
-0+ dev->power.request =RPM_REQ_IDLE ; + else if (dev->power.request != RPM_REQ_IDLE) + retval = -EAGAIN ; +return retval;drivers/base/dd.c +0
-0...
Quoted 8 lines
drivers/base/dd.c +2
-0@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);
Quoted 14 lines
drivers/base/dd.c static void __device_release_driver(stru · line 343
Quoted 9 lines
drivers/base/dd.c +2
-0if (dev->bus)@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);
before and
pm_runtime_put_noidle()
after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.
At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines
We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);before and
pm_runtime_put_noidle()
Quoted 7 lines
after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our
pm_runtime_put_noidle()won't decrease the usage counter below zero.At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.
The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.
The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.
We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:
1. The device sends a wakeup request, probably in the form of an IRQ.
2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.
3. The driver calls pm_request_resume.
4. The runtime PM core carries out the resume callback.
If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Rafael J. WysockiQuoted 16 lines · Rafael J. Wysocki
Quoted 4 lines
We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);before and
pm_runtime_put_noidle()
Quoted 7 lines
after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our
pm_runtime_put_noidle()won't decrease the usage counter below zero.At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.
The problem with this is that it calls pm_runtime_disable() at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.
The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with pm_runtime_disable. We should be able to use this for both probe and remove.
We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:
1. The device sends a wakeup request, probably in the form of an IRQ.
2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.
3. The driver calls pm_request_resume.
4. The runtime PM core carries out the resume callback.
If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 36 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines
We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);before and
pm_runtime_put_noidle()Quoted 7 lines
after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our
pm_runtime_put_noidle()won't decrease the usage counter below zero.At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.
The problem with this is that it calls
pm_runtime_disable()at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
Quoted 5 lines
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with
pm_runtime_disable. We should be able to use this for both probe and remove.
Isn't it what's done in rev. 14?
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);
is exactly a barrier like this. How exactly would you like to implement it instead?
Quoted 10 lines
We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:
1. The device sends a wakeup request, probably in the form of an IRQ.
2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.
Quoted 8 lines
3. The driver calls
pm_request_resume.4. The runtime PM core carries out the resume callback.
If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.
The idea is that once the system sleep transition has started, the non-runtime callbacks are in charge of handling the device.
Quoted 2 lines
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines
The problem with this is that it calls
pm_runtime_disable()at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
Quoted 5 lines
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with
pm_runtime_disable. We should be able to use this for both probe and remove.
Quoted 7 lines
Isn't it what's done in rev. 14?
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);is exactly a barrier like this.
It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.
Quoted 2 lines
How exactly would you like to implement it
instead?
As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.
Quoted 7 lines
Quoted 2 lines
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.
All right. Since it involves a race, the problem may not show up for a while.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Rafael J. WysockiQuoted 16 lines · Rafael J. Wysocki
Quoted 10 lines
The problem with this is that it calls
pm_runtime_disable()at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
Quoted 5 lines
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with
pm_runtime_disable. We should be able to use this for both probe and remove.
Quoted 7 lines
Isn't it what's done in rev. 14?
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);is exactly a barrier like this.
It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.
Quoted 2 lines
How exactly would you like to implement it
instead?
As described above. The barrier would be equivalent to pm_runtime_get_noresume followed by pm_runtime_disable except that it wouldn't actually disable anything.
Quoted 7 lines
Quoted 2 lines
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.
All right. Since it involves a race, the problem may not show up for a while.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 29 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines
The problem with this is that it calls
pm_runtime_disable()at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
Quoted 5 lines
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with
pm_runtime_disable. We should be able to use this for both probe and remove.Quoted 7 lines
Isn't it what's done in rev. 14?
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);is exactly a barrier like this.
It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.
Quoted 6 lines
Quoted 2 lines
How exactly would you like to implement it
instead?As described above. The barrier would be equivalent to
pm_runtime_get_noresumefollowed bypm_runtime_disableexcept that it wouldn't actually disable anything.
OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.
Quoted 10 lines
Quoted 7 lines
Quoted 2 lines
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.
All right. Since it involves a race, the problem may not show up for a while.
Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.
Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.
Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines
How exactly would you like to implement it
instead?As described above. The barrier would be equivalent to
pm_runtime_get_noresumefollowed bypm_runtime_disableexcept that it wouldn't actually disable anything.OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.
Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.
Quoted 4 lines
Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.
The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.
Quoted 3 lines
Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return
-EBUSY.
It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.
Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.
Quoted 4 lines
Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.
If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.
In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.
The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet. For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Rafael J. WysockiQuoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines
How exactly would you like to implement it
instead?As described above. The barrier would be equivalent to
pm_runtime_get_noresumefollowed bypm_runtime_disableexcept that it wouldn't actually disable anything.OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.
Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.
Quoted 4 lines
Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.
The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.
Quoted 3 lines
Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return
-EBUSY.
It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.
Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.
Quoted 4 lines
Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.
If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.
In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.
The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet. For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.
Alan Stern
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Quoted 22 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines
How exactly would you like to implement it
instead?As described above. The barrier would be equivalent to
pm_runtime_get_noresumefollowed bypm_runtime_disableexcept that it wouldn't actually disable anything.OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.
Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.
Quoted 4 lines
Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.
Quoted 4 lines
The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through
pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.
Well, I'm not sure if that's a good idea, but also I have no good techincal arguments against it at the moment. And I'm too tired to argue. ;-)
Quoted 16 lines
Quoted 3 lines
Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return
-EBUSY.It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.
Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.
Quoted 4 lines
Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.
Quoted 5 lines
If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.
We also should preserve the requests in case the system sleep transition fails.
Quoted 5 lines
In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.
The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet.
Agreed.
Quoted 3 lines
For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.
Yes, for now we have to assume that any device with wakeup enabled is a wakeup device.
OK, I'll post the new version of the patch shortly. Please check if the barrier mechanism is implemeted and used correctly.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Alan SternQuoted 22 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 6 lines
Quoted 2 lines
How exactly would you like to implement it
instead?As described above. The barrier would be equivalent to
pm_runtime_get_noresumefollowed bypm_runtime_disableexcept that it wouldn't actually disable anything.OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.
Exactly. In other words, if the driver tries to carry out a resume while the barrier is running, the resume won't get lost. Whereas with the temporarily-disable approach, it _would_ get lost.
Quoted 4 lines
Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.
Quoted 4 lines
The workqueue will be frozen, so runtime PM helpers will run only if they are invoked more or less directly by the driver (i.e., through
pm_runtime_resume, ...). I think we should allow drivers to do what they want, especially between the "prepare" and "suspend" stages.
Well, I'm not sure if that's a good idea, but also I have no good techincal arguments against it at the moment. And I'm too tired to argue. ;-)
Quoted 16 lines
Quoted 3 lines
Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return
-EBUSY.It may. But then again, it may not -- it may depend on the runtime PM core to make sure that resume requests get forwarded appropriately.
Furthermore, if you disable runtime PM _before_ calling the prepare method, that leaves a window during which the driver has no reason to realize that anything unusual is going on.
Quoted 4 lines
Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.
Quoted 5 lines
If every device gets resumed at the end of a system sleep, even the ones that were runtime-suspended before the sleep began, then there's no reason to preserve requests in the workqueue. But if previously-suspended devices don't get resumed at the end of a system sleep, then we should allow requests to remain in the workqueue.
We also should preserve the requests in case the system sleep transition fails.
Quoted 5 lines
In the end, it's probably safer and easier just to leave the workqueue alone -- freeze and unfreeze it, but don't meddle with its contents.
The whole question of remote wakeup vs. runtime suspend vs. system sleep is complicated, and people haven't dealt with all the issues yet.
Agreed.
Quoted 3 lines
For instance, it seems quite likely that with some devices you would want to enable remote wakeup during runtime suspend but not during system sleep. We don't have any good way to do this.
Yes, for now we have to assume that any device with wakeup enabled is a wakeup device.
OK, I'll post the new version of the patch shortly. Please check if the barrier mechanism is implemeted and used correctly.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Alan SternQuoted 29 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 10 lines
The problem with this is that it calls
pm_runtime_disable()at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
Quoted 5 lines
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with
pm_runtime_disable. We should be able to use this for both probe and remove.Quoted 7 lines
Isn't it what's done in rev. 14?
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);is exactly a barrier like this.
It's not exactly the same because it disables runtime PM for a short time. A barrier never disables runtime PM.
Quoted 6 lines
Quoted 2 lines
How exactly would you like to implement it
instead?As described above. The barrier would be equivalent to
pm_runtime_get_noresumefollowed bypm_runtime_disableexcept that it wouldn't actually disable anything.
OK, I can do that, but the only difference between that and the above sequence of three calls will be the possibility to call resume helpers while the "barrier" is in progress.
Quoted 10 lines
Quoted 7 lines
Quoted 2 lines
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.
All right. Since it involves a race, the problem may not show up for a while.
Allowing runtime PM helpers to be run during system sleep transitions would be problematic IMHO, because the run-time PM 'states' are not well defined at that time. Consequently, the rules that the PM helpers follow do not really hold during system sleep transitions.
Also, in principle the device driver's ->suspend() routine (the non-runtime one), or even the ->prepare() callback, may notice that the remote wake-up has happened and put the device back into the full power state and return -EBUSY.
Still, we can allow runtime PM requests to be put into the workqueue during system sleep transitions, to be executed after the resume (or in case the suspend fails, that will make the action described in the previous paragraph somewhat easier). It seems we'd need a separate flag for it, though.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Alan SternQuoted 36 lines · Alan Stern
Quoted 16 lines · Rafael J. Wysocki
Quoted 4 lines
We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);before and
pm_runtime_put_noidle()Quoted 7 lines
after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our
pm_runtime_put_noidle()won't decrease the usage counter below zero.At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.
The problem with this is that it calls
pm_runtime_disable()at a time when the driver is still supposed to be in control of the device. Interfering with the driver's legitimate activity in this way is a bad thing to do.The difficulty here is that our requirements are a little contradictory. We want to prevent all runtime PM callbacks while the remove method is running, but we also want the remove method to be able to carry out its own runtime PM activities.
Quoted 5 lines
So maybe what we really need is more like a barrier. That is, something that will do a "get", wait for outstanding callbacks to finish, carry out a resume if one is pending, and cancel other pending requests. This could easily share code with
pm_runtime_disable. We should be able to use this for both probe and remove.
Isn't it what's done in rev. 14?
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);
is exactly a barrier like this. How exactly would you like to implement it instead?
Quoted 10 lines
We will also need to be a lot more careful about handling runtime PM during system sleep transitions. The current code runs a risk of losing remote wakeup requests. One scenario goes like this:
1. The device sends a wakeup request, probably in the form of an IRQ.
2. The driver fields the interrupt and tells the device to turn
off its interrupt request signal.
Quoted 8 lines
3. The driver calls
pm_request_resume.4. The runtime PM core carries out the resume callback.
If the core disables runtime PM before step 1 (and before we begin the "late" stage of a system sleep, so interrupts still get delivered) then steps 1 and 2 will succeed but step 3 will fail. The wakeup event will be lost.
The idea is that once the system sleep transition has started, the non-runtime callbacks are in charge of handling the device.
Quoted 2 lines
Perhaps this means we don't want to disable runtime PM during system sleep callbacks, but instead use the "barrier" scheme.
I'm not really sure about that. I'd rather do what's right now in the patch (well, that's why it's in there) until drivers and bus types start using the runtime PM framework. If it turns out to be problematic, we'll change it later.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Alan SternQuoted 9 lines · Alan Stern
Quoted 3 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your most recent comments.
Only two comments.
Quoted 8 lines
Quoted 2 lines
(quoted diff) +2
-0+static int __pm_request_idle(struct device *dev) +{...
drivers/base/power/runtime.c line 455
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+if (dev->power.request_pending) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_NONE )Replace != with ==.
Good catch, thanks!
Quoted 17 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +4
-0+ dev->power.request =RPM_REQ_IDLE ; + else if (dev->power.request != RPM_REQ_IDLE) + retval = -EAGAIN ; +return retval;drivers/base/dd.c +0
-0...
Quoted 8 lines
drivers/base/dd.c +2
-0@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);
Quoted 14 lines
drivers/base/dd.c static void __device_release_driver(stru · line 343
Quoted 9 lines
drivers/base/dd.c +2
-0if (dev->bus)@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Hmm. Perhaps we can do something along the lines of our .probe() handling. Namely, call
pm_runtime_disable(dev);pm_runtime_get_noresume(dev);pm_runtime_enable(dev);
before and
pm_runtime_put_noidle()
after? Then, if the driver's or bus type's .remove() needs to resume, it will be able to do that right away and if it wants to suspend, it can always call pm_runtime_put*(), because our pm_runtime_put_noidle() won't decrease the usage counter below zero.
At the same time we can avoid "leftover" suspends that could interfere with .remove() in case it needs to access the hardware.
Best,
Rafael
Re: [PATCH update x2] PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Rafael J. WysockiQuoted 3 lines · Rafael J. Wysocki
Hi,
The patch below should address all of your most recent comments.
Only two comments.
Quoted 2 lines
(quoted diff) +2
-0+static int __pm_request_idle(struct device *dev) +{
...
drivers/base/power/runtime.c line 455
Quoted 3 lines
drivers/base/power/runtime.c new file +3
-0+if (dev->power.request_pending) { +/* Any requests other then RPM_REQ_IDLE take precedence. */ +if (dev->power.request !=RPM_REQ_NONE )
Replace != with ==.
Quoted 8 lines
drivers/base/power/runtime.c new file +4
-0+ dev->power.request =RPM_REQ_IDLE ; + else if (dev->power.request != RPM_REQ_IDLE) + retval = -EAGAIN ; +return retval;drivers/base/dd.c +0
-0
...
Quoted 10 lines
drivers/base/dd.c +2
-0@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
drivers/base/dd.c static void __device_release_driver(stru · line 343
Quoted 7 lines
drivers/base/dd.c +2
-0@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
We may need to be more careful here. The driver's remove method may want to do some runtime PM stuff to the device before giving up control. On the other hand I'm not sure what _should_ be done here, so I can't suggest anything better.
Alan Stern
PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
In reply to Alan SternHi,
The patch below should address all of your most recent comments.
Best,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 13)
Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.
Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.
--- Documentation/power/runtime_pm.txt | 377 ++++++++++++++ drivers/base/dd.c | 15 drivers/base/power/Makefile | 1 drivers/base/power/main.c | 20 drivers/base/power/power.h | 31 - drivers/base/power/runtime.c | 944 +++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 101 +++ include/linux/pm_runtime.h | 115 ++++ kernel/power/Kconfig | 14 kernel/power/main.c | 17 10 files changed, 1624 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
@@ -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
@@ -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
@@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +944 -0
944 lines not on this page · raw message
include/linux/pm_runtime.h new file +115 -0
115 lines not on this page · raw message
drivers/base/power/main.c +19 -1
@@ -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 *devmutex_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 stmutex_unlock (&dpm_list_mtx);device_complete (dev, state); +pm_runtime_enable (dev);mutex_lock (&dpm_list_mtx); }
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up requested during system sleep transition. */ + error = -EBUSY ; +else + error =device_prepare (dev, state);mutex_lock (&dpm_list_mtx);if (error) { dev->power.status =DPM_ON ; +pm_runtime_enable (dev);if (error == -EAGAIN ) {put_device (dev); error =0 ;
drivers/base/dd.c +15 -0
@@ -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_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +/* +* Wait for run-time PM calls to complete and prevent new suspend calls +* until the probe is done. +*/ +pm_runtime_disable (dev); +pm_runtime_get_noresume (dev); +pm_runtime_enable (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev); +if (!ret) +pm_runtime_idle (dev);return ret; }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
drivers/base/power/power.h +24 -7
@@ -1,7 +1,14 @@ -static inline void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -} +#ifdef CONFIG_PM_RUNTIME + +extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev); + +#else /* !CONFIG_PM_RUNTIME */ + +static inline void pm_runtime_init (struct device *dev) {} +static inline void pm_runtime_remove (struct device *dev) {} + +#endif /* !CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP
@@ -16,23 +23,33 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } +extern void device_pm_init (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);extern void device_pm_move_after (struct device *,struct device *);extern void device_pm_move_last (struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_pm_init (struct device *dev) +{ +pm_runtime_init (dev); +} + +static inline void device_pm_remove (struct device *dev) +{ +pm_runtime_remove (dev); +}static inline void device_pm_add (struct device *dev) {} -static inline void device_pm_remove (struct device *dev) {}static inline void device_pm_move_before (struct device *deva,struct device *devb) {}static inline void device_pm_move_after (struct device *deva,struct device *devb) {}static inline void device_pm_move_last (struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM
Documentation/power/runtime_pm.txt new file +377 -0
377 lines not on this page · raw message
PATCH 1/1 PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
In reply to Rafael J. WysockiHi,
The patch below should address all of your recent comments.
Additionally I changed a few bits that I thought could turn out to be problematic at one point.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Introduce core framework for run-time PM of I/O devices (rev. 12)
Introduce a core framework for run-time power management of I/O devices. Add device run-time PM fields to 'struct dev_pm_info' and device run-time PM callbacks to 'struct dev_pm_ops'. Introduce a run-time PM workqueue and define some device run-time PM helper functions at the core level. Document all these things.
Special thanks to Alan Stern for his help with the design and multiple detailed reviews of the pereceding versions of this patch and to Magnus Damm for testing feedback.
--- Documentation/power/runtime_pm.txt | 382 +++++++++++++++ drivers/base/dd.c | 15 drivers/base/power/Makefile | 1 drivers/base/power/main.c | 20 drivers/base/power/power.h | 31 - drivers/base/power/runtime.c | 939 +++++++++++++++++++++++++++++++++++++ include/linux/pm.h | 102 +++- include/linux/pm_runtime.h | 115 ++++ kernel/power/Kconfig | 14 kernel/power/main.c | 17 10 files changed, 1625 insertions(+), 11 deletions(-)
kernel/power/Kconfig +14 -0
@@ -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
@@ -11,6 +11,7 @@#include <linux/kobject.h> #include <linux/string.h> #include <linux/resume-trace.h> +#include <linux/workqueue.h> #include "power.h"
@@ -217,8 +218,24 @@ static struct attribute_group attr_group .attrs = g, }; +#ifdef CONFIG_PM_RUNTIME +struct workqueue_struct *pm_wq; + +static int __init pm_start_workqueue (void ) +{ + pm_wq =create_freezeable_workqueue ("pm" ); + +return pm_wq ?0 : -ENOMEM ; +} +#else +static inline int pm_start_workqueue (void ) {return 0 ; } +#endif +static int __init pm_init (void ) { +int error =pm_start_workqueue (); +if (error) +return error; power_kobj =kobject_create_and_add ("power" ,NULL );if (!power_kobj)return -ENOMEM ;
include/linux/pm.h +99 -3
131 lines not on this page · raw message
drivers/base/power/Makefile +1 -0
@@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/runtime.c new file +939 -0
939 lines not on this page · raw message
include/linux/pm_runtime.h new file +115 -0
115 lines not on this page · raw message
drivers/base/power/main.c +19 -1
@@ -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 *devmutex_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 stmutex_unlock (&dpm_list_mtx);device_complete (dev, state); +pm_runtime_enable (dev);mutex_lock (&dpm_list_mtx); }
@@ -757,11 +770,16 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) +/* Wake-up during suspend. */ + error = -EBUSY ; +else + error =device_prepare (dev, state);mutex_lock (&dpm_list_mtx);if (error) { dev->power.status =DPM_ON ; +pm_runtime_enable (dev);if (error == -EAGAIN ) {put_device (dev); error =0 ;
drivers/base/dd.c +15 -0
@@ -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_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +/* +* Wait for run-time PM calls to complete and prevent new suspend calls +* until the probe is done. +*/ +pm_runtime_disable (dev); +pm_runtime_get_noresume (dev); +pm_runtime_enable (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev); +if (!ret) +pm_runtime_idle (dev);return ret; }
@@ -306,6 +317,8 @@ static void __device_release_driver(stru drv = dev->driver;if (drv) { +pm_runtime_disable (dev); +driver_sysfs_remove (dev);if (dev->bus)
@@ -324,6 +337,8 @@ static void __device_release_driver(strublocking_notifier_call_chain (&dev->bus->p->bus_notifier,BUS_NOTIFY_UNBOUND_DRIVER , dev); + +pm_runtime_enable (dev); } }
drivers/base/power/power.h +24 -7
@@ -1,7 +1,14 @@ -static inline void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -} +#ifdef CONFIG_PM_RUNTIME + +extern void pm_runtime_init (struct device *dev); +extern void pm_runtime_remove (struct device *dev); + +#else /* !CONFIG_PM_RUNTIME */ + +static inline void pm_runtime_init (struct device *dev) {} +static inline void pm_runtime_remove (struct device *dev) {} + +#endif /* !CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP
@@ -16,23 +23,33 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } +extern void device_pm_init (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);extern void device_pm_move_after (struct device *,struct device *);extern void device_pm_move_last (struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_pm_init (struct device *dev) +{ +pm_runtime_init (dev); +} + +static inline void device_pm_remove (struct device *dev) +{ +pm_runtime_remove (dev); +}static inline void device_pm_add (struct device *dev) {} -static inline void device_pm_remove (struct device *dev) {}static inline void device_pm_move_before (struct device *deva,struct device *devb) {}static inline void device_pm_move_after (struct device *deva,struct device *devb) {}static inline void device_pm_move_last (struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM
Documentation/power/runtime_pm.txt new file +382 -0
382 lines not on this page · raw message
Quoted 15 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 9 lines
drivers/base/power/runtime.c line 85
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+spin_unlock_irq (&dev->power.lock); + +if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) + dev->bus->pm->runtime_idle (dev); + +spin_lock_irq (&dev->power.lock);Small optimization: Put the spin_{un}lock_irq stuff inside the "if" statement, so it doesn't happen if the test fails.
Quoted 2 lines
Well, I don't think so. We need to take the lock here unconditionally, because the caller is going to unlock it.
Quoted 7 lines
No, I meant do this:
if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) { spin_unlock_irq(&dev->power.lock); dev->bus->pm->runtime_idle(dev); spin_lock_irq(&dev->power.lock); }
Ah, OK. That makes sense.
Quoted 2 lines
By the way, I don't know if anyone still pays attention to sparse-type annotations. If you do,
Well, I guess I should. :-)
Quoted 41 lines
you should add "
__releases(&dev->power.lock)"
and "__acquires(&dev->power.lock)" annotations to functions like this, which release the lock without first acquiring it, and then acquire the lock without releasing before returning.Quoted 3 lines
Quoted 1 line
The same thing can be done in other places.
I'm not really sure it can.
The other places aren't quite the same as this. I'll leave it to your discretion. :-)
Quoted 11 lines
Quoted 10 lines
drivers/base/power/runtime.c line 710
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+int __pm_runtime_set_status (struct device *dev,unsigned int status) +{ +struct device *parent = dev->parent; +unsigned long flags; +int error =0 ; + +if (status !=RPM_ACTIVE && status !=RPM_SUSPENDED ) +return -EINVAL ;This should go inside the spinlocked area.
Quoted 2 lines
Why? 'status' is a function argument, it doesn't need to be protected from concurrent modification.
Oops, my mistake. Never mind...
Quoted 11 lines
Quoted 10 lines
Quoted 2 lines
drivers/base/power/runtime.c new file +2
-0+int pm_runtime_disable (struct device *dev) +{...
drivers/base/power/runtime.c line 829
Quoted 5 lines
drivers/base/power/runtime.c new file +5
-0+if (dev->power.disable_depth++ >0 ) + goto out; + +if (dev->power.runtime_failure) + goto out;I don't see why this is needed.
Quoted 5 lines
Quoted 1 line
If
dev->power.runtime_failure, there's no need to do anything more.Don't you still want to deactivate the timer and kill any pending requests? True, they won't be able to do anything until the failure state is cleared, but even so...
OK, I'll drop that if ().
Quoted 20 lines
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/base/power/runtime.c new file +8
-0+void pm_runtime_remove (struct device *dev) +{ +pm_runtime_disable (dev); + +if (dev->power.runtime_status ==RPM_ACTIVE ) { +struct device *parent = dev->parent; + +/* Quoted 8 lines
drivers/base/power/runtime.c line 944
Quoted 6 lines
drivers/base/power/runtime.c new file +6
-0+* Change the status back to 'suspended' to match the initial +* status. +*/ +pm_runtime_set_suspended (dev); +if (parent && !parent->power.ignore_children) +pm_request_idle (parent);Shouldn't these last two lines be part of
__pm_runtime_set_status()?Quoted 2 lines
No. It is valid to call
__pm_runtime_set_status()when runtime PM is disabled for the device and I don't think we should kick the parent in such cases.
Quoted 6 lines
Ah, an interesting point. Suppose the device is in
RPM_ACTIVE, and then someone callspm_runtime_disablefollowed bypm_runtime_set_suspended. Then the device's status would change toRPM_SUSPENDEDand the parent's count of active children would be decremented, perhaps to 0. If the count does go to 0, why wouldn't you want to send out an idle notification for the parent?
OK, having reconsidered that, I think you're right, it should go into __pm_runtime_set_status().
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
drivers/base/power/main.c static int dpm_prepare(pm_message_t stat · line 775
Quoted 7 lines
drivers/base/power/main.c +2
-1@@ -757,11 +771,15 @@ static int dpm_prepare(pm_message_t stat dev->power.status =DPM_PREPARING ;mutex_unlock (&dpm_list_mtx); - error =device_prepare (dev, state); +if (pm_runtime_disable (dev) &&device_may_wakeup (dev)) + error = -EBUSY ;What's the reason for the
-EBUSYerror?
Quoted 8 lines
Quoted 3 lines
If this is a wake-up device and
pm_runtime_disable(dev)returned 1 (it can only return 1 or 0), which means there was a resume request pending for the device, suspend fails with-EBUSY(wake-up event during suspend).I see. That's a little obscure; a comment would help. Even something as simple as:
error =
-EBUSY; /* wake-up during suspend */
OK
Quoted 20 lines
Quoted 12 lines
Quoted 11 lines
drivers/base/dd.c int driver_probe_device(struct device_dr · line 208
Quoted 7 lines
drivers/base/dd.c +2
-0@@ -202,7 +203,9 @@ int driver_probe_device(struct device_drpr_debug ("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__,dev_name (dev), drv->name); +pm_runtime_get_noresume (dev); ret =really_probe (dev, drv); +pm_runtime_put_noidle (dev);This is bad because it won't wait if there's a runtime-PM call in progress. Also, we shouldn't use
put_noidlebecause it might subvert the driver's attempt to autosuspend.Quoted 1 line
I'm not sure how that's possible, but whatever.
Suppose the probe routine does:
pm_runtime_get_sync(dev); /* do some work */ pm_runtime_put_sync(dev);
Quoted 8 lines
There is a clear expectation that an idle notification will eventually be sent. But if the probe is surrounded by
pm_runtime_get_noresume(dev); ... probe ... pm_runtime_put_noidle(dev);then there won't be any idle notifications.
Ah, OK. So the point is that we should always idle-notify after .probe(), because that's what .probe() would most probably want to do. I guess that makes sense.
Quoted 11 lines
Quoted 10 lines
Quoted 7 lines
Documentation/power/runtime_pm.txt
Quoted 4 lines
Documentation/power/runtime_pm.txt new file +3
-0+2. Device Run-time PM Callbacks +In particular, it is recommended that ->runtime_suspend() return -EBUSY if +device_may_wakeup() returns 'false' for the device.What's the point of this? I don't understand -- we don't want to discourage people from suspending devices with wakeup enabled.
device_may_wakeup(dev)== false means wake-up is disabled for dev, so suspending it might not be a good idea.
Quoted 8 lines
Okay. This needs to be rephrased. For example,
In particular, if the driver requires remote wakeup capability for proper functioning and
device_may_wakeup()returns 'false' for the device, then ->runtime_suspend() should return-EBUSY.The point is that plenty of drivers can work perfectly well without remote wakeup, so they have no reason to check
device_may_wakeup().
OK
Thanks a lot again, I'll do my best to address the comments in the next version of the patch.
Best,
Rafael