2009-12-21 21:07:14

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 0/5] PM patches for the next pull request

Hi,

I'd like to include the following 5 patches into the next pull request
(shortly).

Two of them are evident fixes, the other three are simple enhancements and/or
documentation updates.

Please let me know if there's anything wrong with them.

Rafael


2009-12-21 21:08:11

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 1/5] PM: Make the initcall_debug style timing for suspend/resume complete

From: Rafael J. Wysocki <[email protected]>

Commit f2511774863487e61b56a97da07ebf8dd61d7836
(PM: Add initcall_debug style timing for suspend/resume) introduced
basic timing instrumentation, needed for a scritps/bootgraph.pl
equivalent or humans, but it missed the fact that bus types and
device classes which haven't been switched to using struct dev_pm_ops
objects yet need special handling. As a result, the suspend/resume
timing information is only available for devices whose bus types or
device classes use struct dev_pm_ops objects, so the majority of
devices is not covered.

Fix this by adding basic suspend/resume timing instrumentation for
devices whose bus types and device classes still don't use struct
dev_pm_ops objects for power management. To reduce code duplication
move the timing code to helper functions.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/power/main.c | 97 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 77 insertions(+), 20 deletions(-)

Index: linux-2.6/drivers/base/power/main.c
===================================================================
--- linux-2.6.orig/drivers/base/power/main.c
+++ linux-2.6/drivers/base/power/main.c
@@ -161,6 +161,32 @@ void device_pm_move_last(struct device *
list_move_tail(&dev->power.entry, &dpm_list);
}

