요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
==================================================
Runtime Power Management Framework for I/O Devices
==================================================
(C) 2009-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
(C) 2010 Alan Stern <stern@rowland.harvard.edu>
(C) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1. Introduction
===============
Support for runtime power management (runtime PM) of I/O devices is provided
at the power management core (PM core) level by means of:
* The power management workqueue pm_wq in which bus types and device drivers can
put their PM-related work items. It is strongly recommended that pm_wq be
used for queuing all work items related to runtime PM, because this allows
them to be synchronized with system-wide power transitions (suspend to RAM,
hibernation and resume from system sleep states). pm_wq is declared in
include/linux/pm_runtime.h and defined in kernel/power/main.c.
* A number of runtime PM fields in the 'power' member of 'struct device' (which
is of the type 'struct dev_pm_info', defined in include/linux/pm.h) that can
be used for synchronizing runtime PM operations with one another.
* Three device runtime PM callbacks in 'struct dev_pm_ops' (defined in
include/linux/pm.h).
* A set of helper functions defined in drivers/base/power/runtime.c that can be
used for carrying out runtime PM operations in such a way that the
synchronization between them is taken care of by the PM core. Bus types and
device drivers are encouraged to use these functions.
The runtime PM callbacks present in 'struct dev_pm_ops', the device runtime PM
fields of 'struct dev_pm_info' and the core helper functions provided for
runtime PM are described below.
2. Device Runtime PM Callbacks
==============================
There are three device runtime PM callbacks defined in 'struct dev_pm_ops'::
struct dev_pm_ops {
...
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
...
};
The ->runtime_suspend(), ->runtime_resume() and ->runtime_idle() callbacks
are executed by the PM core for the device's subsystem that may be either of
the following:
1. PM domain of the device, if the device's PM domain object, dev->pm_domain,
is present.
2. Device type of the device, if both dev->type and dev->type->pm are present.
3. Device class of the device, if both dev->class and dev->class->pm are
present.
4. Bus type of the device, if both dev->bus and dev->bus->pm are present.
If the subsystem chosen by applying the above rules doesn't provide the relevant
callback, the PM core will invoke the corresponding driver callback stored in
dev->driver->pm directly (if present).
The PM core always checks which callback to use in the order given above, so the
priority order of callbacks from high to low is: PM domain, device type, class
and bus type. Moreover, the high-priority one will always take precedence over
a low-priority one. The PM domain, bus type, device type and class callbacks
are referred to as subsystem-level callbacks in what follows.
By default, the callbacks are always invoked in process context with interrupts
enabled. However, the pm_runtime_irq_safe() helper function can be used to tell
the PM core that it is safe to run the ->runtime_suspend(), ->runtime_resume()
and ->runtime_idle() callbacks for the given device in atomic context with
interrupts disabled. This implies that the callback routines in question must
not block or sleep, but it also means that the synchronous helper functions
listed at the end of Section 4 may be used for that device within an interrupt
handler or generally in an atomic context.
The subsystem-level suspend callback, if present, is _entirely_ _responsible_
for handling the suspend of the device as appropriate, which may, but need not
include executing the device driver's own ->runtime_suspend() callback (from the
PM core's point of view it is not necessary to implement a ->runtime_suspend()
callback in a device driver as long as the subsystem-level suspend callback
knows what to do to handle the device).
* Once the subsystem-level suspend callback (or the driver suspend callback,
if invoked directly) has completed successfully for the given device, the PM
core regards the device as suspended, which need not mean that it has been
put into a low power state. It is supposed to mean, however, that the
device will not process data and will not communicate with the CPU(s) and
RAM until the appropriate resume callback is executed for it. The runtime
PM status of a device after successful execution of the suspend callback is
'suspended'.
* If the suspend callback returns -EBUSY or -EAGAIN, the device's runtime PM
status remains 'active', which means that the device _must_ be fully
operational afterwards.
* If the suspend callback returns an error code different from -EBUSY and
-EAGAIN, the PM core regards this as a fatal error and will refuse to run
the helper functions described in Section 4 for the device until its status
is directly set to either 'active', or 'suspended' (the PM core provides
special helper functions for this purpose).
In particular, if the driver requires remote wakeup capability (i.e. hardware
mechanism allowing the device to request a change of its power state, such as
PCI PME) for proper functioning and device_can_wakeup() returns 'false' for the
device, then ->runtime_suspend() should return -EBUSY. On the other hand, if
device_can_wakeup() returns 'true' for the device and the device is put into a
low-power state during the execution of the suspend callback, it is expected
that remote wakeup will be enabled for the device. Generally, remote wakeup
should be enabled for all input devices put into low-power states at run time.
The subsystem-level resume callback, if present, is **entirely responsible** for
handling the resume of the device as appropriate, which may, but need not
include executing the device driver's own ->runtime_resume() callback (from the
PM core's point of view it is not necessary to implement a ->runtime_resume()
callback in a device driver as long as the subsystem-level resume callback knows
what to do to handle the device).
* Once the subsystem-level resume callback (or the driver resume callback, if
invoked directly) has completed successfully, the PM core regards the device
as fully operational, which means that the device _must_ be able to complete
I/O operations as needed. The runtime PM status of the device is then
'active'.
* If the resume callback returns an error code, the PM core regards this as a
fatal error and will refuse to run the helper functions described in Section
4 for the device, until its status is directly set to either 'active', or
'suspended' (by means of special helper functions provided by the PM core
for this purpose).
The idle callback (a subsystem-level one, if present, or the driver one) is
executed by the PM core whenever the device appears to be idle, which is
indicated to the PM core by two counters, the device's usage counter and the
counter of 'active' children of the device.
* If any of these counters is decreased using a helper function provided by
the PM core and it turns out to be equal to zero, the other counter is
checked. If that counter also is equal to zero, the PM core executes the
idle callback with the device as its argument.
The action performed by the idle callback is totally dependent on the subsystem
(or driver) in question, but the expected and recommended action is to check
if the device can be suspended (i.e. if all of the conditions necessary for
suspending the device are satisfied) and to queue up a suspend request for the
device in that case. If there is no idle callback, or if the callback returns
0, then the PM core will attempt to carry out a runtime suspend of the device,
also respecting devices configured for autosuspend. In essence this means a
call to pm_runtime_autosuspend(). To prevent this (for example, if the callback
routine has started a delayed suspend), the routine must return a non-zero
value. Negative error return codes are ignored by the PM core.
The helper functions provided by the PM core, described in Section 4, guarantee
that the following constraints are met with respect to runtime PM callbacks for
one device:
(1) The callbacks are mutually exclusive (e.g. it is forbidden to execute
->runtime_suspend() in parallel with ->runtime_resume() or with another
instance of ->runtime_suspend() for the same device) with the exception that
->runtime_suspend() or ->runtime_resume() can be executed in parallel with
->runtime_idle() (although ->runtime_idle() will not be started while any
of the other callbacks is being executed for the same device).
(2) ->runtime_idle() and ->runtime_suspend() can only be executed for 'active'
devices (i.e. the PM core will only execute ->runtime_idle() or
->runtime_suspend() for the devices the runtime PM status of which is
'active').
(3) ->runtime_idle() and ->runtime_suspend() can only be executed for a device
the usage counter of which is equal to zero _and_ either the counter of
'active' children of which is equal to zero, or the 'power.ignore_children'
flag of which is set.
(4) ->runtime_resume() can only be executed for 'suspended' devices (i.e. the
PM core will only execute ->runtime_resume() for the devices the runtime
PM status of which is 'suspended').
Additionally, the helper functions provided by the PM core obey the following
rules:
* If ->runtime_suspend() is about to be executed or there's a pending request
to execute it, ->runtime_idle() will not be executed for the same device.
* A request to execute or to schedule the execution of ->runtime_suspend()
will cancel any pending requests to execute ->runtime_idle() for the same
device.
* If ->runtime_resume() is about to be executed or there's a pending request
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,
except for scheduled autosuspends.
3. Runtime PM Device Fields
===========================
The following device runtime 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 and autosuspend requests
`unsigned long timer_expires;`
- timer expiration time, in jiffies (if this is different from zero, the
timer is running and will expire at that time, otherwise the timer is not
running)
`struct work_struct work;`
- work structure used for queuing up requests (i.e. work items in pm_wq)
`wait_queue_head_t wait_queue;`
- wait queue used if any of the helper functions needs to wait for another
one to complete
`spinlock_t lock;`
- lock used for synchronization
`atomic_t usage_count;`
- the usage counter of the device
`atomic_t child_count;`
- the count of 'active' children of the device
`unsigned int ignore_children;`
- if set, the value of child_count is ignored (but still updated)
`unsigned int disable_depth;`
- used for disabling the helper functions (they work normally if this is
equal to zero); the initial value of it is 1 (i.e. runtime PM is
initially disabled for all devices)
`int runtime_error;`
- if set, there was a fatal error (one of the callbacks returned error code
as described in Section 2), so the helper functions will not work until
this flag is cleared; this is the error code returned by the failing
callback
`unsigned int idle_notification;`
- if set, ->runtime_idle() is being executed
`unsigned int request_pending;`
- if set, there's a pending request (i.e. a work item queued up into pm_wq)
`enum rpm_request request;`
- type of request that's pending (valid if request_pending is set)
`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 "start a resume as soon as you've suspended"
`enum rpm_status runtime_status;`
- the runtime PM status of the device; this field's initial value is
RPM_SUSPENDED, which means that each device is initially regarded by the
PM core as 'suspended', regardless of its real hardware status
`enum rpm_status last_status;`
- the last runtime PM status of the device captured before disabling runtime
PM for it (invalid initially and when disable_depth is 0)
`unsigned int runtime_auto;`
- if set, indicates that the user space has allowed the device driver to
power manage the device at run time via the /sys/devices/.../power/control
`interface;` it may only be modified with the help of the
pm_runtime_allow() and pm_runtime_forbid() helper functions
`unsigned int no_callbacks;`
- indicates that the device does not use the runtime PM callbacks (see
Section 8); it may be modified only by the pm_runtime_no_callbacks()
helper function
`unsigned int irq_safe;`
- indicates that the ->runtime_suspend() and ->runtime_resume() callbacks
will be invoked with the spinlock held and interrupts disabled
`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. Runtime PM Device Helper Functions
=====================================
The following runtime PM helper functions are defined in
drivers/base/power/runtime.c and include/linux/pm_runtime.h:
`void pm_runtime_init(struct device *dev);`
- initialize the device runtime PM fields in 'struct dev_pm_info'
`void pm_runtime_remove(struct device *dev);`
- make sure that the runtime PM of the device will be disabled after
removing the device from device hierarchy
`int pm_runtime_idle(struct device *dev);`
- execute the subsystem-level idle callback for the device; returns an
error code on failure, where -EINPROGRESS means that ->runtime_idle() is
already being executed; if there is no callback or the callback returns 0
then run pm_runtime_autosuspend(dev) and return its result
`int pm_runtime_suspend(struct device *dev);`
- execute the subsystem-level suspend callback for the device; returns 0 on
success, 1 if the device's runtime PM status was already 'suspended', or
error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt
to suspend the device again in future and -EACCES means that
'power.disable_depth' is different from 0
`int pm_runtime_autosuspend(struct device *dev);`
- same as pm_runtime_suspend() except that a call to
pm_runtime_mark_last_busy() is made and 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 runtime PM status is already 'active' (also if
'power.disable_depth' is nonzero, but the status was 'active' when it was
changing from 0 to 1) or error code on failure, where -EAGAIN means it may
be safe to attempt to resume the device again in future, but
'power.runtime_error' should be checked additionally, and -EACCES means
that the callback could not be run, because 'power.disable_depth' was
different from 0
`int pm_runtime_resume_and_get(struct device *dev);`
- run pm_runtime_resume(dev) and if successful, increment the device's
usage counter; returns 0 on success (whether or not the device's
runtime PM status was already 'active') or the error code from
pm_runtime_resume() on failure.
`int pm_request_idle(struct device *dev);`
- submit a request to execute the subsystem-level idle callback for the
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);`
- Call pm_runtime_mark_last_busy() and schedule the execution of the
subsystem-level suspend callback for the device when the autosuspend delay
expires
`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
suspend work item in pm_wq, in milliseconds (if 'delay' is zero, the work
item is queued up immediately); returns 0 on success, 1 if the device's PM
runtime status was already 'suspended', or error code if the request
hasn't been scheduled (or queued up if 'delay' is 0); if the execution of
->runtime_suspend() is already scheduled and not yet expired, the new
value of 'delay' will be used as the time to wait
`int pm_request_resume(struct device *dev);`
- submit a request to execute the subsystem-level resume callback for the
device (the request is represented by a work item in pm_wq); returns 0 on
success, 1 if the device's runtime PM status was already 'active', or
error code if the request hasn't been queued up
`void pm_runtime_get_noresume(struct device *dev);`
- increment the device's usage counter
`int pm_runtime_get(struct device *dev);`
- increment the device's usage counter, run pm_request_resume(dev) and
return its result
`int pm_runtime_get_sync(struct device *dev);`
- increment the device's usage counter, run pm_runtime_resume(dev) and
return its result;
note that it does not drop the device's usage counter on errors, so
consider using pm_runtime_resume_and_get() instead of it, especially
if its return value is checked by the caller, as this is likely to
result in cleaner code.
`int pm_runtime_get_if_in_use(struct device *dev);`
- return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
runtime PM status is RPM_ACTIVE and the runtime PM usage counter is
nonzero, increment the counter and return 1; otherwise return 0 without
changing the counter
`int pm_runtime_get_if_active(struct device *dev);`
- return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
runtime PM status is RPM_ACTIVE, increment the counter and
return 1; otherwise return 0 without changing the counter
`void pm_runtime_put_noidle(struct device *dev);`
- decrement the device's usage counter
`int pm_runtime_put(struct device *dev);`
- 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);`
- set the power.last_busy field to the current time and 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_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; if the result is 0 then run
pm_runtime_idle(dev) and return its result
`int pm_runtime_put_sync_suspend(struct device *dev);`
- decrement the device's usage counter; if the result is 0 then run
pm_runtime_suspend(dev) and return its result
`int pm_runtime_put_sync_autosuspend(struct device *dev);`
- set the power.last_busy field to the current time and 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);`
- decrement the device's 'power.disable_depth' field; if that field is equal
to zero, the runtime PM helper functions can execute subsystem-level
callbacks described in Section 2 for the device
`int pm_runtime_disable(struct device *dev);`
- increment the device's 'power.disable_depth' field (if the value of that
field was previously zero, this prevents subsystem-level runtime PM
callbacks from being run for the device), make sure that all of the
pending runtime PM operations on the device are either completed or
canceled; returns 1 if there was a resume request pending and it was
necessary to execute the subsystem-level resume callback for the device
to satisfy that request, otherwise 0 is returned
`int pm_runtime_barrier(struct device *dev);`
- check if there's a resume request pending for the device and resume it
(synchronously) in that case, cancel any other pending runtime PM requests
regarding it and wait for all runtime PM operations on it in progress to
complete; returns 1 if there was a resume request pending and it was
necessary to execute the subsystem-level resume callback for the device to
satisfy that request, otherwise 0 is returned
`void pm_suspend_ignore_children(struct device *dev, bool enable);`
- set/unset the power.ignore_children flag of the device
`int pm_runtime_set_active(struct device *dev);`
- clear the device's 'power.runtime_error' flag, set the device's runtime
PM status to 'active' and update its parent's counter of 'active'
children as appropriate (it is only valid to use this function if
'power.runtime_error' is set or 'power.disable_depth' is greater than
zero); it will fail and return error code if the device has a parent
which is not active and the 'power.ignore_children' flag of which is unset
`void pm_runtime_set_suspended(struct device *dev);`
- clear the device's 'power.runtime_error' flag, set the device's runtime
PM status to 'suspended' and update its parent's counter of 'active'
children as appropriate (it is only valid to use this function if
'power.runtime_error' is set or 'power.disable_depth' is greater than
zero)
`bool pm_runtime_active(struct device *dev);`
- return true if the device's runtime PM status is 'active' or its
'power.disable_depth' field is not equal to zero, or false otherwise
`bool pm_runtime_suspended(struct device *dev);`
- return true if the device's runtime PM status is 'suspended' and its
'power.disable_depth' field is equal to zero, or false otherwise
`bool pm_runtime_status_suspended(struct device *dev);`
- return true if the device's runtime PM status is 'suspended'
`void pm_runtime_allow(struct device *dev);`
- set the power.runtime_auto flag for the device and decrease its usage
counter (used by the /sys/devices/.../power/control interface to
effectively allow the device to be power managed at run time)
`void pm_runtime_forbid(struct device *dev);`
- unset the power.runtime_auto flag for the device and increase its usage
counter (used by the /sys/devices/.../power/control interface to
effectively prevent the device from being power managed at run time)
`void pm_runtime_no_callbacks(struct device *dev);`
- set the power.no_callbacks flag for the device and remove the runtime
PM attributes from /sys/devices/.../power (or prevent them from being
added when the device is registered)
`void pm_runtime_irq_safe(struct device *dev);`
- set the power.irq_safe flag for the device, causing the runtime-PM
callbacks to be invoked with interrupts off
`bool pm_runtime_is_irq_safe(struct device *dev);`
- return true if power.irq_safe flag was set for the device, causing
the runtime-PM callbacks to be invoked with interrupts off
`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; call
pm_runtime_get_sync if the flag was previously cleared and
power.autosuspend_delay is negative
`void pm_runtime_dont_use_autosuspend(struct device *dev);`
- clear the power.use_autosuspend flag, disabling autosuspend delays;
decrement the device's usage counter if the flag was previously set and
power.autosuspend_delay is negative; call pm_runtime_idle
`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 runtime suspends are
prevented; if power.use_autosuspend is set, pm_runtime_get_sync may be
called or the device's usage counter may be decremented and
pm_runtime_idle called depending on if power.autosuspend_delay is
changed to or from a negative value; if power.use_autosuspend is clear,
pm_runtime_idle is called
`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_put_autosuspend()
- pm_runtime_enable()
- pm_suspend_ignore_children()
- pm_runtime_set_active()
- pm_runtime_set_suspended()
- pm_runtime_suspended()
- pm_runtime_mark_last_busy()
- pm_runtime_autosuspend_expiration()
If pm_runtime_irq_safe() has been called for a device then the following helper
functions may also be used in interrupt context:
- pm_runtime_idle()
- pm_runtime_suspend()
- pm_runtime_autosuspend()
- pm_runtime_resume()
- pm_runtime_get_sync()
- pm_runtime_put_sync()
- pm_runtime_put_sync_suspend()
- pm_runtime_put_sync_autosuspend()
5. Runtime PM Initialization, Device Probing and Removal
========================================================
Initially, the runtime PM is disabled for all devices, which means that the
majority of the runtime PM helper functions described in Section 4 will return
-EAGAIN until pm_runtime_enable() is called for the device.
In addition to that, the initial runtime PM status of all devices is
'suspended', but it need not reflect the actual physical state of the device.
Thus, if the device is initially active (i.e. it is able to process I/O), its
runtime PM status must be changed to 'active', with the help of
pm_runtime_set_active(), before pm_runtime_enable() is called for the device.
However, if the device has a parent and the parent's runtime PM is enabled,
calling pm_runtime_set_active() for the device will affect the parent, unless
the parent's 'power.ignore_children' flag is set. Namely, in that case the
parent won't be able to suspend at run time, using the PM core's helper
functions, as long as the child's status is 'active', even if the child's
runtime PM is still disabled (i.e. pm_runtime_enable() hasn't been called for
the child yet or pm_runtime_disable() has been called for it). For this reason,
once pm_runtime_set_active() has been called for the device, pm_runtime_enable()
should be called for it too as soon as reasonably possible or its runtime PM
status should be changed back to 'suspended' with the help of
pm_runtime_set_suspended().
If the default initial runtime PM status of the device (i.e. 'suspended')
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 runtime PM has to be
enabled earlier by calling pm_runtime_enable().
Note, if the device may execute pm_runtime calls during the probe (such as
if it is registered with a subsystem that may call back in) then the
pm_runtime_get_sync() call paired with a pm_runtime_put() call will be
appropriate to ensure that the device is not put back to sleep during the
probe. This can happen with systems such as the network device layer.
It may be desirable to suspend the device once ->probe() has finished.
Therefore the driver core uses the asynchronous pm_request_idle() to submit a
request to execute the subsystem-level idle callback for the device at that
time. A driver that makes use of the runtime autosuspend feature may want to
update the last busy mark before returning from ->probe().
Moreover, the driver core prevents runtime PM callbacks from racing with the bus
notifier callback in __device_release_driver(), which is necessary because the
notifier is used by some subsystems to carry out operations affecting the
runtime PM functionality. It does so by calling pm_runtime_get_sync() before
driver_sysfs_remove() and the BUS_NOTIFY_UNBIND_DRIVER notifications. This
resumes the device if it's in the suspended state and prevents it from
being suspended again while those routines are being executed.
To allow bus types and drivers to put devices into the suspended state by
calling pm_runtime_suspend() from their ->remove() routines, the driver core
executes pm_runtime_put_sync() after running the BUS_NOTIFY_UNBIND_DRIVER
notifications in __device_release_driver(). This requires bus types and
drivers to make their ->remove() callbacks avoid races with runtime PM directly,
but it also allows more flexibility in the handling of devices during the
removal of their drivers.
Drivers in ->remove() callback should undo the runtime PM changes done
in ->probe(). Usually this means calling pm_runtime_disable(),
pm_runtime_dont_use_autosuspend() etc.
The user space can effectively disallow the driver of the device to power manage
it at run time by changing the value of its /sys/devices/.../power/control
attribute to "on", which causes pm_runtime_forbid() to be called. In principle,
this mechanism may also be used by the driver to effectively turn off the
runtime power management of the device until the user space turns it on.
Namely, during the initialization the driver can make sure that the runtime PM
status of the device is 'active' and call pm_runtime_forbid(). It should be
noted, however, that if the user space has already intentionally changed the
value of /sys/devices/.../power/control to "auto" to allow the driver to power
manage the device at run time, the driver may confuse it by using
pm_runtime_forbid() this way.
6. Runtime PM and System Sleep
==============================
Runtime PM and system sleep (i.e., system suspend and hibernation, also known
as suspend-to-RAM and suspend-to-disk) interact with each other in a couple of
ways. If a device is active when a system sleep starts, everything is
straightforward. But what should happen if the device is already suspended?
The device may have different wake-up settings for runtime PM and system sleep.
For example, remote wake-up may be enabled for runtime suspend but disallowed
for system sleep (device_may_wakeup(dev) returns 'false'). When this happens,
the subsystem-level system suspend callback is responsible for changing the
device's wake-up setting (it may leave that to the device driver's system
suspend routine). It may be necessary to resume the device and suspend it again
in order to do so. The same is true if the driver uses different power levels
or other settings for runtime suspend and system sleep.
During system resume, the simplest approach is to bring all devices back to full
power, even if they had been suspended before the system suspend began. There
are several reasons for this, including:
* The device might need to switch power levels, wake-up settings, etc.
* Remote wake-up events might have been lost by the firmware.
* The device's children may need the device to be at full power in order
to resume themselves.
* The driver's idea of the device state may not agree with the device's
physical state. This can happen during resume from hibernation.
* The device might need to be reset.
* Even though the device was suspended, if its usage counter was > 0 then most
likely it would need a runtime resume in the near future anyway.
If the device had been suspended before the system suspend began and it's
brought back to full power during resume, then its runtime PM status will have
to be updated to reflect the actual post-system sleep status. The way to do
this is:
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
The PM core always increments the runtime usage counter before calling the
->suspend() callback and decrements it after calling the ->resume() callback.
Hence disabling runtime PM temporarily like this will not cause any runtime
suspend attempts to be permanently lost. If the usage count goes to zero
following the return of the ->resume() callback, the ->runtime_idle() callback
will be invoked as usual.
On some systems, however, system sleep is not entered through a global firmware
or hardware operation. Instead, all hardware components are put into low-power
states directly by the kernel in a coordinated way. Then, the system sleep
state effectively follows from the states the hardware components end up in
and the system is woken up from that state by a hardware interrupt or a similar
mechanism entirely under the kernel's control. As a result, the kernel never
gives control away and the states of all devices during resume are precisely
known to it. If that is the case and none of the situations listed above takes
place (in particular, if the system is not waking up from hibernation), it may
be more efficient to leave the devices that had been suspended before the system
suspend began in the suspended state.
To this end, the PM core provides a mechanism allowing some coordination between
different levels of device hierarchy. Namely, if a system suspend .prepare()
callback returns a positive number for a device, that indicates to the PM core
that the device appears to be runtime-suspended and its state is fine, so it
may be left in runtime suspend provided that all of its descendants are also
left in runtime suspend. If that happens, the PM core will not execute any
system suspend and resume callbacks for all of those devices, except for the
.complete() callback, which is then entirely responsible for handling the device
as appropriate. This only applies to system suspend transitions that are not
related to hibernation (see Documentation/driver-api/pm/devices.rst for more
information).
The PM core does its best to reduce the probability of race conditions between
the runtime PM and system suspend/resume (and hibernation) callbacks by carrying
out the following operations:
* During system suspend pm_runtime_get_noresume() is called for every device
right before executing the subsystem-level .prepare() callback for it and
pm_runtime_barrier() is called for every device right before executing the
subsystem-level .suspend() callback for it. In addition to that the PM core
calls __pm_runtime_disable() with 'false' as the second argument for every
device right before executing the subsystem-level .suspend_late() callback
for it.
* During system resume pm_runtime_enable() and pm_runtime_put() are called for
every device right after executing the subsystem-level .resume_early()
callback and right after executing the subsystem-level .complete() callback
for it, respectively.
7. Generic subsystem callbacks
==============================
Subsystems may wish to conserve code space by using the set of generic power
management callbacks provided by the PM core, defined in
driver/base/power/generic_ops.c:
`int pm_generic_runtime_suspend(struct device *dev);`
- invoke the ->runtime_suspend() callback provided by the driver of this
device and return its result, or return 0 if not defined
`int pm_generic_runtime_resume(struct device *dev);`
- invoke the ->runtime_resume() callback provided by the driver of this
device and return its result, or return 0 if not defined
`int pm_generic_suspend(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->suspend()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_suspend_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_resume(struct device *dev);`
- invoke the ->resume() callback provided by the driver of this device and,
if successful, change the device's runtime PM status to 'active'
`int pm_generic_resume_noirq(struct device *dev);`
- invoke the ->resume_noirq() callback provided by the driver of this device
`int pm_generic_freeze(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->freeze()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_freeze_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_thaw(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->thaw()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_thaw_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_poweroff(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->poweroff()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_poweroff_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_restore(struct device *dev);`
- invoke the ->restore() callback provided by the driver of this device and,
if successful, change the device's runtime PM status to 'active'
`int pm_generic_restore_noirq(struct device *dev);`
- invoke the ->restore_noirq() callback provided by the device's driver
These functions are the defaults used by the PM core if a subsystem doesn't
provide its own callbacks for ->runtime_idle(), ->runtime_suspend(),
->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(),
->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(),
->poweroff(), ->poweroff_noirq(), ->restore(), ->restore_noirq() in the
subsystem-level dev_pm_ops structure.
Device drivers that wish to use the same function as a system suspend, freeze,
poweroff and runtime suspend callback, and similarly for system resume, thaw,
restore, and runtime resume, can achieve similar behaviour with the help of the
DEFINE_RUNTIME_DEV_PM_OPS() defined in include/linux/pm_runtime.h (possibly setting its
last argument to NULL).
8. "No-Callback" Devices
========================
Some "devices" are only logical sub-devices of their parent and cannot be
power-managed on their own. (The prototype example is a USB interface. Entire
USB devices can go into low-power mode or send wake-up requests, but neither is
possible for individual interfaces.) The drivers for these devices have no
need of runtime PM callbacks; if the callbacks did exist, ->runtime_suspend()
and ->runtime_resume() would always return 0 without doing anything else and
->runtime_idle() would always call pm_runtime_suspend().
Subsystems can tell the PM core about these devices by calling
pm_runtime_no_callbacks(). This should be done after the device structure is
initialized and before it is registered (although after device registration is
also okay). The routine will set the device's power.no_callbacks flag and
prevent the non-debugging runtime PM sysfs attributes from being created.
When power.no_callbacks is set, the PM core will not invoke the
->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks.
Instead it will assume that suspends and resumes always succeed and that idle
devices should be suspended.
As a consequence, the PM core will never directly inform the device's subsystem
or driver about runtime 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.
Note that, in some cases it may not be desirable for subsystems/drivers to call
pm_runtime_no_callbacks() for their devices. This could be because a subset of
the runtime PM callbacks needs to be implemented, a platform dependent PM
domain could get attached to the device or that the device is power managed
through a supplier device link. For these reasons and to avoid boilerplate code
in subsystems/drivers, the PM core allows runtime PM callbacks to be
unassigned. More precisely, if a callback pointer is NULL, the PM core will act
as though there was a callback and it returned 0.
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 runtime 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 runtime suspends will
automatically be delayed until the desired period of inactivity has elapsed.
Inactivity is determined based on the power.last_busy field. 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, which means sometimes taking the autosuspend delay into
account (see pm_runtime_idle). The autosuspend variants of the functions also
call pm_runtime_mark_last_busy().
Under some circumstances a driver or subsystem may want to prevent a device
from autosuspending immediately, even though the usage counter is zero and the
autosuspend delay time has expired. If the ->runtime_suspend() callback
returns -EAGAIN or -EBUSY, and if the next autosuspend delay expiration time is
in the future (as it normally would be if the callback invoked
pm_runtime_mark_last_busy()), the PM core will automatically reschedule the
autosuspend. The ->runtime_suspend() callback can't do this rescheduling
itself because no suspend requests of any kind are accepted while the device is
suspending (i.e., while the callback is running).
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_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_next_request(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.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Runtime PM framework 개요
1-39이 문서는 I/O device의 runtime power management(runtime PM)를 PM core 수준에서 제공하는 구조를 설명합니다. 저작권 표기는 Rafael J. Wysocki, Alan Stern, Intel Corp. 및 Novell Inc.에 속합니다.
Runtime PM은 네 가지 기반을 사용합니다. 첫째, bus type과 device driver가 PM 관련 작업을 넣는 `pm_wq` workqueue입니다. 모든 runtime PM work item을 `pm_wq`에 넣으면 suspend-to-RAM, hibernation, system resume 같은 system-wide power transition과 동기화할 수 있습니다. 선언은 `include/linux/pm_runtime.h`, 정의는 `kernel/power/main.c`에 있습니다.
둘째, `struct device`의 `power` 멤버인 `struct dev_pm_info` 안에 runtime PM 동기화 필드가 있습니다. 셋째, `struct dev_pm_ops`에 세 개의 runtime PM callback이 있습니다. 넷째, `drivers/base/power/runtime.c`와 `include/linux/pm_runtime.h`의 helper가 PM core의 동기화를 거쳐 runtime PM operation을 수행합니다. Bus type과 driver는 이 helper를 사용하는 것이 권장됩니다.
Callback 실행과 상태 변경은 PM core의 공통 동기화 계층을 통과합니다.
==================================================
Runtime Power Management Framework for I/O Devices
==================================================
(C) 2009-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
(C) 2010 Alan Stern <stern@rowland.harvard.edu>
(C) 2014 Intel Corp., Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1. Introduction
===============
Support for runtime power management (runtime PM) of I/O devices is provided
at the power management core (PM core) level by means of:
* The power management workqueue pm_wq in which bus types and device drivers can
put their PM-related work items. It is strongly recommended that pm_wq be
used for queuing all work items related to runtime PM, because this allows
them to be synchronized with system-wide power transitions (suspend to RAM,
hibernation and resume from system sleep states). pm_wq is declared in
include/linux/pm_runtime.h and defined in kernel/power/main.c.
* A number of runtime PM fields in the 'power' member of 'struct device' (which
is of the type 'struct dev_pm_info', defined in include/linux/pm.h) that can
be used for synchronizing runtime PM operations with one another.
* Three device runtime PM callbacks in 'struct dev_pm_ops' (defined in
include/linux/pm.h).
* A set of helper functions defined in drivers/base/power/runtime.c that can be
used for carrying out runtime PM operations in such a way that the
synchronization between them is taken care of by the PM core. Bus types and
device drivers are encouraged to use these functions.
The runtime PM callbacks present in 'struct dev_pm_ops', the device runtime PM
fields of 'struct dev_pm_info' and the core helper functions provided for
runtime PM are described below.
Callback 종류, 우선순위와 실행 문맥
40-85`struct dev_pm_ops`의 runtime callback은 `->runtime_suspend()`, `->runtime_resume()`, `->runtime_idle()`입니다. PM core는 device의 subsystem callback을 먼저 고르고, 해당 callback이 없을 때만 `dev->driver->pm`의 driver callback을 직접 호출합니다.
선택 우선순위는 `dev->pm_domain`, `dev->type->pm`, `dev->class->pm`, `dev->bus->pm` 순서입니다. 높은 우선순위 callback이 낮은 우선순위를 항상 대체하며, 이 네 계층의 callback을 이후 설명에서는 subsystem-level callback이라고 부릅니다.
기본적으로 callback은 interrupt가 enable된 process context에서 실행됩니다. `pm_runtime_irq_safe()`를 호출하면 세 callback을 interrupt가 disable된 atomic context에서 실행해도 안전하다고 PM core에 알립니다. 이 경우 callback은 block하거나 sleep하면 안 되지만, 4절 끝에 열거된 synchronous helper를 interrupt handler나 atomic context에서 사용할 수 있습니다.
위에서 먼저 발견된 subsystem callback이 driver callback보다 우선합니다.
irq-safe 선언 여부에 따라 callback이 지켜야 할 제약이 달라집니다.
2. Device Runtime PM Callbacks
==============================
There are three device runtime PM callbacks defined in 'struct dev_pm_ops'::
struct dev_pm_ops {
...
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
...
};
The ->runtime_suspend(), ->runtime_resume() and ->runtime_idle() callbacks
are executed by the PM core for the device's subsystem that may be either of
the following:
1. PM domain of the device, if the device's PM domain object, dev->pm_domain,
is present.
2. Device type of the device, if both dev->type and dev->type->pm are present.
3. Device class of the device, if both dev->class and dev->class->pm are
present.
4. Bus type of the device, if both dev->bus and dev->bus->pm are present.
If the subsystem chosen by applying the above rules doesn't provide the relevant
callback, the PM core will invoke the corresponding driver callback stored in
dev->driver->pm directly (if present).
The PM core always checks which callback to use in the order given above, so the
priority order of callbacks from high to low is: PM domain, device type, class
and bus type. Moreover, the high-priority one will always take precedence over
a low-priority one. The PM domain, bus type, device type and class callbacks
are referred to as subsystem-level callbacks in what follows.
By default, the callbacks are always invoked in process context with interrupts
enabled. However, the pm_runtime_irq_safe() helper function can be used to tell
the PM core that it is safe to run the ->runtime_suspend(), ->runtime_resume()
and ->runtime_idle() callbacks for the given device in atomic context with
interrupts disabled. This implies that the callback routines in question must
not block or sleep, but it also means that the synchronous helper functions
listed at the end of Section 4 may be used for that device within an interrupt
handler or generally in an atomic context.
Suspend callback의 책임과 반환값
86-120Subsystem-level suspend callback이 존재하면 device suspend 처리를 전적으로 책임집니다. 필요한 경우 driver의 `->runtime_suspend()`를 호출할 수 있지만 의무는 아닙니다. Subsystem callback이 device를 올바르게 처리할 수 있다면 driver callback 자체가 없어도 됩니다.
Callback이 성공하면 PM core는 device를 `suspended`로 간주합니다. 반드시 물리적인 저전력 상태라는 뜻은 아니지만, 대응하는 resume callback이 실행될 때까지 device가 데이터를 처리하거나 CPU 및 RAM과 통신해서는 안 됩니다.
`-EBUSY` 또는 `-EAGAIN`을 반환하면 runtime PM 상태는 `active`로 남고 device는 완전히 동작 가능해야 합니다. 이 둘이 아닌 오류는 치명 오류로 취급되어 `runtime_error`가 설정되며, 특별한 helper로 상태를 직접 `active` 또는 `suspended`로 고칠 때까지 4절 helper 실행이 거부됩니다.
정상 동작에 PCI PME 같은 remote wakeup이 필요한데 `device_can_wakeup()`이 false이면 `->runtime_suspend()`는 `-EBUSY`를 반환해야 합니다. true인 device를 저전력 상태로 넣을 때는 remote wakeup을 enable해야 하며, runtime에 저전력 상태로 들어간 input device는 일반적으로 모두 remote wakeup을 enable해야 합니다.
오류 종류에 따라 이후 helper 사용 가능 여부가 달라집니다.
The subsystem-level suspend callback, if present, is _entirely_ _responsible_
for handling the suspend of the device as appropriate, which may, but need not
include executing the device driver's own ->runtime_suspend() callback (from the
PM core's point of view it is not necessary to implement a ->runtime_suspend()
callback in a device driver as long as the subsystem-level suspend callback
knows what to do to handle the device).
* Once the subsystem-level suspend callback (or the driver suspend callback,
if invoked directly) has completed successfully for the given device, the PM
core regards the device as suspended, which need not mean that it has been
put into a low power state. It is supposed to mean, however, that the
device will not process data and will not communicate with the CPU(s) and
RAM until the appropriate resume callback is executed for it. The runtime
PM status of a device after successful execution of the suspend callback is
'suspended'.
* If the suspend callback returns -EBUSY or -EAGAIN, the device's runtime PM
status remains 'active', which means that the device _must_ be fully
operational afterwards.
* If the suspend callback returns an error code different from -EBUSY and
-EAGAIN, the PM core regards this as a fatal error and will refuse to run
the helper functions described in Section 4 for the device until its status
is directly set to either 'active', or 'suspended' (the PM core provides
special helper functions for this purpose).
In particular, if the driver requires remote wakeup capability (i.e. hardware
mechanism allowing the device to request a change of its power state, such as
PCI PME) for proper functioning and device_can_wakeup() returns 'false' for the
device, then ->runtime_suspend() should return -EBUSY. On the other hand, if
device_can_wakeup() returns 'true' for the device and the device is put into a
low-power state during the execution of the suspend callback, it is expected
that remote wakeup will be enabled for the device. Generally, remote wakeup
should be enabled for all input devices put into low-power states at run time.
Resume과 idle callback
121-159Subsystem-level resume callback 역시 device resume 처리를 전적으로 책임지며, 필요에 따라 driver의 `->runtime_resume()`를 호출할 수 있습니다. 성공하면 device는 필요한 I/O를 완수할 수 있는 완전 동작 상태여야 하고 runtime PM 상태는 `active`가 됩니다.
Resume callback의 오류는 치명 오류입니다. 특별한 PM core helper로 상태를 `active` 또는 `suspended`로 직접 설정하기 전까지 4절 helper 실행이 거부됩니다.
PM core는 usage counter와 active child counter로 idle 여부를 판단합니다. Core helper로 한 counter를 감소시킨 결과가 0이면 다른 counter도 검사하고, 둘 다 0이면 subsystem-level callback 또는 driver `->runtime_idle()`을 실행합니다.
Idle callback은 suspend 조건을 확인한 뒤 가능하면 suspend request를 queue하는 것이 권장됩니다. Callback이 없거나 0을 반환하면 PM core가 autosuspend 설정을 존중해 `pm_runtime_autosuspend()`와 같은 runtime suspend를 시도합니다. Callback이 delayed suspend를 이미 시작하는 등 이를 막으려면 0이 아닌 값을 반환해야 하며, 음수 오류 코드는 PM core가 무시합니다.
두 counter가 모두 0일 때 idle callback이 suspend 가능성을 판정합니다.
The subsystem-level resume callback, if present, is **entirely responsible** for
handling the resume of the device as appropriate, which may, but need not
include executing the device driver's own ->runtime_resume() callback (from the
PM core's point of view it is not necessary to implement a ->runtime_resume()
callback in a device driver as long as the subsystem-level resume callback knows
what to do to handle the device).
* Once the subsystem-level resume callback (or the driver resume callback, if
invoked directly) has completed successfully, the PM core regards the device
as fully operational, which means that the device _must_ be able to complete
I/O operations as needed. The runtime PM status of the device is then
'active'.
* If the resume callback returns an error code, the PM core regards this as a
fatal error and will refuse to run the helper functions described in Section
4 for the device, until its status is directly set to either 'active', or
'suspended' (by means of special helper functions provided by the PM core
for this purpose).
The idle callback (a subsystem-level one, if present, or the driver one) is
executed by the PM core whenever the device appears to be idle, which is
indicated to the PM core by two counters, the device's usage counter and the
counter of 'active' children of the device.
* If any of these counters is decreased using a helper function provided by
the PM core and it turns out to be equal to zero, the other counter is
checked. If that counter also is equal to zero, the PM core executes the
idle callback with the device as its argument.
The action performed by the idle callback is totally dependent on the subsystem
(or driver) in question, but the expected and recommended action is to check
if the device can be suspended (i.e. if all of the conditions necessary for
suspending the device are satisfied) and to queue up a suspend request for the
device in that case. If there is no idle callback, or if the callback returns
0, then the PM core will attempt to carry out a runtime suspend of the device,
also respecting devices configured for autosuspend. In essence this means a
call to pm_runtime_autosuspend(). To prevent this (for example, if the callback
routine has started a delayed suspend), the routine must return a non-zero
value. Negative error return codes are ignored by the PM core.
Callback 동시성과 요청 취소 규칙
160-202PM core helper는 한 device에 대한 callback을 상호 배타적으로 실행합니다. 같은 device의 suspend와 resume, 또는 suspend 두 인스턴스는 병렬 실행할 수 없습니다. 예외적으로 suspend나 resume은 이미 실행 중인 idle과 병렬일 수 있지만, suspend 또는 resume 실행 중에는 새 idle을 시작하지 않습니다.
`->runtime_idle()`과 `->runtime_suspend()`는 runtime 상태가 `active`이고 usage counter가 0이며, active child counter도 0이거나 `power.ignore_children`이 설정된 device에만 실행됩니다. `->runtime_resume()`은 `suspended` device에만 실행됩니다.
Suspend 실행 또는 pending suspend request가 있으면 idle을 실행하지 않습니다. Suspend 실행·예약 요청은 pending idle request를 취소합니다. Resume 실행 또는 pending resume request가 있으면 다른 callback은 실행하지 않으며, resume 요청은 scheduled autosuspend를 제외한 다른 pending 또는 scheduled callback request를 취소합니다.
Runtime 상태와 두 counter가 허용 조건을 결정합니다.
Resume이 가장 강하게 다른 요청을 밀어내고, suspend는 idle을 밀어냅니다.
The helper functions provided by the PM core, described in Section 4, guarantee
that the following constraints are met with respect to runtime PM callbacks for
one device:
(1) The callbacks are mutually exclusive (e.g. it is forbidden to execute
->runtime_suspend() in parallel with ->runtime_resume() or with another
instance of ->runtime_suspend() for the same device) with the exception that
->runtime_suspend() or ->runtime_resume() can be executed in parallel with
->runtime_idle() (although ->runtime_idle() will not be started while any
of the other callbacks is being executed for the same device).
(2) ->runtime_idle() and ->runtime_suspend() can only be executed for 'active'
devices (i.e. the PM core will only execute ->runtime_idle() or
->runtime_suspend() for the devices the runtime PM status of which is
'active').
(3) ->runtime_idle() and ->runtime_suspend() can only be executed for a device
the usage counter of which is equal to zero _and_ either the counter of
'active' children of which is equal to zero, or the 'power.ignore_children'
flag of which is set.
(4) ->runtime_resume() can only be executed for 'suspended' devices (i.e. the
PM core will only execute ->runtime_resume() for the devices the runtime
PM status of which is 'suspended').
Additionally, the helper functions provided by the PM core obey the following
rules:
* If ->runtime_suspend() is about to be executed or there's a pending request
to execute it, ->runtime_idle() will not be executed for the same device.
* A request to execute or to schedule the execution of ->runtime_suspend()
will cancel any pending requests to execute ->runtime_idle() for the same
device.
* If ->runtime_resume() is about to be executed or there's a pending request
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,
except for scheduled autosuspends.
Timer, work, counter와 오류 필드
203-245`include/linux/pm.h`의 `struct dev_pm_info`에는 다음 device runtime PM 필드가 있습니다. 이 필드는 모두 `struct device`의 `power` 멤버에 속합니다.
Timer와 workqueue 상태, 동기화, 사용 관계와 오류를 device별로 보관합니다.
3. Runtime PM Device Fields
===========================
The following device runtime 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 and autosuspend requests
`unsigned long timer_expires;`
- timer expiration time, in jiffies (if this is different from zero, the
timer is running and will expire at that time, otherwise the timer is not
running)
`struct work_struct work;`
- work structure used for queuing up requests (i.e. work items in pm_wq)
`wait_queue_head_t wait_queue;`
- wait queue used if any of the helper functions needs to wait for another
one to complete
`spinlock_t lock;`
- lock used for synchronization
`atomic_t usage_count;`
- the usage counter of the device
`atomic_t child_count;`
- the count of 'active' children of the device
`unsigned int ignore_children;`
- if set, the value of child_count is ignored (but still updated)
`unsigned int disable_depth;`
- used for disabling the helper functions (they work normally if this is
equal to zero); the initial value of it is 1 (i.e. runtime PM is
initially disabled for all devices)
`int runtime_error;`
- if set, there was a fatal error (one of the callbacks returned error code
as described in Section 2), so the helper functions will not work until
this flag is cleared; this is the error code returned by the failing
callback
Request, 상태, policy와 autosuspend 필드
246-303나머지 `struct dev_pm_info` 필드는 실행 중인 idle, pending request, runtime status, 사용자 policy, irq-safe 및 autosuspend 상태를 나타냅니다. `runtime_status`의 초기값은 실제 hardware 상태와 무관하게 `RPM_SUSPENDED`입니다.
Pending operation과 상태 전환, userspace policy 및 autosuspend 계산 정보를 보관합니다.
`runtime_auto`는 `pm_runtime_allow()`와 `pm_runtime_forbid()`로만, `no_callbacks`는 `pm_runtime_no_callbacks()`로만, `use_autosuspend`는 `pm_runtime_use_autosuspend()`와 `pm_runtime_dont_use_autosuspend()`로만 변경해야 합니다.
`unsigned int idle_notification;`
- if set, ->runtime_idle() is being executed
`unsigned int request_pending;`
- if set, there's a pending request (i.e. a work item queued up into pm_wq)
`enum rpm_request request;`
- type of request that's pending (valid if request_pending is set)
`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 "start a resume as soon as you've suspended"
`enum rpm_status runtime_status;`
- the runtime PM status of the device; this field's initial value is
RPM_SUSPENDED, which means that each device is initially regarded by the
PM core as 'suspended', regardless of its real hardware status
`enum rpm_status last_status;`
- the last runtime PM status of the device captured before disabling runtime
PM for it (invalid initially and when disable_depth is 0)
`unsigned int runtime_auto;`
- if set, indicates that the user space has allowed the device driver to
power manage the device at run time via the /sys/devices/.../power/control
`interface;` it may only be modified with the help of the
pm_runtime_allow() and pm_runtime_forbid() helper functions
`unsigned int no_callbacks;`
- indicates that the device does not use the runtime PM callbacks (see
Section 8); it may be modified only by the pm_runtime_no_callbacks()
helper function
`unsigned int irq_safe;`
- indicates that the ->runtime_suspend() and ->runtime_resume() callbacks
will be invoked with the spinlock held and interrupts disabled
`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'.
초기화와 synchronous 상태 helper
304-349Runtime PM helper는 `drivers/base/power/runtime.c`와 `include/linux/pm_runtime.h`에 정의됩니다. 초기화·제거와 직접 callback 실행 계열의 의미는 다음과 같습니다.
반환값 1은 이미 목표 상태였음을 뜻하고, 특정 오류는 재시도 가능성을 나타냅니다.
`pm_runtime_get_sync()`는 오류가 나도 증가시킨 usage counter를 되돌리지 않습니다. 반환값을 검사하는 코드는 counter 균형이 더 명확한 `pm_runtime_resume_and_get()` 사용을 우선 고려해야 합니다.
4. Runtime PM Device Helper Functions
=====================================
The following runtime PM helper functions are defined in
drivers/base/power/runtime.c and include/linux/pm_runtime.h:
`void pm_runtime_init(struct device *dev);`
- initialize the device runtime PM fields in 'struct dev_pm_info'
`void pm_runtime_remove(struct device *dev);`
- make sure that the runtime PM of the device will be disabled after
removing the device from device hierarchy
`int pm_runtime_idle(struct device *dev);`
- execute the subsystem-level idle callback for the device; returns an
error code on failure, where -EINPROGRESS means that ->runtime_idle() is
already being executed; if there is no callback or the callback returns 0
then run pm_runtime_autosuspend(dev) and return its result
`int pm_runtime_suspend(struct device *dev);`
- execute the subsystem-level suspend callback for the device; returns 0 on
success, 1 if the device's runtime PM status was already 'suspended', or
error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt
to suspend the device again in future and -EACCES means that
'power.disable_depth' is different from 0
`int pm_runtime_autosuspend(struct device *dev);`
- same as pm_runtime_suspend() except that a call to
pm_runtime_mark_last_busy() is made and 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 runtime PM status is already 'active' (also if
'power.disable_depth' is nonzero, but the status was 'active' when it was
changing from 0 to 1) or error code on failure, where -EAGAIN means it may
be safe to attempt to resume the device again in future, but
'power.runtime_error' should be checked additionally, and -EACCES means
that the callback could not be run, because 'power.disable_depth' was
different from 0
`int pm_runtime_resume_and_get(struct device *dev);`
- run pm_runtime_resume(dev) and if successful, increment the device's
usage counter; returns 0 on success (whether or not the device's
runtime PM status was already 'active') or the error code from
pm_runtime_resume() on failure.
비동기 request와 usage counter 획득
350-402Request helper는 callback을 즉시 실행하기보다 `pm_wq`에 work item을 넣거나 timer로 예약합니다. Get helper는 usage counter를 증가시키고 필요에 따라 resume을 요청하거나 동기 실행합니다.
Delay 0은 즉시 queue이며, 기존 suspend timer에는 새 delay가 적용됩니다.
동기/비동기 resume 여부와 조건부 증가를 구분해야 합니다.
`int pm_request_idle(struct device *dev);`
- submit a request to execute the subsystem-level idle callback for the
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);`
- Call pm_runtime_mark_last_busy() and schedule the execution of the
subsystem-level suspend callback for the device when the autosuspend delay
expires
`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
suspend work item in pm_wq, in milliseconds (if 'delay' is zero, the work
item is queued up immediately); returns 0 on success, 1 if the device's PM
runtime status was already 'suspended', or error code if the request
hasn't been scheduled (or queued up if 'delay' is 0); if the execution of
->runtime_suspend() is already scheduled and not yet expired, the new
value of 'delay' will be used as the time to wait
`int pm_request_resume(struct device *dev);`
- submit a request to execute the subsystem-level resume callback for the
device (the request is represented by a work item in pm_wq); returns 0 on
success, 1 if the device's runtime PM status was already 'active', or
error code if the request hasn't been queued up
`void pm_runtime_get_noresume(struct device *dev);`
- increment the device's usage counter
`int pm_runtime_get(struct device *dev);`
- increment the device's usage counter, run pm_request_resume(dev) and
return its result
`int pm_runtime_get_sync(struct device *dev);`
- increment the device's usage counter, run pm_runtime_resume(dev) and
return its result;
note that it does not drop the device's usage counter on errors, so
consider using pm_runtime_resume_and_get() instead of it, especially
if its return value is checked by the caller, as this is likely to
result in cleaner code.
`int pm_runtime_get_if_in_use(struct device *dev);`
- return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
runtime PM status is RPM_ACTIVE and the runtime PM usage counter is
nonzero, increment the counter and return 1; otherwise return 0 without
changing the counter
`int pm_runtime_get_if_active(struct device *dev);`
- return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
runtime PM status is RPM_ACTIVE, increment the counter and
return 1; otherwise return 0 without changing the counter
Usage counter 반환, enable/disable과 barrier
403-453Put helper는 usage counter를 감소시키고 0이 되면 idle, suspend 또는 autosuspend를 요청합니다. `*_autosuspend()` 변형 중 public helper는 `last_busy`도 현재 시각으로 갱신합니다.
Counter가 0이 되었을 때 이어지는 작업이 서로 다릅니다.
`pm_runtime_enable()`은 `disable_depth`를 감소시키며 0이 되면 subsystem callback을 실행할 수 있습니다. `pm_runtime_disable()`은 이를 증가시키고 pending operation을 완료하거나 취소합니다. Pending resume을 만족시키려고 실제 resume callback을 실행해야 했다면 1, 아니면 0을 반환합니다.
`pm_runtime_barrier()`는 pending resume이 있으면 동기 resume하고, 다른 pending runtime PM request를 취소하며, 진행 중인 operation이 끝날 때까지 기다립니다. Resume callback 실행이 필요했으면 1, 아니면 0입니다.
Disable과 barrier는 pending work를 정리하지만 disable_depth 변경 여부가 다릅니다.
`void pm_runtime_put_noidle(struct device *dev);`
- decrement the device's usage counter
`int pm_runtime_put(struct device *dev);`
- 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);`
- set the power.last_busy field to the current time and 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_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; if the result is 0 then run
pm_runtime_idle(dev) and return its result
`int pm_runtime_put_sync_suspend(struct device *dev);`
- decrement the device's usage counter; if the result is 0 then run
pm_runtime_suspend(dev) and return its result
`int pm_runtime_put_sync_autosuspend(struct device *dev);`
- set the power.last_busy field to the current time and 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);`
- decrement the device's 'power.disable_depth' field; if that field is equal
to zero, the runtime PM helper functions can execute subsystem-level
callbacks described in Section 2 for the device
`int pm_runtime_disable(struct device *dev);`
- increment the device's 'power.disable_depth' field (if the value of that
field was previously zero, this prevents subsystem-level runtime PM
callbacks from being run for the device), make sure that all of the
pending runtime PM operations on the device are either completed or
canceled; returns 1 if there was a resume request pending and it was
necessary to execute the subsystem-level resume callback for the device
to satisfy that request, otherwise 0 is returned
`int pm_runtime_barrier(struct device *dev);`
- check if there's a resume request pending for the device and resume it
(synchronously) in that case, cancel any other pending runtime PM requests
regarding it and wait for all runtime PM operations on it in progress to
complete; returns 1 if there was a resume request pending and it was
necessary to execute the subsystem-level resume callback for the device to
satisfy that request, otherwise 0 is returned
상태 보정, userspace policy와 irq-safe
454-505직접 상태 보정은 runtime_error가 있거나 runtime PM이 disable된 경우에만 유효합니다.
`pm_runtime_set_active()`와 `pm_runtime_set_suspended()`는 `runtime_error`가 설정됐거나 `disable_depth > 0`일 때만 사용해야 합니다. Active 설정은 부모가 active가 아니고 부모의 `ignore_children`도 unset이면 실패합니다.
`pm_runtime_allow()`와 `pm_runtime_forbid()`는 `/sys/devices/.../power/control`에서 driver의 runtime power management 허용 여부를 구현합니다. Allow는 usage counter를 낮추고 forbid는 높여 runtime suspend를 막습니다.
`void pm_suspend_ignore_children(struct device *dev, bool enable);`
- set/unset the power.ignore_children flag of the device
`int pm_runtime_set_active(struct device *dev);`
- clear the device's 'power.runtime_error' flag, set the device's runtime
PM status to 'active' and update its parent's counter of 'active'
children as appropriate (it is only valid to use this function if
'power.runtime_error' is set or 'power.disable_depth' is greater than
zero); it will fail and return error code if the device has a parent
which is not active and the 'power.ignore_children' flag of which is unset
`void pm_runtime_set_suspended(struct device *dev);`
- clear the device's 'power.runtime_error' flag, set the device's runtime
PM status to 'suspended' and update its parent's counter of 'active'
children as appropriate (it is only valid to use this function if
'power.runtime_error' is set or 'power.disable_depth' is greater than
zero)
`bool pm_runtime_active(struct device *dev);`
- return true if the device's runtime PM status is 'active' or its
'power.disable_depth' field is not equal to zero, or false otherwise
`bool pm_runtime_suspended(struct device *dev);`
- return true if the device's runtime PM status is 'suspended' and its
'power.disable_depth' field is equal to zero, or false otherwise
`bool pm_runtime_status_suspended(struct device *dev);`
- return true if the device's runtime PM status is 'suspended'
`void pm_runtime_allow(struct device *dev);`
- set the power.runtime_auto flag for the device and decrease its usage
counter (used by the /sys/devices/.../power/control interface to
effectively allow the device to be power managed at run time)
`void pm_runtime_forbid(struct device *dev);`
- unset the power.runtime_auto flag for the device and increase its usage
counter (used by the /sys/devices/.../power/control interface to
effectively prevent the device from being power managed at run time)
`void pm_runtime_no_callbacks(struct device *dev);`
- set the power.no_callbacks flag for the device and remove the runtime
PM attributes from /sys/devices/.../power (or prevent them from being
added when the device is registered)
`void pm_runtime_irq_safe(struct device *dev);`
- set the power.irq_safe flag for the device, causing the runtime-PM
callbacks to be invoked with interrupts off
`bool pm_runtime_is_irq_safe(struct device *dev);`
- return true if power.irq_safe flag was set for the device, causing
the runtime-PM callbacks to be invoked with interrupts off
Autosuspend 시간 helper
506-535last_busy와 delay를 이용해 inactivity window를 계산합니다.
`use_autosuspend`가 clear인 상태에서 delay를 바꾸면 `pm_runtime_idle()`이 호출됩니다. Flag가 set이면 delay가 음수로 바뀌거나 음수에서 벗어날 때 usage counter와 동기 resume/idle 동작이 조정됩니다.
`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; call
pm_runtime_get_sync if the flag was previously cleared and
power.autosuspend_delay is negative
`void pm_runtime_dont_use_autosuspend(struct device *dev);`
- clear the power.use_autosuspend flag, disabling autosuspend delays;
decrement the device's usage counter if the flag was previously set and
power.autosuspend_delay is negative; call pm_runtime_idle
`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 runtime suspends are
prevented; if power.use_autosuspend is set, pm_runtime_get_sync may be
called or the device's usage counter may be decremented and
pm_runtime_idle called depending on if power.autosuspend_delay is
changed to or from a negative value; if power.use_autosuspend is clear,
pm_runtime_idle is called
`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
Interrupt context에서 허용되는 helper
536-567다음 helper는 일반적으로 interrupt context에서 안전합니다. Request 계열, non-resume get/put 계열, enable과 상태 flag 조작, last_busy 및 autosuspend expiration 계산이 여기에 포함됩니다.
이 목록은 device의 irq_safe 설정과 무관합니다.
Device에 `pm_runtime_irq_safe()`를 호출했다면 synchronous callback 실행 helper도 interrupt context에서 사용할 수 있습니다.
Callback이 block하거나 sleep하지 않아야 합니다.
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_put_autosuspend()
- pm_runtime_enable()
- pm_suspend_ignore_children()
- pm_runtime_set_active()
- pm_runtime_set_suspended()
- pm_runtime_suspended()
- pm_runtime_mark_last_busy()
- pm_runtime_autosuspend_expiration()
If pm_runtime_irq_safe() has been called for a device then the following helper
functions may also be used in interrupt context:
- pm_runtime_idle()
- pm_runtime_suspend()
- pm_runtime_autosuspend()
- pm_runtime_resume()
- pm_runtime_get_sync()
- pm_runtime_put_sync()
- pm_runtime_put_sync_suspend()
- pm_runtime_put_sync_autosuspend()
초기 상태와 probe 절차
568-610모든 device의 runtime PM은 처음에 disable되어 있으므로 `pm_runtime_enable()` 전에는 4절 helper 대부분이 `-EAGAIN`을 반환합니다. 초기 runtime PM 상태는 실제 hardware와 무관하게 `suspended`입니다.
Device가 실제로 I/O 가능한 active 상태라면 `pm_runtime_enable()` 전에 `pm_runtime_set_active()`로 상태를 맞춰야 합니다. 부모 runtime PM이 enable되어 있고 `ignore_children`이 unset이면 child를 active로 바꾸는 순간 부모의 active child counter가 증가해 부모가 runtime suspend하지 못합니다. 따라서 곧바로 child의 runtime PM을 enable하거나 다시 suspended로 돌려야 합니다.
실제 device가 초기 `suspended` 상태와 일치하고 `->probe()`에서 깨워야 한다면 먼저 `pm_runtime_enable()`을 호출한 뒤 `pm_runtime_resume()`을 사용합니다.
Probe 도중 subsystem callback 진입 등으로 runtime PM 호출이 가능하다면 `pm_runtime_get_sync()`와 대응하는 `pm_runtime_put()`으로 probe 동안 device가 다시 잠들지 않게 해야 합니다. Network device layer가 이런 경우입니다.
Probe가 끝난 뒤 driver core는 asynchronous `pm_request_idle()`을 제출해 device를 suspend할 기회를 줍니다. Autosuspend driver는 probe 반환 전에 last_busy를 갱신하는 것이 좋습니다.
실제 hardware 상태와 runtime_status를 enable 전에 일치시킵니다.
5. Runtime PM Initialization, Device Probing and Removal
========================================================
Initially, the runtime PM is disabled for all devices, which means that the
majority of the runtime PM helper functions described in Section 4 will return
-EAGAIN until pm_runtime_enable() is called for the device.
In addition to that, the initial runtime PM status of all devices is
'suspended', but it need not reflect the actual physical state of the device.
Thus, if the device is initially active (i.e. it is able to process I/O), its
runtime PM status must be changed to 'active', with the help of
pm_runtime_set_active(), before pm_runtime_enable() is called for the device.
However, if the device has a parent and the parent's runtime PM is enabled,
calling pm_runtime_set_active() for the device will affect the parent, unless
the parent's 'power.ignore_children' flag is set. Namely, in that case the
parent won't be able to suspend at run time, using the PM core's helper
functions, as long as the child's status is 'active', even if the child's
runtime PM is still disabled (i.e. pm_runtime_enable() hasn't been called for
the child yet or pm_runtime_disable() has been called for it). For this reason,
once pm_runtime_set_active() has been called for the device, pm_runtime_enable()
should be called for it too as soon as reasonably possible or its runtime PM
status should be changed back to 'suspended' with the help of
pm_runtime_set_suspended().
If the default initial runtime PM status of the device (i.e. 'suspended')
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 runtime PM has to be
enabled earlier by calling pm_runtime_enable().
Note, if the device may execute pm_runtime calls during the probe (such as
if it is registered with a subsystem that may call back in) then the
pm_runtime_get_sync() call paired with a pm_runtime_put() call will be
appropriate to ensure that the device is not put back to sleep during the
probe. This can happen with systems such as the network device layer.
It may be desirable to suspend the device once ->probe() has finished.
Therefore the driver core uses the asynchronous pm_request_idle() to submit a
request to execute the subsystem-level idle callback for the device at that
time. A driver that makes use of the runtime autosuspend feature may want to
update the last busy mark before returning from ->probe().
Driver unbind, remove와 userspace 제어
611-643Driver core는 `__device_release_driver()`에서 runtime PM callback과 bus notifier가 경쟁하지 않도록 합니다. `driver_sysfs_remove()`와 `BUS_NOTIFY_UNBIND_DRIVER` 전에 `pm_runtime_get_sync()`을 호출해 suspended device를 resume하고 해당 routine 동안 다시 suspend되지 않게 합니다.
`BUS_NOTIFY_UNBIND_DRIVER` 뒤에는 `pm_runtime_put_sync()`을 호출합니다. 이 때문에 bus type과 driver의 `->remove()`는 runtime PM race를 직접 막아야 하지만, remove 중 `pm_runtime_suspend()`를 호출해 device를 suspended 상태로 둘 수 있습니다.
Driver의 `->remove()`는 `->probe()`에서 수행한 runtime PM 변경을 되돌려야 합니다. 일반적으로 `pm_runtime_disable()`, `pm_runtime_dont_use_autosuspend()` 등을 호출합니다.
Userspace가 `/sys/devices/.../power/control`을 `on`으로 바꾸면 `pm_runtime_forbid()`가 호출되어 driver의 runtime PM을 사실상 막습니다. Driver도 초기화 중 active 상태를 보장한 뒤 forbid할 수 있지만, userspace가 의도적으로 `auto`를 선택했다면 이를 뒤집어 혼란을 주지 않아야 합니다.
Driver core가 usage reference를 잡아 notifier와 callback의 경쟁을 줄입니다.
Moreover, the driver core prevents runtime PM callbacks from racing with the bus
notifier callback in __device_release_driver(), which is necessary because the
notifier is used by some subsystems to carry out operations affecting the
runtime PM functionality. It does so by calling pm_runtime_get_sync() before
driver_sysfs_remove() and the BUS_NOTIFY_UNBIND_DRIVER notifications. This
resumes the device if it's in the suspended state and prevents it from
being suspended again while those routines are being executed.
To allow bus types and drivers to put devices into the suspended state by
calling pm_runtime_suspend() from their ->remove() routines, the driver core
executes pm_runtime_put_sync() after running the BUS_NOTIFY_UNBIND_DRIVER
notifications in __device_release_driver(). This requires bus types and
drivers to make their ->remove() callbacks avoid races with runtime PM directly,
but it also allows more flexibility in the handling of devices during the
removal of their drivers.
Drivers in ->remove() callback should undo the runtime PM changes done
in ->probe(). Usually this means calling pm_runtime_disable(),
pm_runtime_dont_use_autosuspend() etc.
The user space can effectively disallow the driver of the device to power manage
it at run time by changing the value of its /sys/devices/.../power/control
attribute to "on", which causes pm_runtime_forbid() to be called. In principle,
this mechanism may also be used by the driver to effectively turn off the
runtime power management of the device until the user space turns it on.
Namely, during the initialization the driver can make sure that the runtime PM
status of the device is 'active' and call pm_runtime_forbid(). It should be
noted, however, that if the user space has already intentionally changed the
value of /sys/devices/.../power/control to "auto" to allow the driver to power
manage the device at run time, the driver may confuse it by using
pm_runtime_forbid() this way.
System sleep과 runtime PM 상태 정합성
644-695Runtime PM과 system suspend/hibernation은 상호작용합니다. System sleep 시작 시 device가 active이면 단순하지만 이미 runtime-suspended라면 wakeup 설정과 power level 차이를 처리해야 합니다.
Runtime suspend에서는 remote wakeup을 허용하지만 system sleep에서는 `device_may_wakeup(dev)`가 false일 수 있습니다. 이때 subsystem-level system suspend callback은 wakeup 설정을 바꿔야 하며, 필요하면 device를 resume한 뒤 다시 suspend합니다. Runtime suspend와 system sleep이 서로 다른 power level이나 설정을 쓰는 경우도 같습니다.
System resume에서 가장 단순한 방법은 이전 runtime 상태와 무관하게 모두 full power로 돌리는 것입니다. Power level과 wakeup 설정 변경, firmware가 잃은 remote wakeup event, child resume 의존성, hibernation 후 software와 physical state 불일치, reset 필요성, usage counter가 0보다 커 곧 runtime resume할 가능성 등이 이유입니다.
System sleep 전에 suspended였지만 resume에서 full power가 된 device는 실제 상태에 맞게 runtime PM 상태를 갱신해야 합니다. 순서는 `pm_runtime_disable(dev)`, `pm_runtime_set_active(dev)`, `pm_runtime_enable(dev)`입니다.
PM core는 system `->suspend()` 전 usage counter를 증가시키고 `->resume()` 후 감소시키므로 runtime PM을 잠시 disable해도 suspend 시도가 영구히 사라지지 않습니다. Resume callback 뒤 counter가 0이 되면 평소처럼 `->runtime_idle()`을 호출합니다.
Full power로 복원된 hardware와 runtime_status를 다시 맞춥니다.
6. Runtime PM and System Sleep
==============================
Runtime PM and system sleep (i.e., system suspend and hibernation, also known
as suspend-to-RAM and suspend-to-disk) interact with each other in a couple of
ways. If a device is active when a system sleep starts, everything is
straightforward. But what should happen if the device is already suspended?
The device may have different wake-up settings for runtime PM and system sleep.
For example, remote wake-up may be enabled for runtime suspend but disallowed
for system sleep (device_may_wakeup(dev) returns 'false'). When this happens,
the subsystem-level system suspend callback is responsible for changing the
device's wake-up setting (it may leave that to the device driver's system
suspend routine). It may be necessary to resume the device and suspend it again
in order to do so. The same is true if the driver uses different power levels
or other settings for runtime suspend and system sleep.
During system resume, the simplest approach is to bring all devices back to full
power, even if they had been suspended before the system suspend began. There
are several reasons for this, including:
* The device might need to switch power levels, wake-up settings, etc.
* Remote wake-up events might have been lost by the firmware.
* The device's children may need the device to be at full power in order
to resume themselves.
* The driver's idea of the device state may not agree with the device's
physical state. This can happen during resume from hibernation.
* The device might need to be reset.
* Even though the device was suspended, if its usage counter was > 0 then most
likely it would need a runtime resume in the near future anyway.
If the device had been suspended before the system suspend began and it's
brought back to full power during resume, then its runtime PM status will have
to be updated to reflect the actual post-system sleep status. The way to do
this is:
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
The PM core always increments the runtime usage counter before calling the
->suspend() callback and decrements it after calling the ->resume() callback.
Hence disabling runtime PM temporarily like this will not cause any runtime
suspend attempts to be permanently lost. If the usage count goes to zero
following the return of the ->resume() callback, the ->runtime_idle() callback
will be invoked as usual.
계층형 device의 coordinated system sleep
696-736일부 system은 global firmware/hardware operation 없이 kernel이 모든 component를 협조적으로 저전력 상태에 넣어 system sleep을 만듭니다. Kernel이 제어권을 넘기지 않으므로 resume 때 모든 device 상태를 정확히 알 수 있고, hibernation이 아니며 앞 절의 예외가 없다면 원래 runtime-suspended였던 device를 그대로 둘 수 있습니다.
System suspend `.prepare()`가 device에 대해 양수를 반환하면 해당 device의 runtime-suspended 상태가 적절하다는 뜻입니다. 모든 descendant도 runtime suspend에 남는다면 PM core는 그 계층의 system suspend/resume callback을 실행하지 않습니다. 단, `.complete()`는 실행되며 이후 처리를 전적으로 책임집니다. 이는 hibernation과 무관한 system suspend에만 적용됩니다. 자세한 내용은 `Documentation/driver-api/pm/devices.rst`를 참고합니다.
System suspend 중 PM core는 각 device의 subsystem `.prepare()` 직전에 `pm_runtime_get_noresume()`을, `.suspend()` 직전에 `pm_runtime_barrier()`를 호출합니다. `.suspend_late()` 직전에는 두 번째 인수 false로 `__pm_runtime_disable()`을 호출합니다.
System resume 중에는 각 device의 subsystem `.resume_early()` 직후 `pm_runtime_enable()`을, `.complete()` 직후 `pm_runtime_put()`을 호출합니다. 이 순서로 runtime PM과 system sleep callback의 race 가능성을 줄입니다.
각 system sleep 단계 주위에서 runtime PM request와 counter를 정리합니다.
On some systems, however, system sleep is not entered through a global firmware
or hardware operation. Instead, all hardware components are put into low-power
states directly by the kernel in a coordinated way. Then, the system sleep
state effectively follows from the states the hardware components end up in
and the system is woken up from that state by a hardware interrupt or a similar
mechanism entirely under the kernel's control. As a result, the kernel never
gives control away and the states of all devices during resume are precisely
known to it. If that is the case and none of the situations listed above takes
place (in particular, if the system is not waking up from hibernation), it may
be more efficient to leave the devices that had been suspended before the system
suspend began in the suspended state.
To this end, the PM core provides a mechanism allowing some coordination between
different levels of device hierarchy. Namely, if a system suspend .prepare()
callback returns a positive number for a device, that indicates to the PM core
that the device appears to be runtime-suspended and its state is fine, so it
may be left in runtime suspend provided that all of its descendants are also
left in runtime suspend. If that happens, the PM core will not execute any
system suspend and resume callbacks for all of those devices, except for the
.complete() callback, which is then entirely responsible for handling the device
as appropriate. This only applies to system suspend transitions that are not
related to hibernation (see Documentation/driver-api/pm/devices.rst for more
information).
The PM core does its best to reduce the probability of race conditions between
the runtime PM and system suspend/resume (and hibernation) callbacks by carrying
out the following operations:
* During system suspend pm_runtime_get_noresume() is called for every device
right before executing the subsystem-level .prepare() callback for it and
pm_runtime_barrier() is called for every device right before executing the
subsystem-level .suspend() callback for it. In addition to that the PM core
calls __pm_runtime_disable() with 'false' as the second argument for every
device right before executing the subsystem-level .suspend_late() callback
for it.
* During system resume pm_runtime_enable() and pm_runtime_put() are called for
every device right after executing the subsystem-level .resume_early()
callback and right after executing the subsystem-level .complete() callback
for it, respectively.
PM core의 generic subsystem callback
737-818Subsystem은 `driver/base/power/generic_ops.c`의 generic PM callback을 사용해 중복 코드를 줄일 수 있습니다.
Runtime-suspended 여부에 따라 driver callback을 생략하거나 실행하고, resume/restore 성공 시 상태를 active로 맞춥니다.
Subsystem-level `dev_pm_ops`가 runtime idle/suspend/resume과 suspend/resume/freeze/thaw/poweroff/restore의 일반·noirq callback을 제공하지 않으면 PM core가 이 generic function을 기본값으로 사용합니다.
System suspend·freeze·poweroff와 runtime suspend에 같은 function을 쓰고, system resume·thaw·restore와 runtime resume에도 같은 function을 쓰려는 driver는 `include/linux/pm_runtime.h`의 `DEFINE_RUNTIME_DEV_PM_OPS()`를 사용할 수 있습니다. 마지막 인수는 필요에 따라 `NULL`로 둘 수 있습니다.
7. Generic subsystem callbacks
==============================
Subsystems may wish to conserve code space by using the set of generic power
management callbacks provided by the PM core, defined in
driver/base/power/generic_ops.c:
`int pm_generic_runtime_suspend(struct device *dev);`
- invoke the ->runtime_suspend() callback provided by the driver of this
device and return its result, or return 0 if not defined
`int pm_generic_runtime_resume(struct device *dev);`
- invoke the ->runtime_resume() callback provided by the driver of this
device and return its result, or return 0 if not defined
`int pm_generic_suspend(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->suspend()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_suspend_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_resume(struct device *dev);`
- invoke the ->resume() callback provided by the driver of this device and,
if successful, change the device's runtime PM status to 'active'
`int pm_generic_resume_noirq(struct device *dev);`
- invoke the ->resume_noirq() callback provided by the driver of this device
`int pm_generic_freeze(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->freeze()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_freeze_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_thaw(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->thaw()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_thaw_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_poweroff(struct device *dev);`
- if the device has not been suspended at run time, invoke the ->poweroff()
callback provided by its driver and return its result, or return 0 if not
defined
`int pm_generic_poweroff_noirq(struct device *dev);`
- if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
callback provided by the device's driver and return its result, or return
0 if not defined
`int pm_generic_restore(struct device *dev);`
- invoke the ->restore() callback provided by the driver of this device and,
if successful, change the device's runtime PM status to 'active'
`int pm_generic_restore_noirq(struct device *dev);`
- invoke the ->restore_noirq() callback provided by the device's driver
These functions are the defaults used by the PM core if a subsystem doesn't
provide its own callbacks for ->runtime_idle(), ->runtime_suspend(),
->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(),
->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(),
->poweroff(), ->poweroff_noirq(), ->restore(), ->restore_noirq() in the
subsystem-level dev_pm_ops structure.
Device drivers that wish to use the same function as a system suspend, freeze,
poweroff and runtime suspend callback, and similarly for system resume, thaw,
restore, and runtime resume, can achieve similar behaviour with the help of the
DEFINE_RUNTIME_DEV_PM_OPS() defined in include/linux/pm_runtime.h (possibly setting its
last argument to NULL).
Callback이 없는 logical device
819-854일부 device는 parent의 logical sub-device일 뿐 독립적으로 power management할 수 없습니다. 대표적으로 USB interface는 전체 USB device와 달리 개별 interface만 저전력 상태로 들어가거나 wakeup request를 보낼 수 없습니다.
이런 driver의 suspend/resume callback은 아무 일 없이 0을 반환하고 idle은 항상 `pm_runtime_suspend()`를 호출하는 형태가 될 것입니다. Subsystem은 device 초기화 뒤 registration 전에 `pm_runtime_no_callbacks()`를 호출해 이를 PM core에 알릴 수 있으며 registration 뒤 호출도 허용됩니다.
이 함수는 `power.no_callbacks`를 설정하고 debugging 이외의 runtime PM sysfs attribute 생성을 막습니다. Flag가 설정되면 PM core는 idle/suspend/resume callback을 호출하지 않고 suspend와 resume이 항상 성공하며 idle device는 suspend해야 한다고 가정합니다. Parent driver가 parent power state 변경을 child driver에 직접 알려야 합니다.
다만 일부 callback만 구현해야 하거나 platform PM domain이 붙을 수 있거나 supplier device link로 power management되는 device에는 `pm_runtime_no_callbacks()`가 적절하지 않을 수 있습니다. 이 경우 callback pointer를 `NULL`로 두면 PM core는 해당 callback이 존재하며 0을 반환한 것처럼 처리하므로 boilerplate callback이 필요 없습니다.
독립 power control 가능성과 외부 PM 관계를 기준으로 결정합니다.
8. "No-Callback" Devices
========================
Some "devices" are only logical sub-devices of their parent and cannot be
power-managed on their own. (The prototype example is a USB interface. Entire
USB devices can go into low-power mode or send wake-up requests, but neither is
possible for individual interfaces.) The drivers for these devices have no
need of runtime PM callbacks; if the callbacks did exist, ->runtime_suspend()
and ->runtime_resume() would always return 0 without doing anything else and
->runtime_idle() would always call pm_runtime_suspend().
Subsystems can tell the PM core about these devices by calling
pm_runtime_no_callbacks(). This should be done after the device structure is
initialized and before it is registered (although after device registration is
also okay). The routine will set the device's power.no_callbacks flag and
prevent the non-debugging runtime PM sysfs attributes from being created.
When power.no_callbacks is set, the PM core will not invoke the
->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks.
Instead it will assume that suspends and resumes always succeed and that idle
devices should be suspended.
As a consequence, the PM core will never directly inform the device's subsystem
or driver about runtime 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.
Note that, in some cases it may not be desirable for subsystems/drivers to call
pm_runtime_no_callbacks() for their devices. This could be because a subset of
the runtime PM callbacks needs to be implemented, a platform dependent PM
domain could get attached to the device or that the device is power managed
through a supplier device link. For these reasons and to avoid boilerplate code
in subsystems/drivers, the PM core allows runtime PM callbacks to be
unassigned. More precisely, if a callback pointer is NULL, the PM core will act
as though there was a callback and it returned 0.
Autosuspend 개념과 설정
855-891Power state 변경에는 시간과 energy가 들기 때문에 device가 충분히 오래 머물 가능성이 있을 때만 저전력 상태로 보내야 합니다. 일정 시간 사용되지 않은 device는 계속 idle일 가능성이 있다는 heuristic을 적용하면, 최적이 아닌 경우에도 full-power와 low-power 사이를 너무 빠르게 왕복하는 현상을 막습니다.
`autosuspend`는 device를 자동으로 suspend한다는 뜻이 아닙니다. Subsystem이나 driver가 여전히 적절한 PM routine을 호출해야 하며, runtime suspend를 원하는 inactivity period가 지날 때까지 자동으로 지연한다는 뜻입니다.
Inactivity는 `power.last_busy`를 기준으로 계산합니다. 초기 delay policy는 subsystem이 `pm_runtime_set_autosuspend_delay()`로 설정할 수 있지만, device registration 뒤에는 userspace가 `/sys/devices/.../power/autosuspend_delay_ms`로 제어해야 합니다.
Autosuspend를 쓰려면 가능하면 device registration 전에 `pm_runtime_use_autosuspend()`를 호출하고 이후 `*_autosuspend()` helper를 사용합니다. Non-autosuspend helper도 계속 정상 동작하며 `pm_runtime_idle()`처럼 상황에 따라 delay를 고려합니다. Autosuspend 변형은 `pm_runtime_mark_last_busy()`도 호출합니다.
기존 helper를 delay-aware 변형으로 바꿉니다.
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 runtime 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 runtime suspends will
automatically be delayed until the desired period of inactivity has elapsed.
Inactivity is determined based on the power.last_busy field. 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, which means sometimes taking the autosuspend delay into
account (see pm_runtime_idle). The autosuspend variants of the functions also
call pm_runtime_mark_last_busy().
Autosuspend 재예약과 I/O race
892-972Usage counter가 0이고 delay도 끝났지만 즉시 autosuspend하면 안 되는 경우가 있습니다. `->runtime_suspend()`가 `-EAGAIN` 또는 `-EBUSY`를 반환하고 다음 autosuspend 만료 시각이 미래라면, 보통 callback이 `pm_runtime_mark_last_busy()`를 호출한 경우처럼 PM core가 autosuspend를 자동 재예약합니다. Suspend callback 실행 중에는 어떤 suspend request도 받지 않으므로 callback이 스스로 재예약할 수는 없습니다.
Interrupt context의 비동기 사용은 race를 피할 수 없습니다. PM core는 `->runtime_suspend()`와 새 I/O request 도착을 동기화할 수 없으므로 driver의 private lock으로 보호해야 합니다.
예제에서 `foo_read_or_write()`는 private lock을 잡고 request를 I/O queue에 넣습니다. Pending request가 0에서 1로 바뀌면 `pm_runtime_get()`으로 device 사용을 잡고, suspend 상태가 아니면 다음 request를 처리합니다.
`foo_io_completion()`은 pending count가 0이 되면 `pm_runtime_put_autosuspend()`를 호출하고, 남은 request가 있으면 다음 것을 처리합니다. `foo_runtime_suspend()`는 같은 private lock 아래 pending request를 다시 확인해 0보다 크면 `-EBUSY`, 아니면 hardware를 suspend하고 `is_suspended`를 설정합니다.
`foo_runtime_resume()`은 같은 lock으로 hardware를 resume하고 `is_suspended`를 clear한 뒤 last_busy를 갱신하며 pending I/O가 있으면 처리를 재개합니다. 핵심은 completion의 autosuspend 요청과 새 read/write가 경쟁할 수 있으므로 suspend callback이 lock을 잡은 상태에서 pending I/O를 반드시 재검사해야 한다는 점입니다.
Userspace는 `power.autosuspend_delay`를 언제든 바꿀 수 있습니다. Driver가 이를 고려해야 한다면 suspend callback의 private lock 안에서 `pm_runtime_autosuspend_expiration()`을 호출하고, 0이 아닌 값이 반환되면 delay가 아직 끝나지 않았으므로 `-EAGAIN`을 반환합니다.
모든 pending count 검사와 suspend 상태 변경을 동일한 private lock으로 직렬화합니다.
Under some circumstances a driver or subsystem may want to prevent a device
from autosuspending immediately, even though the usage counter is zero and the
autosuspend delay time has expired. If the ->runtime_suspend() callback
returns -EAGAIN or -EBUSY, and if the next autosuspend delay expiration time is
in the future (as it normally would be if the callback invoked
pm_runtime_mark_last_busy()), the PM core will automatically reschedule the
autosuspend. The ->runtime_suspend() callback can't do this rescheduling
itself because no suspend requests of any kind are accepted while the device is
suspending (i.e., while the callback is running).
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_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_next_request(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.
요약·해설
runtime_pm.rst:1-972Runtime PM은 PM core가 device별 상태, usage 및 child counter, callback 직렬화, workqueue와 autosuspend timer를 통합 관리하는 framework입니다. Driver는 직접 상태를 추측하기보다 균형 잡힌 get/put helper와 subsystem callback 규칙을 따라야 하며, system sleep 및 비동기 I/O와의 race는 명시적으로 동기화해야 합니다.