| From 886486b792e4f6f96d4fbe8ec5bf20811cab7d6a Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Thu, 3 Nov 2011 23:39:18 +0100 |
| Subject: PM / Runtime: Automatically retry failed autosuspends |
| |
| From: Alan Stern <stern@rowland.harvard.edu> |
| |
| commit 886486b792e4f6f96d4fbe8ec5bf20811cab7d6a 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 |
| 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> |
| Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| Documentation/power/runtime_pm.txt | 10 ++++++++++ |
| drivers/base/power/runtime.c | 18 ++++++++++++++++-- |
| 2 files changed, 26 insertions(+), 2 deletions(-) |
| |
| --- a/Documentation/power/runtime_pm.txt |
| +++ b/Documentation/power/runtime_pm.txt |
| @@ -782,6 +782,16 @@ will behave normally, not taking the aut |
| 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. |
| --- a/drivers/base/power/runtime.c |
| +++ b/drivers/base/power/runtime.c |
| @@ -285,6 +285,9 @@ static int rpm_callback(int (*cb)(struct |
| * 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 |
| * 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. |
| */ |
| @@ -396,10 +399,21 @@ static int rpm_suspend(struct device *de |
| 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); |
| + } |
| } else { |
| no_callback: |
| __update_runtime_status(dev, RPM_SUSPENDED); |