+static ktime_t initcall_debug_start(struct device *dev)
+{
+ ktime_t calltime = ktime_set(0, 0);
+
+ if (initcall_debug) {
+ pr_info("calling %s+ @ %i\n",
+ dev_name(dev), task_pid_nr(current));
+ calltime = ktime_get();
+ }
+
+ return calltime;
+}
+
+static void initcall_debug_report(struct device *dev, ktime_t calltime,
+ int error)
+{
+ ktime_t delta, rettime;
+
+ if (initcall_debug) {
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev),
+ error, (unsigned long long)ktime_to_ns(delta) >> 10);
+ }
+}
+
/**
* pm_op - Execute the PM operation appropriate for given PM event.
* @dev: Device to handle.
@@ -172,13 +198,9 @@ static int pm_op(struct device *dev,
pm_message_t state)
{
int error = 0;
- ktime_t calltime, delta, rettime;
+ ktime_t calltime;

- if (initcall_debug) {
- pr_info("calling %s+ @ %i\n",
- dev_name(dev), task_pid_nr(current));
- calltime = ktime_get();
- }
+ calltime = initcall_debug_start(dev);

switch (state.event) {
#ifdef CONFIG_SUSPEND
@@ -227,12 +249,7 @@ static int pm_op(struct device *dev,
error = -EINVAL;
}

- if (initcall_debug) {
- rettime = ktime_get();
- delta = ktime_sub(rettime, calltime);
- pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev),
- error, (unsigned long long)ktime_to_ns(delta) >> 10);
- }
+ initcall_debug_report(dev, calltime, error);

return error;
}
@@ -309,8 +326,9 @@ static int pm_noirq_op(struct device *de
if (initcall_debug) {
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
- printk("initcall %s_i+ returned %d after %Ld usecs\n", dev_name(dev),
- error, (unsigned long long)ktime_to_ns(delta) >> 10);
+ printk("initcall %s_i+ returned %d after %Ld usecs\n",
+ dev_name(dev), error,
+ (unsigned long long)ktime_to_ns(delta) >> 10);
}

return error;
@@ -408,6 +426,26 @@ void dpm_resume_noirq(pm_message_t state
EXPORT_SYMBOL_GPL(dpm_resume_noirq);

/**
+ * legacy_resume - Execute a legacy (bus or class) resume callback for device.
+ * dev: Device to resume.
+ * cb: Resume callback to execute.
+ */
+static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
+{
+ int error;
+ ktime_t calltime;
+
+ calltime = initcall_debug_start(dev);
+
+ error = cb(dev);
+ suspend_report_result(cb, error);
+
+ initcall_debug_report(dev, calltime, error);
+
+ return error;
+}
+
+/**
* device_resume - Execute "resume" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
@@ -427,7 +465,7 @@ static int device_resume(struct device *
error = pm_op(dev, dev->bus->pm, state);
} else if (dev->bus->resume) {
pm_dev_dbg(dev, state, "legacy ");
- error = dev->bus->resume(dev);
+ error = legacy_resume(dev, dev->bus->resume);
}
if (error)
goto End;
@@ -448,7 +486,7 @@ static int device_resume(struct device *
error = pm_op(dev, dev->class->pm, state);
} else if (dev->class->resume) {
pm_dev_dbg(dev, state, "legacy class ");
- error = dev->class->resume(dev);
+ error = legacy_resume(dev, dev->class->resume);
}
}
End:
@@ -648,6 +686,27 @@ int dpm_suspend_noirq(pm_message_t state
EXPORT_SYMBOL_GPL(dpm_suspend_noirq);

/**
+ * legacy_suspend - Execute a legacy (bus or class) suspend callback for device.
+ * dev: Device to suspend.
+ * cb: Suspend callback to execute.
+ */
+static int legacy_suspend(struct device *dev, pm_message_t state,
+ int (*cb)(struct device *dev, pm_message_t state))
+{
+ int error;
+ ktime_t calltime;
+
+ calltime = initcall_debug_start(dev);
+
+ error = cb(dev, state);
+ suspend_report_result(cb, error);
+
+ initcall_debug_report(dev, calltime, error);
+
+ return error;
+}
+
+/**
* device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
@@ -664,8 +723,7 @@ static int device_suspend(struct device
error = pm_op(dev, dev->class->pm, state);
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
- error = dev->class->suspend(dev, state);
- suspend_report_result(dev->class->suspend, error);
+ error = legacy_suspend(dev, state, dev->class->suspend);
}
if (error)
goto End;
@@ -686,8 +744,7 @@ static int device_suspend(struct device
error = pm_op(dev, dev->bus->pm, state);
} else if (dev->bus->suspend) {
pm_dev_dbg(dev, state, "legacy ");
- error = dev->bus->suspend(dev, state);
- suspend_report_result(dev->bus->suspend, error);
+ error = legacy_suspend(dev, state, dev->bus->suspend);
}
}
End:

2009-12-21 21:07:36

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 2/5] PM: Measure device suspend and resume times

From: Rafael J. Wysocki <[email protected]>

Measure and print the time of suspending and resuming all devices.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/power/main.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

Index: linux-2.6/drivers/base/power/main.c
===================================================================
--- linux-2.6.orig/drivers/base/power/main.c
+++ linux-2.6/drivers/base/power/main.c
@@ -372,6 +372,23 @@ static void pm_dev_err(struct device *de
kobject_name(&dev->kobj), pm_verb(state.event), info, error);
}

+static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)
+{
+ ktime_t calltime;
+ s64 usecs64;
+ int usecs;
+
+ calltime = ktime_get();
+ usecs64 = ktime_to_ns(ktime_sub(calltime, starttime));
+ do_div(usecs64, NSEC_PER_USEC);
+ usecs = usecs64;
+ if (usecs == 0)
+ usecs = 1;
+ pr_info("PM: %s%s%s of devices complete after %ld.%03ld msecs\n",
+ info ?: "", info ? " " : "", pm_verb(state.event),
+ usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
+}
+
/*------------------------- Resume routines -------------------------*/

/**
@@ -408,6 +425,7 @@ static int device_resume_noirq(struct de
void dpm_resume_noirq(pm_message_t state)
{
struct device *dev;
+ ktime_t starttime = ktime_get();

mutex_lock(&dpm_list_mtx);
transition_started = false;
@@ -421,6 +439,7 @@ void dpm_resume_noirq(pm_message_t state
pm_dev_err(dev, state, " early", error);
}
mutex_unlock(&dpm_list_mtx);
+ dpm_show_time(starttime, state, "early");
resume_device_irqs();
}
EXPORT_SYMBOL_GPL(dpm_resume_noirq);
@@ -506,6 +525,7 @@ static int device_resume(struct device *
static void dpm_resume(pm_message_t state)
{
struct list_head list;
+ ktime_t starttime = ktime_get();

INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
@@ -534,6 +554,7 @@ static void dpm_resume(pm_message_t stat
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
+ dpm_show_time(starttime, state, NULL);
}

/**
@@ -666,6 +687,7 @@ static int device_suspend_noirq(struct d
int dpm_suspend_noirq(pm_message_t state)
{
struct device *dev;
+ ktime_t starttime = ktime_get();
int error = 0;

suspend_device_irqs();
@@ -681,6 +703,8 @@ int dpm_suspend_noirq(pm_message_t state
mutex_unlock(&dpm_list_mtx);
if (error)
dpm_resume_noirq(resume_event(state));
+ else
+ dpm_show_time(starttime, state, "late");
return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_noirq);
@@ -760,6 +784,7 @@ static int device_suspend(struct device
static int dpm_suspend(pm_message_t state)
{
struct list_head list;
+ ktime_t starttime = ktime_get();
int error = 0;

INIT_LIST_HEAD(&list);
@@ -785,6 +810,8 @@ static int dpm_suspend(pm_message_t stat
}
list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx);
+ if (!error)
+ dpm_show_time(starttime, state, NULL);
return error;
}

2009-12-21 21:07:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 3/5] PM: Use pm_runtime_put_sync in system resume

From: Alan Stern <[email protected]>

This patch (as1317) fixes a bug in the PM core. When a device is
resumed following a system sleep, the core decrements the device's
runtime PM usage counter but doesn't issue an idle notification if the
counter reaches 0. This could prevent an otherwise unused device from
being runtime-suspended again after the system sleep.

The fix is to call pm_runtime_put_sync() instead of
pm_runtime_put_noidle().

Signed-off-by: Alan Stern <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/power/main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

Index: linux-2.6/drivers/base/power/main.c
===================================================================
--- linux-2.6.orig/drivers/base/power/main.c
+++ linux-2.6/drivers/base/power/main.c
@@ -607,7 +607,7 @@ static void dpm_complete(pm_message_t st
mutex_unlock(&dpm_list_mtx);

device_complete(dev, state);
- pm_runtime_put_noidle(dev);
+ pm_runtime_put_sync(dev);

mutex_lock(&dpm_list_mtx);
}
@@ -880,7 +880,7 @@ static int dpm_prepare(pm_message_t stat
pm_runtime_get_noresume(dev);
if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
/* Wake-up requested during system sleep transition. */
- pm_runtime_put_noidle(dev);
+ pm_runtime_put_sync(dev);
error = -EBUSY;
} else {
error = device_prepare(dev, state);

2009-12-21 21:08:16

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 4/5] PM / Runtime: Use device type and device class callbacks

From: Rafael J. Wysocki <[email protected]>

The power management of some devices is handled through device types
and device classes rather than through bus types. Since these
devices may also benefit from using the run-time power management
core, extend it so that the device type and device class run-time PM
callbacks can be taken into consideration by it if the bus type
callback is not defined.

Update the run-time PM core documentation to reflect this change.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
Documentation/power/runtime_pm.txt | 173 ++++++++++++++++++-------------------
drivers/base/power/runtime.c | 45 +++++++++
2 files changed, 132 insertions(+), 86 deletions(-)

