|
@@ -158,7 +158,8 @@ rules:
|
|
|
to execute it, the other callbacks will not be executed for the same device.
|
|
|
|
|
|
* A request to execute ->runtime_resume() will cancel any pending or
|
|
|
- scheduled requests to execute the other callbacks for the same device.
|
|
|
+ scheduled requests to execute the other callbacks for the same device,
|
|
|
+ except for scheduled autosuspends.
|
|
|
|
|
|
3. Run-time PM Device Fields
|
|
|
|
|
@@ -166,7 +167,7 @@ The following device run-time PM fields are present in 'struct dev_pm_info', as
|
|
|
defined in include/linux/pm.h:
|
|
|
|
|
|
struct timer_list suspend_timer;
|
|
|
- - timer used for scheduling (delayed) suspend request
|
|
|
+ - timer used for scheduling (delayed) suspend and autosuspend requests
|
|
|
|
|
|
unsigned long timer_expires;
|
|
|
- timer expiration time, in jiffies (if this is different from zero, the
|
|
@@ -236,6 +237,23 @@ defined in include/linux/pm.h:
|
|
|
Section 8); it may be modified only by the pm_runtime_no_callbacks()
|
|
|
helper function
|
|
|
|
|
|
+ unsigned int use_autosuspend;
|
|
|
+ - indicates that the device's driver supports delayed autosuspend (see
|
|
|
+ Section 9); it may be modified only by the
|
|
|
+ pm_runtime{_dont}_use_autosuspend() helper functions
|
|
|
+
|
|
|
+ unsigned int timer_autosuspends;
|
|
|
+ - indicates that the PM core should attempt to carry out an autosuspend
|
|
|
+ when the timer expires rather than a normal suspend
|
|
|
+
|
|
|
+ int autosuspend_delay;
|
|
|
+ - the delay time (in milliseconds) to be used for autosuspend
|
|
|
+
|
|
|
+ unsigned long last_busy;
|
|
|
+ - the time (in jiffies) when the pm_runtime_mark_last_busy() helper
|
|
|
+ function was last called for this device; used in calculating inactivity
|
|
|
+ periods for autosuspend
|
|
|
+
|
|
|
All of the above fields are members of the 'power' member of 'struct device'.
|
|
|
|
|
|
4. Run-time PM Device Helper Functions
|
|
@@ -261,6 +279,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
|
|
error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt
|
|
|
to suspend the device again in future
|
|
|
|
|
|
+ int pm_runtime_autosuspend(struct device *dev);
|
|
|
+ - same as pm_runtime_suspend() except that the autosuspend delay is taken
|
|
|
+ into account; if pm_runtime_autosuspend_expiration() says the delay has
|
|
|
+ not yet expired then an autosuspend is scheduled for the appropriate time
|
|
|
+ and 0 is returned
|
|
|
+
|
|
|
int pm_runtime_resume(struct device *dev);
|
|
|
- execute the subsystem-level resume callback for the device; returns 0 on
|
|
|
success, 1 if the device's run-time PM status was already 'active' or
|
|
@@ -273,6 +297,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
|
|
device (the request is represented by a work item in pm_wq); returns 0 on
|
|
|
success or error code if the request has not been queued up
|
|
|
|
|
|
+ int pm_request_autosuspend(struct device *dev);
|
|
|
+ - schedule the execution of the subsystem-level suspend callback for the
|
|
|
+ device when the autosuspend delay has expired; if the delay has already
|
|
|
+ expired then the work item is queued up immediately
|
|
|
+
|
|
|
int pm_schedule_suspend(struct device *dev, unsigned int delay);
|
|
|
- schedule the execution of the subsystem-level suspend callback for the
|
|
|
device in future, where 'delay' is the time to wait before queuing up a
|
|
@@ -304,12 +333,20 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
|
|
- decrement the device's usage counter
|
|
|
|
|
|
int pm_runtime_put(struct device *dev);
|
|
|
- - decrement the device's usage counter, run pm_request_idle(dev) and return
|
|
|
- its result
|
|
|
+ - decrement the device's usage counter; if the result is 0 then run
|
|
|
+ pm_request_idle(dev) and return its result
|
|
|
+
|
|
|
+ int pm_runtime_put_autosuspend(struct device *dev);
|
|
|
+ - decrement the device's usage counter; if the result is 0 then run
|
|
|
+ pm_request_autosuspend(dev) and return its result
|
|
|
|
|
|
int pm_runtime_put_sync(struct device *dev);
|
|
|
- - decrement the device's usage counter, run pm_runtime_idle(dev) and return
|
|
|
- its result
|
|
|
+ - decrement the device's usage counter; if the result is 0 then run
|
|
|
+ pm_runtime_idle(dev) and return its result
|
|
|
+
|
|
|
+ int pm_runtime_put_sync_autosuspend(struct device *dev);
|
|
|
+ - decrement the device's usage counter; if the result is 0 then run
|
|
|
+ pm_runtime_autosuspend(dev) and return its result
|
|
|
|
|
|
void pm_runtime_enable(struct device *dev);
|
|
|
- enable the run-time PM helper functions to run the device bus type's
|
|
@@ -360,19 +397,46 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
|
|
PM attributes from /sys/devices/.../power (or prevent them from being
|
|
|
added when the device is registered)
|
|
|
|
|
|
+ void pm_runtime_mark_last_busy(struct device *dev);
|
|
|
+ - set the power.last_busy field to the current time
|
|
|
+
|
|
|
+ void pm_runtime_use_autosuspend(struct device *dev);
|
|
|
+ - set the power.use_autosuspend flag, enabling autosuspend delays
|
|
|
+
|
|
|
+ void pm_runtime_dont_use_autosuspend(struct device *dev);
|
|
|
+ - clear the power.use_autosuspend flag, disabling autosuspend delays
|
|
|
+
|
|
|
+ void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
|
|
|
+ - set the power.autosuspend_delay value to 'delay' (expressed in
|
|
|
+ milliseconds); if 'delay' is negative then run-time suspends are
|
|
|
+ prevented
|
|
|
+
|
|
|
+ unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
|
|
|
+ - calculate the time when the current autosuspend delay period will expire,
|
|
|
+ based on power.last_busy and power.autosuspend_delay; if the delay time
|
|
|
+ is 1000 ms or larger then the expiration time is rounded up to the
|
|
|
+ nearest second; returns 0 if the delay period has already expired or
|
|
|
+ power.use_autosuspend isn't set, otherwise returns the expiration time
|
|
|
+ in jiffies
|
|
|
+
|
|
|
It is safe to execute the following helper functions from interrupt context:
|
|
|
|
|
|
pm_request_idle()
|
|
|
+pm_request_autosuspend()
|
|
|
pm_schedule_suspend()
|
|
|
pm_request_resume()
|
|
|
pm_runtime_get_noresume()
|
|
|
pm_runtime_get()
|
|
|
pm_runtime_put_noidle()
|
|
|
pm_runtime_put()
|
|
|
+pm_runtime_put_autosuspend()
|
|
|
+pm_runtime_enable()
|
|
|
pm_suspend_ignore_children()
|
|
|
pm_runtime_set_active()
|
|
|
pm_runtime_set_suspended()
|
|
|
-pm_runtime_enable()
|
|
|
+pm_runtime_suspended()
|
|
|
+pm_runtime_mark_last_busy()
|
|
|
+pm_runtime_autosuspend_expiration()
|
|
|
|
|
|
5. Run-time PM Initialization, Device Probing and Removal
|
|
|
|
|
@@ -561,3 +625,115 @@ As a consequence, the PM core will never directly inform the device's subsystem
|
|
|
or driver about run-time power changes. Instead, the driver for the device's
|
|
|
parent must take responsibility for telling the device's driver when the
|
|
|
parent's power state changes.
|
|
|
+
|
|
|
+9. Autosuspend, or automatically-delayed suspends
|
|
|
+
|
|
|
+Changing a device's power state isn't free; it requires both time and energy.
|
|
|
+A device should be put in a low-power state only when there's some reason to
|
|
|
+think it will remain in that state for a substantial time. A common heuristic
|
|
|
+says that a device which hasn't been used for a while is liable to remain
|
|
|
+unused; following this advice, drivers should not allow devices to be suspended
|
|
|
+at run-time until they have been inactive for some minimum period. Even when
|
|
|
+the heuristic ends up being non-optimal, it will still prevent devices from
|
|
|
+"bouncing" too rapidly between low-power and full-power states.
|
|
|
+
|
|
|
+The term "autosuspend" is an historical remnant. It doesn't mean that the
|
|
|
+device is automatically suspended (the subsystem or driver still has to call
|
|
|
+the appropriate PM routines); rather it means that run-time suspends will
|
|
|
+automatically be delayed until the desired period of inactivity has elapsed.
|
|
|
+
|
|
|
+Inactivity is determined based on the power.last_busy field. Drivers should
|
|
|
+call pm_runtime_mark_last_busy() to update this field after carrying out I/O,
|
|
|
+typically just before calling pm_runtime_put_autosuspend(). The desired length
|
|
|
+of the inactivity period is a matter of policy. Subsystems can set this length
|
|
|
+initially by calling pm_runtime_set_autosuspend_delay(), but after device
|
|
|
+registration the length should be controlled by user space, using the
|
|
|
+/sys/devices/.../power/autosuspend_delay_ms attribute.
|
|
|
+
|
|
|
+In order to use autosuspend, subsystems or drivers must call
|
|
|
+pm_runtime_use_autosuspend() (preferably before registering the device), and
|
|
|
+thereafter they should use the various *_autosuspend() helper functions instead
|
|
|
+of the non-autosuspend counterparts:
|
|
|
+
|
|
|
+ Instead of: pm_runtime_suspend use: pm_runtime_autosuspend;
|
|
|
+ Instead of: pm_schedule_suspend use: pm_request_autosuspend;
|
|
|
+ Instead of: pm_runtime_put use: pm_runtime_put_autosuspend;
|
|
|
+ Instead of: pm_runtime_put_sync use: pm_runtime_put_sync_autosuspend.
|
|
|
+
|
|
|
+Drivers may also continue to use the non-autosuspend helper functions; they
|
|
|
+will behave normally, not taking the autosuspend delay into account.
|
|
|
+Similarly, if the power.use_autosuspend field isn't set then the autosuspend
|
|
|
+helper functions will behave just like the non-autosuspend counterparts.
|
|
|
+
|
|
|
+The implementation is well suited for asynchronous use in interrupt contexts.
|
|
|
+However such use inevitably involves races, because the PM core can't
|
|
|
+synchronize ->runtime_suspend() callbacks with the arrival of I/O requests.
|
|
|
+This synchronization must be handled by the driver, using its private lock.
|
|
|
+Here is a schematic pseudo-code example:
|
|
|
+
|
|
|
+ foo_read_or_write(struct foo_priv *foo, void *data)
|
|
|
+ {
|
|
|
+ lock(&foo->private_lock);
|
|
|
+ add_request_to_io_queue(foo, data);
|
|
|
+ if (foo->num_pending_requests++ == 0)
|
|
|
+ pm_runtime_get(&foo->dev);
|
|
|
+ if (!foo->is_suspended)
|
|
|
+ foo_process_next_request(foo);
|
|
|
+ unlock(&foo->private_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ foo_io_completion(struct foo_priv *foo, void *req)
|
|
|
+ {
|
|
|
+ lock(&foo->private_lock);
|
|
|
+ if (--foo->num_pending_requests == 0) {
|
|
|
+ pm_runtime_mark_last_busy(&foo->dev);
|
|
|
+ pm_runtime_put_autosuspend(&foo->dev);
|
|
|
+ } else {
|
|
|
+ foo_process_next_request(foo);
|
|
|
+ }
|
|
|
+ unlock(&foo->private_lock);
|
|
|
+ /* Send req result back to the user ... */
|
|
|
+ }
|
|
|
+
|
|
|
+ int foo_runtime_suspend(struct device *dev)
|
|
|
+ {
|
|
|
+ struct foo_priv foo = container_of(dev, ...);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ lock(&foo->private_lock);
|
|
|
+ if (foo->num_pending_requests > 0) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ } else {
|
|
|
+ /* ... suspend the device ... */
|
|
|
+ foo->is_suspended = 1;
|
|
|
+ }
|
|
|
+ unlock(&foo->private_lock);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ int foo_runtime_resume(struct device *dev)
|
|
|
+ {
|
|
|
+ struct foo_priv foo = container_of(dev, ...);
|
|
|
+
|
|
|
+ lock(&foo->private_lock);
|
|
|
+ /* ... resume the device ... */
|
|
|
+ foo->is_suspended = 0;
|
|
|
+ pm_runtime_mark_last_busy(&foo->dev);
|
|
|
+ if (foo->num_pending_requests > 0)
|
|
|
+ foo_process_requests(foo);
|
|
|
+ unlock(&foo->private_lock);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+The important point is that after foo_io_completion() asks for an autosuspend,
|
|
|
+the foo_runtime_suspend() callback may race with foo_read_or_write().
|
|
|
+Therefore foo_runtime_suspend() has to check whether there are any pending I/O
|
|
|
+requests (while holding the private lock) before allowing the suspend to
|
|
|
+proceed.
|
|
|
+
|
|
|
+In addition, the power.autosuspend_delay field can be changed by user space at
|
|
|
+any time. If a driver cares about this, it can call
|
|
|
+pm_runtime_autosuspend_expiration() from within the ->runtime_suspend()
|
|
|
+callback while holding its private lock. If the function returns a nonzero
|
|
|
+value then the delay has not yet expired and the callback should return
|
|
|
+-EAGAIN.
|