Commit 8dc9c791 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

PM / Runtime: Automatically retry failed autosuspends

commit 886486b7 upstream.

Originally, the runtime PM core would send an idle notification
whenever a suspend attempt failed.  The idle callback routine could
then schedule a delayed suspend for some time later.

However this behavior was changed by commit
f71648d7 (PM / Runtime: Remove idle
notification after failing suspend).  No notifications were sent, and
there was no clear mechanism to retry failed suspends.

This caused problems for the usbhid driver, because it fails
autosuspend attempts as long as a key is being held down.  Therefore
this patch (as1492) adds a mechanism for retrying failed
autosuspends.  If the callback routine updates the last_busy field so
that the next autosuspend expiration time is in the future, the
autosuspend will automatically be rescheduled.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Tested-by: default avatarHenrik Rydberg <rydberg@euromail.se>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 2d92f691
...@@ -708,6 +708,16 @@ will behave normally, not taking the autosuspend delay into account. ...@@ -708,6 +708,16 @@ will behave normally, not taking the autosuspend delay into account.
Similarly, if the power.use_autosuspend field isn't set then the autosuspend Similarly, if the power.use_autosuspend field isn't set then the autosuspend
helper functions will behave just like the non-autosuspend counterparts. helper functions will behave just like the non-autosuspend counterparts.
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. The implementation is well suited for asynchronous use in interrupt contexts.
However such use inevitably involves races, because the PM core can't However such use inevitably involves races, because the PM core can't
synchronize ->runtime_suspend() callbacks with the arrival of I/O requests. synchronize ->runtime_suspend() callbacks with the arrival of I/O requests.
......
...@@ -278,6 +278,9 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) ...@@ -278,6 +278,9 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
* If a deferred resume was requested while the callback was running then carry * If a deferred resume was requested while the callback was running then carry
* it out; otherwise send an idle notification for the device (if the suspend * it out; otherwise send an idle notification for the device (if the suspend
* failed) or for its parent (if the suspend succeeded). * failed) or for its parent (if the suspend succeeded).
* If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO
* flag is set and the next autosuspend-delay expiration time is in the
* future, schedule another autosuspend attempt.
* *
* This function must be called under dev->power.lock with interrupts disabled. * This function must be called under dev->power.lock with interrupts disabled.
*/ */
...@@ -389,10 +392,21 @@ static int rpm_suspend(struct device *dev, int rpmflags) ...@@ -389,10 +392,21 @@ static int rpm_suspend(struct device *dev, int rpmflags)
if (retval) { if (retval) {
__update_runtime_status(dev, RPM_ACTIVE); __update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = 0; dev->power.deferred_resume = 0;
if (retval == -EAGAIN || retval == -EBUSY) if (retval == -EAGAIN || retval == -EBUSY) {
dev->power.runtime_error = 0; dev->power.runtime_error = 0;
else
/*
* If the callback routine failed an autosuspend, and
* if the last_busy time has been updated so that there
* is a new autosuspend expiration time, automatically
* reschedule another autosuspend.
*/
if ((rpmflags & RPM_AUTO) &&
pm_runtime_autosuspend_expiration(dev) != 0)
goto repeat;
} else {
pm_runtime_cancel_pending(dev); pm_runtime_cancel_pending(dev);
}
} else { } else {
no_callback: no_callback:
__update_runtime_status(dev, RPM_SUSPENDED); __update_runtime_status(dev, RPM_SUSPENDED);
......
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