Index: linux-2.6/drivers/base/power/runtime.c
===================================================================
--- linux-2.6.orig/drivers/base/power/runtime.c
+++ linux-2.6/drivers/base/power/runtime.c
@@ -85,6 +85,19 @@ static int __pm_runtime_idle(struct devi
dev->bus->pm->runtime_idle(dev);

spin_lock_irq(&dev->power.lock);
+ } else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) {
+ spin_unlock_irq(&dev->power.lock);
+
+ dev->type->pm->runtime_idle(dev);
+
+ spin_lock_irq(&dev->power.lock);
+ } else if (dev->class && dev->class->pm
+ && dev->class->pm->runtime_idle) {
+ spin_unlock_irq(&dev->power.lock);
+
+ dev->class->pm->runtime_idle(dev);
+
+ spin_lock_irq(&dev->power.lock);
}

dev->power.idle_notification = false;
@@ -194,6 +207,22 @@ int __pm_runtime_suspend(struct device *

spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
+ } else if (dev->type && dev->type->pm
+ && dev->type->pm->runtime_suspend) {
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = dev->type->pm->runtime_suspend(dev);
+
+ spin_lock_irq(&dev->power.lock);
+ dev->power.runtime_error = retval;
+ } else if (dev->class && dev->class->pm
+ && dev->class->pm->runtime_suspend) {
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = dev->class->pm->runtime_suspend(dev);
+
+ spin_lock_irq(&dev->power.lock);
+ dev->power.runtime_error = retval;
} else {
retval = -ENOSYS;
}
@@ -359,6 +388,22 @@ int __pm_runtime_resume(struct device *d

spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval;
+ } else if (dev->type && dev->type->pm
+ && dev->type->pm->runtime_resume) {
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = dev->type->pm->runtime_resume(dev);
+
+ spin_lock_irq(&dev->power.lock);
+ dev->power.runtime_error = retval;
+ } else if (dev->class && dev->class->pm
+ && dev->class->pm->runtime_resume) {
+ spin_unlock_irq(&dev->power.lock);
+
+ retval = dev->class->pm->runtime_resume(dev);
+
+ spin_lock_irq(&dev->power.lock);
+ dev->power.runtime_error = retval;
} else {
retval = -ENOSYS;
}
Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -42,80 +42,81 @@ struct dev_pm_ops {
...
};

-The ->runtime_suspend() callback is executed by the PM core for the bus type of
-the device being suspended. The bus type's callback is then _entirely_
-_responsible_ for handling the device as appropriate, which may, but need not
-include executing the device driver's own ->runtime_suspend() callback (from the
+The ->runtime_suspend(), ->runtime_resume() and ->runtime_idle() callbacks are
+executed by the PM core for either the bus type, or device type (if the bus
+type's callback is not defined), or device class (if the bus type's and device
+type's callbacks are not defined) of given device. The bus type, device type
+and device class callbacks are referred to as subsystem-level callbacks in what
+follows.
+
+The subsystem-level suspend callback 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 bus type's ->runtime_suspend() knows
-what to do to handle the device).
+callback in a device driver as long as the subsystem-level suspend callback
+knows what to do to handle the device).

