Commit 2218a4fc authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6:
  PM: Runtime PM documentation update
  PM / Runtime: Use device type and device class callbacks
  PM: Use pm_runtime_put_sync in system resume
  PM: Measure device suspend and resume times
  PM: Make the initcall_debug style timing for suspend/resume complete
parents fe35d4a0 f1212ae1
This diff is collapsed.
...@@ -161,6 +161,32 @@ void device_pm_move_last(struct device *dev) ...@@ -161,6 +161,32 @@ void device_pm_move_last(struct device *dev)
list_move_tail(&dev->power.entry, &dpm_list); 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. * pm_op - Execute the PM operation appropriate for given PM event.
* @dev: Device to handle. * @dev: Device to handle.
...@@ -172,13 +198,9 @@ static int pm_op(struct device *dev, ...@@ -172,13 +198,9 @@ static int pm_op(struct device *dev,
pm_message_t state) pm_message_t state)
{ {
int error = 0; int error = 0;
ktime_t calltime, delta, rettime; ktime_t calltime;
if (initcall_debug) { calltime = initcall_debug_start(dev);
pr_info("calling %s+ @ %i\n",
dev_name(dev), task_pid_nr(current));
calltime = ktime_get();
}
switch (state.event) { switch (state.event) {
#ifdef CONFIG_SUSPEND #ifdef CONFIG_SUSPEND
...@@ -227,12 +249,7 @@ static int pm_op(struct device *dev, ...@@ -227,12 +249,7 @@ static int pm_op(struct device *dev,
error = -EINVAL; error = -EINVAL;
} }
if (initcall_debug) { initcall_debug_report(dev, calltime, error);
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);
}
return error; return error;
} }
...@@ -309,8 +326,9 @@ static int pm_noirq_op(struct device *dev, ...@@ -309,8 +326,9 @@ static int pm_noirq_op(struct device *dev,
if (initcall_debug) { if (initcall_debug) {
rettime = ktime_get(); rettime = ktime_get();
delta = ktime_sub(rettime, calltime); delta = ktime_sub(rettime, calltime);
printk("initcall %s_i+ returned %d after %Ld usecs\n", dev_name(dev), printk("initcall %s_i+ returned %d after %Ld usecs\n",
error, (unsigned long long)ktime_to_ns(delta) >> 10); dev_name(dev), error,
(unsigned long long)ktime_to_ns(delta) >> 10);
} }
return error; return error;
...@@ -354,6 +372,23 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, ...@@ -354,6 +372,23 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
kobject_name(&dev->kobj), pm_verb(state.event), info, error); 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 -------------------------*/ /*------------------------- Resume routines -------------------------*/
/** /**
...@@ -390,6 +425,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) ...@@ -390,6 +425,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
void dpm_resume_noirq(pm_message_t state) void dpm_resume_noirq(pm_message_t state)
{ {
struct device *dev; struct device *dev;
ktime_t starttime = ktime_get();
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
transition_started = false; transition_started = false;
...@@ -403,10 +439,31 @@ void dpm_resume_noirq(pm_message_t state) ...@@ -403,10 +439,31 @@ void dpm_resume_noirq(pm_message_t state)
pm_dev_err(dev, state, " early", error); pm_dev_err(dev, state, " early", error);
} }
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
dpm_show_time(starttime, state, "early");
resume_device_irqs(); resume_device_irqs();
} }
EXPORT_SYMBOL_GPL(dpm_resume_noirq); 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. * device_resume - Execute "resume" callbacks for given device.
* @dev: Device to handle. * @dev: Device to handle.
...@@ -427,7 +484,7 @@ static int device_resume(struct device *dev, pm_message_t state) ...@@ -427,7 +484,7 @@ static int device_resume(struct device *dev, pm_message_t state)
error = pm_op(dev, dev->bus->pm, state); error = pm_op(dev, dev->bus->pm, state);
} else if (dev->bus->resume) { } else if (dev->bus->resume) {
pm_dev_dbg(dev, state, "legacy "); pm_dev_dbg(dev, state, "legacy ");
error = dev->bus->resume(dev); error = legacy_resume(dev, dev->bus->resume);
} }
if (error) if (error)
goto End; goto End;
...@@ -448,7 +505,7 @@ static int device_resume(struct device *dev, pm_message_t state) ...@@ -448,7 +505,7 @@ static int device_resume(struct device *dev, pm_message_t state)
error = pm_op(dev, dev->class->pm, state); error = pm_op(dev, dev->class->pm, state);
} else if (dev->class->resume) { } else if (dev->class->resume) {
pm_dev_dbg(dev, state, "legacy class "); pm_dev_dbg(dev, state, "legacy class ");
error = dev->class->resume(dev); error = legacy_resume(dev, dev->class->resume);
} }
} }
End: End:
...@@ -468,6 +525,7 @@ static int device_resume(struct device *dev, pm_message_t state) ...@@ -468,6 +525,7 @@ static int device_resume(struct device *dev, pm_message_t state)
static void dpm_resume(pm_message_t state) static void dpm_resume(pm_message_t state)
{ {
struct list_head list; struct list_head list;
ktime_t starttime = ktime_get();
INIT_LIST_HEAD(&list); INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
...@@ -496,6 +554,7 @@ static void dpm_resume(pm_message_t state) ...@@ -496,6 +554,7 @@ static void dpm_resume(pm_message_t state)
} }
list_splice(&list, &dpm_list); list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
dpm_show_time(starttime, state, NULL);
} }
/** /**
...@@ -548,7 +607,7 @@ static void dpm_complete(pm_message_t state) ...@@ -548,7 +607,7 @@ static void dpm_complete(pm_message_t state)
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
device_complete(dev, state); device_complete(dev, state);
pm_runtime_put_noidle(dev); pm_runtime_put_sync(dev);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
} }
...@@ -628,6 +687,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) ...@@ -628,6 +687,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
int dpm_suspend_noirq(pm_message_t state) int dpm_suspend_noirq(pm_message_t state)
{ {
struct device *dev; struct device *dev;
ktime_t starttime = ktime_get();
int error = 0; int error = 0;
suspend_device_irqs(); suspend_device_irqs();
...@@ -643,10 +703,33 @@ int dpm_suspend_noirq(pm_message_t state) ...@@ -643,10 +703,33 @@ int dpm_suspend_noirq(pm_message_t state)
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
if (error) if (error)
dpm_resume_noirq(resume_event(state)); dpm_resume_noirq(resume_event(state));
else
dpm_show_time(starttime, state, "late");
return error; return error;
} }
EXPORT_SYMBOL_GPL(dpm_suspend_noirq); 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. * device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle. * @dev: Device to handle.
...@@ -664,8 +747,7 @@ static int device_suspend(struct device *dev, pm_message_t state) ...@@ -664,8 +747,7 @@ static int device_suspend(struct device *dev, pm_message_t state)
error = pm_op(dev, dev->class->pm, state); error = pm_op(dev, dev->class->pm, state);
} else if (dev->class->suspend) { } else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class "); pm_dev_dbg(dev, state, "legacy class ");
error = dev->class->suspend(dev, state); error = legacy_suspend(dev, state, dev->class->suspend);
suspend_report_result(dev->class->suspend, error);
} }
if (error) if (error)
goto End; goto End;
...@@ -686,8 +768,7 @@ static int device_suspend(struct device *dev, pm_message_t state) ...@@ -686,8 +768,7 @@ static int device_suspend(struct device *dev, pm_message_t state)
error = pm_op(dev, dev->bus->pm, state); error = pm_op(dev, dev->bus->pm, state);
} else if (dev->bus->suspend) { } else if (dev->bus->suspend) {
pm_dev_dbg(dev, state, "legacy "); pm_dev_dbg(dev, state, "legacy ");
error = dev->bus->suspend(dev, state); error = legacy_suspend(dev, state, dev->bus->suspend);
suspend_report_result(dev->bus->suspend, error);
} }
} }
End: End:
...@@ -703,6 +784,7 @@ static int device_suspend(struct device *dev, pm_message_t state) ...@@ -703,6 +784,7 @@ static int device_suspend(struct device *dev, pm_message_t state)
static int dpm_suspend(pm_message_t state) static int dpm_suspend(pm_message_t state)
{ {
struct list_head list; struct list_head list;
ktime_t starttime = ktime_get();
int error = 0; int error = 0;
INIT_LIST_HEAD(&list); INIT_LIST_HEAD(&list);
...@@ -728,6 +810,8 @@ static int dpm_suspend(pm_message_t state) ...@@ -728,6 +810,8 @@ static int dpm_suspend(pm_message_t state)
} }
list_splice(&list, dpm_list.prev); list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
if (!error)
dpm_show_time(starttime, state, NULL);
return error; return error;
} }
...@@ -796,7 +880,7 @@ static int dpm_prepare(pm_message_t state) ...@@ -796,7 +880,7 @@ static int dpm_prepare(pm_message_t state)
pm_runtime_get_noresume(dev); pm_runtime_get_noresume(dev);
if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
/* Wake-up requested during system sleep transition. */ /* Wake-up requested during system sleep transition. */
pm_runtime_put_noidle(dev); pm_runtime_put_sync(dev);
error = -EBUSY; error = -EBUSY;
} else { } else {
error = device_prepare(dev, state); error = device_prepare(dev, state);
......
...@@ -84,6 +84,19 @@ static int __pm_runtime_idle(struct device *dev) ...@@ -84,6 +84,19 @@ static int __pm_runtime_idle(struct device *dev)
dev->bus->pm->runtime_idle(dev); 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); spin_lock_irq(&dev->power.lock);
} }
...@@ -192,6 +205,22 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) ...@@ -192,6 +205,22 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
retval = dev->bus->pm->runtime_suspend(dev); retval = dev->bus->pm->runtime_suspend(dev);
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); spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval; dev->power.runtime_error = retval;
} else { } else {
...@@ -357,6 +386,22 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) ...@@ -357,6 +386,22 @@ int __pm_runtime_resume(struct device *dev, bool from_wq)
retval = dev->bus->pm->runtime_resume(dev); retval = dev->bus->pm->runtime_resume(dev);
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); spin_lock_irq(&dev->power.lock);
dev->power.runtime_error = retval; dev->power.runtime_error = retval;
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment