summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2011-11-03 23:39:18 +0100
committerRafael J. Wysocki <rjw@sisk.pl>2011-11-04 22:28:14 +0100
commit886486b792e4f6f96d4fbe8ec5bf20811cab7d6a (patch)
tree1c638e14492c16f8f69ca71fa93b2d81d8e4eeb6
parent6513fd6972f725291ee8ce62c7a39fb8a6c7391e (diff)
PM / Runtime: Automatically retry failed autosuspends
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 f71648d73c1650b8b4aceb3856bebbde6daa3b86 (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: Alan Stern <stern@rowland.harvard.edu> Tested-by: Henrik Rydberg <rydberg@euromail.se> Cc: <stable@kernel.org> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--Documentation/power/runtime_pm.txt10
-rw-r--r--drivers/base/power/runtime.c18
2 files changed, 26 insertions, 2 deletions
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt
index 0e856088db7c..5336149f831b 100644
--- a/Documentation/power/runtime_pm.txt
+++ b/Documentation/power/runtime_pm.txt
@@ -789,6 +789,16 @@ will behave normally, not taking the autosuspend delay into account.
Similarly, if the power.use_autosuspend field isn't set then the autosuspend
helper functions will behave just like the non-autosuspend counterparts.
+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.
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 18ef87e525fa..124dbf60c9bf 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -293,6 +293,9 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
* the callback was running then carry it out, otherwise send an idle
* notification for its parent (if the suspend succeeded and both
* ignore_children of parent->power and irq_safe of dev->power are not set).
+ * 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.
*/
@@ -413,10 +416,21 @@ static int rpm_suspend(struct device *dev, int rpmflags)
if (retval) {
__update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = false;
- if (retval == -EAGAIN || retval == -EBUSY)
+ if (retval == -EAGAIN || retval == -EBUSY) {
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);
+ }
wake_up_all(&dev->power.wait_queue);
goto out;
}