- * Once the bus type's ->runtime_suspend() callback has completed successfully
+ * Once the subsystem-level suspend callback has completed successfully
for given device, the PM core regards the device as suspended, which need
not mean that the device 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 its bus type's
- ->runtime_resume() callback is executed for it. The run-time PM status of
- a device after successful execution of its bus type's ->runtime_suspend()
- callback is 'suspended'.
-
- * If the bus type's ->runtime_suspend() callback returns -EBUSY or -EAGAIN,
- the device's run-time PM status is supposed to be 'active', which means that
- the device _must_ be fully operational afterwards.
-
- * If the bus type's ->runtime_suspend() callback returns an error code
- different from -EBUSY or -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 the status of it is directly set either to 'active'
- or to 'suspended' (the PM core provides special helper functions for this
- purpose).
-
-In particular, if the driver requires remote wakeup capability for proper
-functioning and device_run_wake() returns 'false' for the device, then
-->runtime_suspend() should return -EBUSY. On the other hand, if
-device_run_wake() returns 'true' for the device and the device is put
-into a low power state during the execution of its bus type's
-->runtime_suspend(), it is expected that remote wake-up (i.e. hardware mechanism
-allowing the device to request a change of its power state, such as PCI PME)
-will be enabled for the device. Generally, remote wake-up should be enabled
-for all input devices put into a low power state at run time.
-
-The ->runtime_resume() callback is executed by the PM core for the bus type of
-the device being woken up. The bus type's callback is then _entirely_
-_responsible_ for handling 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 bus type's ->runtime_resume() knows
-what to do to handle the device).
-
- * Once the bus type's ->runtime_resume() callback 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 run-time
- PM status of the device is then 'active'.
-
- * If the bus type's ->runtime_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 either to 'active' or to 'suspended' (the PM core provides
- special helper functions for this purpose).
-
-The ->runtime_idle() callback is executed by the PM core for the bus type of
-given device 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.
+ not communicate with the CPU(s) and RAM until the subsystem-level resume
+ callback is executed for it. The run-time PM status of a device after
+ successful execution of the subsystem-level suspend callback is 'suspended'.
+
+ * If the subsystem-level suspend callback returns -EBUSY or -EAGAIN,
+ the device's run-time PM status is 'active', which means that the device
+ _must_ be fully operational afterwards.
+
+ * If the subsystem-level suspend callback returns an error code different
+ from -EBUSY or -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 the status of it is directly set either to 'active', or to 'suspended'
+ (the PM core provides special helper functions for this purpose).
+
+In particular, if the driver requires remote wake-up 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_run_wake() returns 'false' for the
+device, then ->runtime_suspend() should return -EBUSY. On the other hand, if
+device_run_wake() returns 'true' for the device and the device is put into a low
+power state during the execution of the subsystem-level suspend callback, it is
+expected that remote wake-up will be enabled for the device. Generally, remote
+wake-up should be enabled for all input devices put into a low power state at
+run time.
+
+The subsystem-level resume callback 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 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 run-time PM status
+ of the device is then 'active'.
+
+ * If the subsystem-level 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
+ either to 'active' or to 'suspended' (the PM core provides special helper
+ functions for this purpose).
+
+The subsystem-level idle callback 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
- device bus type's ->runtime_idle() callback (with the device as an
- argument).
+ subsystem-level idle callback with the device as an argument.

-The action performed by a bus type's ->runtime_idle() callback is totally
-dependent on the bus type in question, but the expected and recommended action
-is to check if the device can be suspended (i.e. if all of the conditions
-necessary for suspending the device are satisfied) and to queue up a suspend
-request for the device in that case. The value returned by this callback is
-ignored by the PM core.
+The action performed by a subsystem-level idle callback is totally dependent on
+the subsystem 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. The value returned by this callback is 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 the bus type's run-time
@@ -238,41 +239,41 @@ drivers/base/power/runtime.c and include
removing the device from device hierarchy

int pm_runtime_idle(struct device *dev);
- - execute ->runtime_idle() for the device's bus type; returns 0 on success
- or error code on failure, where -EINPROGRESS means that ->runtime_idle()
- is already being executed
+ - execute the subsystem-level idle callback for the device; returns 0 on
+ success or error code on failure, where -EINPROGRESS means that
+ ->runtime_idle() is already being executed

int pm_runtime_suspend(struct device *dev);
- - execute ->runtime_suspend() for the device's bus type; returns 0 on
+ - execute the subsystem-level suspend callback for the device; returns 0 on
success, 1 if the device's run-time 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

int pm_runtime_resume(struct device *dev);
- - execute ->runtime_resume() for the device's bus type; returns 0 on
+ - execute the subsystem-leve resume callback for the device; returns 0 on
success, 1 if the device's run-time PM status was already 'active' 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

int pm_request_idle(struct device *dev);
- - submit a request to execute ->runtime_idle() for the device's bus type
- (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
+ - 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_schedule_suspend(struct device *dev, unsigned int delay);
- - schedule the execution of ->runtime_suspend() for the device's bus type
- in future, where 'delay' is the time to wait before queuing up a suspend
- work item in pm_wq, in milliseconds (if 'delay' is zero, the work item is
- queued up immediately); returns 0 on success, 1 if the device's PM
+ - 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
run-time 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 ->runtime_resume() for the device's bus type
- (the request is represented by a work item in pm_wq); returns 0 on
+ - 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 run-time PM status was already 'active', or
error code if the request hasn't been queued up

@@ -303,12 +304,12 @@ drivers/base/power/runtime.c and include
run-time PM callbacks described in Section 2

int pm_runtime_disable(struct device *dev);
- - prevent the run-time PM helper functions from running the device bus
- type's run-time PM callbacks, make sure that all of the pending run-time
- 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
- ->runtime_resume() for the device's bus type to satisfy that request,
- otherwise 0 is returned
+ - prevent the run-time PM helper functions from running subsystem-level
+ run-time PM callbacks for the device, make sure that all of the pending
+ run-time 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

void pm_suspend_ignore_children(struct device *dev, bool enable);
- set/unset the power.ignore_children flag of the device
@@ -378,5 +379,5 @@ pm_runtime_suspend() or pm_runtime_idle(
they will fail returning -EAGAIN, because the device's usage counter is
incremented by the core before executing ->probe() and ->remove(). Still, it
may be desirable to suspend the device as soon as ->probe() or ->remove() has
-finished, so the PM core uses pm_runtime_idle_sync() to invoke the device bus
-type's ->runtime_idle() callback at that time.
+finished, so the PM core uses pm_runtime_idle_sync() to invoke the
+subsystem-level idle callback for the device at that time.

2009-12-21 21:07:32

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 5/5] PM: Runtime PM documentation update

From: Alan Stern <[email protected]>

This patch (as1318) updates the runtime PM documentation, adding a
section discussing the interaction between runtime PM and system sleep.

[rjw: Rebased and made it agree with the other updates better.]

Signed-off-by: Alan Stern <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
Documentation/power/runtime_pm.txt | 50 +++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)

Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -381,3 +381,53 @@ incremented by the core before executing
may be desirable to suspend the device as soon as ->probe() or ->remove() has
finished, so the PM core uses pm_runtime_idle_sync() to invoke the
subsystem-level idle callback for the device at that time.
+
+6. Run-time PM and System Sleep
+
+Run-time 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 run-time PM and system sleep.
+For example, remote wake-up may be enabled for run-time 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 run-time suspend and system sleep.
+
+During system resume, devices generally should be brought back to full power,
+even if they were suspended before the system sleep 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 run-time resume in the near future anyway.
+
+ * Always going back to full power is simplest.
+
+If the device was suspended before the sleep began, then its run-time 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 run-time usage counter before calling the
+->prepare() callback and decrements it after calling the ->complete() callback.
+Hence disabling run-time PM temporarily like this will not cause any run-time
+suspend callbacks to be lost.

2009-12-21 21:15:53

by Alan Stern

[permalink] [raw]
Subject: Re: [PATCH 1/5] PM: Make the initcall_debug style timing for suspend/resume complete

On Mon, 21 Dec 2009, Rafael J. Wysocki wrote:

> From: Rafael J. Wysocki <[email protected]>
>
> Commit f2511774863487e61b56a97da07ebf8dd61d7836
> (PM: Add initcall_debug style timing for suspend/resume) introduced
> basic timing instrumentation, needed for a scritps/bootgraph.pl
> equivalent or humans, but it missed the fact that bus types and
> device classes which haven't been switched to using struct dev_pm_ops
> objects yet need special handling. As a result, the suspend/resume
> timing information is only available for devices whose bus types or
> device classes use struct dev_pm_ops objects, so the majority of
> devices is not covered.
>
> Fix this by adding basic suspend/resume timing instrumentation for
> devices whose bus types and device classes still don't use struct
> dev_pm_ops objects for power management. To reduce code duplication
> move the timing code to helper functions.

A minor complaint...

> +/**
> * device_resume - Execute "resume" callbacks for given device.
> * @dev: Device to handle.
> * @state: PM transition of the system being carried out.
> @@ -427,7 +465,7 @@ static int device_resume(struct device *
> error = pm_op(dev, dev->bus->pm, state);
> } else if (dev->bus->resume) {
> pm_dev_dbg(dev, state, "legacy ");
> - error = dev->bus->resume(dev);
> + error = legacy_resume(dev, dev->bus->resume);
> }
> if (error)
> goto End;
> @@ -448,7 +486,7 @@ static int device_resume(struct device *
> error = pm_op(dev, dev->class->pm, state);
> } else if (dev->class->resume) {
> pm_dev_dbg(dev, state, "legacy class ");
> - error = dev->class->resume(dev);
> + error = legacy_resume(dev, dev->class->resume);
> }
> }

If none of the method callbacks are non-NULL, this won't print
anything. The same is true for device_suspend().

Now maybe you don't _want_ to print anything in that case, but for
debugging purposes it could be useful.

Alan Stern

2009-12-21 22:39:39

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 1/5] PM: Make the initcall_debug style timing for suspend/resume complete

On Monday 21 December 2009, Alan Stern wrote:
> On Mon, 21 Dec 2009, Rafael J. Wysocki wrote:
>
> > From: Rafael J. Wysocki <[email protected]>
> >
> > Commit f2511774863487e61b56a97da07ebf8dd61d7836
> > (PM: Add initcall_debug style timing for suspend/resume) introduced
> > basic timing instrumentation, needed for a scritps/bootgraph.pl
> > equivalent or humans, but it missed the fact that bus types and
> > device classes which haven't been switched to using struct dev_pm_ops
> > objects yet need special handling. As a result, the suspend/resume
> > timing information is only available for devices whose bus types or
> > device classes use struct dev_pm_ops objects, so the majority of
> > devices is not covered.
> >
> > Fix this by adding basic suspend/resume timing instrumentation for
> > devices whose bus types and device classes still don't use struct
> > dev_pm_ops objects for power management. To reduce code duplication
> > move the timing code to helper functions.
>
> A minor complaint...
>
> > +/**
> > * device_resume - Execute "resume" callbacks for given device.
> > * @dev: Device to handle.
> > * @state: PM transition of the system being carried out.
> > @@ -427,7 +465,7 @@ static int device_resume(struct device *
> > error = pm_op(dev, dev->bus->pm, state);
> > } else if (dev->bus->resume) {
> > pm_dev_dbg(dev, state, "legacy ");
> > - error = dev->bus->resume(dev);
> > + error = legacy_resume(dev, dev->bus->resume);
> > }
> > if (error)
> > goto End;
> > @@ -448,7 +486,7 @@ static int device_resume(struct device *
> > error = pm_op(dev, dev->class->pm, state);
> > } else if (dev->class->resume) {
> > pm_dev_dbg(dev, state, "legacy class ");
> > - error = dev->class->resume(dev);
> > + error = legacy_resume(dev, dev->class->resume);
> > }
> > }
>
> If none of the method callbacks are non-NULL, this won't print
> anything. The same is true for device_suspend().
>
> Now maybe you don't _want_ to print anything in that case, but for
> debugging purposes it could be useful.

I'm going to add that in a separate patch.

Rafael