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

USB: use the runtime-PM autosuspend implementation

This patch (as1428) converts USB over to the new runtime-PM core
autosuspend framework.  One slightly awkward aspect of the conversion
is that USB devices will now have two suspend-delay attributes: the
old power/autosuspend file and the new power/autosuspend_delay_ms
file.  One expresses the delay time in seconds and the other in
milliseconds, but otherwise they do the same thing.  The old attribute
can be deprecated and then removed eventually.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6ddf27cd
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Alan Stern <stern@rowland.harvard.edu> Alan Stern <stern@rowland.harvard.edu>
December 11, 2009 October 28, 2010
...@@ -107,9 +107,14 @@ allowed to issue dynamic suspends. ...@@ -107,9 +107,14 @@ allowed to issue dynamic suspends.
The user interface for controlling dynamic PM is located in the power/ The user interface for controlling dynamic PM is located in the power/
subdirectory of each USB device's sysfs directory, that is, in subdirectory of each USB device's sysfs directory, that is, in
/sys/bus/usb/devices/.../power/ where "..." is the device's ID. The /sys/bus/usb/devices/.../power/ where "..." is the device's ID. The
relevant attribute files are: wakeup, control, and autosuspend. relevant attribute files are: wakeup, control, and
(There may also be a file named "level"; this file was deprecated autosuspend_delay_ms. (There may also be a file named "level"; this
as of the 2.6.35 kernel and replaced by the "control" file.) file was deprecated as of the 2.6.35 kernel and replaced by the
"control" file. In 2.6.38 the "autosuspend" file will be deprecated
and replaced by the "autosuspend_delay_ms" file. The only difference
is that the newer file expresses the delay in milliseconds whereas the
older file uses seconds. Confusingly, both files are present in 2.6.37
but only "autosuspend" works.)
power/wakeup power/wakeup
...@@ -140,33 +145,36 @@ as of the 2.6.35 kernel and replaced by the "control" file.) ...@@ -140,33 +145,36 @@ as of the 2.6.35 kernel and replaced by the "control" file.)
suspended and autoresume was not allowed. This suspended and autoresume was not allowed. This
setting is no longer supported.) setting is no longer supported.)
power/autosuspend power/autosuspend_delay_ms
This file contains an integer value, which is the This file contains an integer value, which is the
number of seconds the device should remain idle before number of milliseconds the device should remain idle
the kernel will autosuspend it (the idle-delay time). before the kernel will autosuspend it (the idle-delay
The default is 2. 0 means to autosuspend as soon as time). The default is 2000. 0 means to autosuspend
the device becomes idle, and negative values mean as soon as the device becomes idle, and negative
never to autosuspend. You can write a number to the values mean never to autosuspend. You can write a
file to change the autosuspend idle-delay time. number to the file to change the autosuspend
idle-delay time.
Writing "-1" to power/autosuspend and writing "on" to power/control do
essentially the same thing -- they both prevent the device from being Writing "-1" to power/autosuspend_delay_ms and writing "on" to
autosuspended. Yes, this is a redundancy in the API. power/control do essentially the same thing -- they both prevent the
device from being autosuspended. Yes, this is a redundancy in the
API.
(In 2.6.21 writing "0" to power/autosuspend would prevent the device (In 2.6.21 writing "0" to power/autosuspend would prevent the device
from being autosuspended; the behavior was changed in 2.6.22. The from being autosuspended; the behavior was changed in 2.6.22. The
power/autosuspend attribute did not exist prior to 2.6.21, and the power/autosuspend attribute did not exist prior to 2.6.21, and the
power/level attribute did not exist prior to 2.6.22. power/control power/level attribute did not exist prior to 2.6.22. power/control
was added in 2.6.34.) was added in 2.6.34, and power/autosuspend_delay_ms was added in
2.6.37 but did not become functional until 2.6.38.)
Changing the default idle-delay time Changing the default idle-delay time
------------------------------------ ------------------------------------
The default autosuspend idle-delay time is controlled by a module The default autosuspend idle-delay time (in seconds) is controlled by
parameter in usbcore. You can specify the value when usbcore is a module parameter in usbcore. You can specify the value when usbcore
loaded. For example, to set it to 5 seconds instead of 2 you would is loaded. For example, to set it to 5 seconds instead of 2 you would
do: do:
modprobe usbcore autosuspend=5 modprobe usbcore autosuspend=5
...@@ -234,25 +242,23 @@ every device. ...@@ -234,25 +242,23 @@ every device.
If a driver knows that its device has proper suspend/resume support, If a driver knows that its device has proper suspend/resume support,
it can enable autosuspend all by itself. For example, the video it can enable autosuspend all by itself. For example, the video
driver for a laptop's webcam might do this, since these devices are driver for a laptop's webcam might do this (in recent kernels they
rarely used and so should normally be autosuspended. do), since these devices are rarely used and so should normally be
autosuspended.
Sometimes it turns out that even when a device does work okay with Sometimes it turns out that even when a device does work okay with
autosuspend there are still problems. For example, there are autosuspend there are still problems. For example, the usbhid driver,
experimental patches adding autosuspend support to the usbhid driver, which manages keyboards and mice, has autosuspend support. Tests with
which manages keyboards and mice, among other things. Tests with a a number of keyboards show that typing on a suspended keyboard, while
number of keyboards showed that typing on a suspended keyboard, while causing the keyboard to do a remote wakeup all right, will nonetheless
causing the keyboard to do a remote wakeup all right, would frequently result in lost keystrokes. Tests with mice show that some
nonetheless frequently result in lost keystrokes. Tests with mice of them will issue a remote-wakeup request in response to button
showed that some of them would issue a remote-wakeup request in presses but not to motion, and some in response to neither.
response to button presses but not to motion, and some in response to
neither.
The kernel will not prevent you from enabling autosuspend on devices The kernel will not prevent you from enabling autosuspend on devices
that can't handle it. It is even possible in theory to damage a that can't handle it. It is even possible in theory to damage a
device by suspending it at the wrong time -- for example, suspending a device by suspending it at the wrong time. (Highly unlikely, but
USB hard disk might cause it to spin down without parking the heads. possible.) Take care.
(Highly unlikely, but possible.) Take care.
The driver interface for Power Management The driver interface for Power Management
...@@ -336,10 +342,6 @@ autosuspend the interface's device. When the usage counter is = 0 ...@@ -336,10 +342,6 @@ autosuspend the interface's device. When the usage counter is = 0
then the interface is considered to be idle, and the kernel may then the interface is considered to be idle, and the kernel may
autosuspend the device. autosuspend the device.
(There is a similar usage counter field in struct usb_device,
associated with the device itself rather than any of its interfaces.
This counter is used only by the USB core.)
Drivers need not be concerned about balancing changes to the usage Drivers need not be concerned about balancing changes to the usage
counter; the USB core will undo any remaining "get"s when a driver counter; the USB core will undo any remaining "get"s when a driver
is unbound from its interface. As a corollary, drivers must not call is unbound from its interface. As a corollary, drivers must not call
...@@ -409,11 +411,11 @@ during autosuspend. For example, there's not much point ...@@ -409,11 +411,11 @@ during autosuspend. For example, there's not much point
autosuspending a keyboard if the user can't cause the keyboard to do a autosuspending a keyboard if the user can't cause the keyboard to do a
remote wakeup by typing on it. If the driver sets remote wakeup by typing on it. If the driver sets
intf->needs_remote_wakeup to 1, the kernel won't autosuspend the intf->needs_remote_wakeup to 1, the kernel won't autosuspend the
device if remote wakeup isn't available or has been disabled through device if remote wakeup isn't available. (If the device is already
the power/wakeup attribute. (If the device is already autosuspended, autosuspended, though, setting this flag won't cause the kernel to
though, setting this flag won't cause the kernel to autoresume it. autoresume it. Normally a driver would set this flag in its probe
Normally a driver would set this flag in its probe method, at which method, at which time the device is guaranteed not to be
time the device is guaranteed not to be autosuspended.) autosuspended.)
If a driver does its I/O asynchronously in interrupt context, it If a driver does its I/O asynchronously in interrupt context, it
should call usb_autopm_get_interface_async() before starting output and should call usb_autopm_get_interface_async() before starting output and
...@@ -422,20 +424,19 @@ it receives an input event, it should call ...@@ -422,20 +424,19 @@ it receives an input event, it should call
usb_mark_last_busy(struct usb_device *udev); usb_mark_last_busy(struct usb_device *udev);
in the event handler. This sets udev->last_busy to the current time. in the event handler. This tells the PM core that the device was just
udev->last_busy is the field used for idle-delay calculations; busy and therefore the next autosuspend idle-delay expiration should
updating it will cause any pending autosuspend to be moved back. Most be pushed back. Many of the usb_autopm_* routines also make this call,
of the usb_autopm_* routines will also set the last_busy field to the so drivers need to worry only when interrupt-driven input arrives.
current time.
Asynchronous operation is always subject to races. For example, a Asynchronous operation is always subject to races. For example, a
driver may call one of the usb_autopm_*_interface_async() routines at driver may call the usb_autopm_get_interface_async() routine at a time
a time when the core has just finished deciding the device has been when the core has just finished deciding the device has been idle for
idle for long enough but not yet gotten around to calling the driver's long enough but not yet gotten around to calling the driver's suspend
suspend method. The suspend method must be responsible for method. The suspend method must be responsible for synchronizing with
synchronizing with the output request routine and the URB completion the I/O request routine and the URB completion handler; it should
handler; it should cause autosuspends to fail with -EBUSY if the cause autosuspends to fail with -EBUSY if the driver needs to use the
driver needs to use the device. device.
External suspend calls should never be allowed to fail in this way, External suspend calls should never be allowed to fail in this way,
only autosuspend calls. The driver can tell them apart by checking only autosuspend calls. The driver can tell them apart by checking
...@@ -472,7 +473,9 @@ Firstly, a device may already be autosuspended when a system suspend ...@@ -472,7 +473,9 @@ Firstly, a device may already be autosuspended when a system suspend
occurs. Since system suspends are supposed to be as transparent as occurs. Since system suspends are supposed to be as transparent as
possible, the device should remain suspended following the system possible, the device should remain suspended following the system
resume. But this theory may not work out well in practice; over time resume. But this theory may not work out well in practice; over time
the kernel's behavior in this regard has changed. the kernel's behavior in this regard has changed. As of 2.6.37 the
policy is to resume all devices during a system resume and let them
handle their own runtime suspends afterward.
Secondly, a dynamic power-management event may occur as a system Secondly, a dynamic power-management event may occur as a system
suspend is underway. The window for this is short, since system suspend is underway. The window for this is short, since system
......
...@@ -1397,32 +1397,7 @@ void usb_autosuspend_device(struct usb_device *udev) ...@@ -1397,32 +1397,7 @@ void usb_autosuspend_device(struct usb_device *udev)
int status; int status;
usb_mark_last_busy(udev); usb_mark_last_busy(udev);
status = pm_runtime_put_sync(&udev->dev); status = pm_runtime_put_sync_autosuspend(&udev->dev);
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&udev->dev.power.usage_count),
status);
}
/**
* usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
*
* This routine should be called when a core subsystem thinks @udev may
* be ready to autosuspend.
*
* @udev's usage counter left unchanged. If it is 0 and all the interfaces
* are inactive then an autosuspend will be attempted. The attempt may
* fail or be delayed.
*
* The caller must hold @udev's device lock.
*
* This routine can run only in process context.
*/
void usb_try_autosuspend_device(struct usb_device *udev)
{
int status;
status = pm_runtime_idle(&udev->dev);
dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&udev->dev.power.usage_count), __func__, atomic_read(&udev->dev.power.usage_count),
status); status);
...@@ -1508,32 +1483,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); ...@@ -1508,32 +1483,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
void usb_autopm_put_interface_async(struct usb_interface *intf) void usb_autopm_put_interface_async(struct usb_interface *intf)
{ {
struct usb_device *udev = interface_to_usbdev(intf); struct usb_device *udev = interface_to_usbdev(intf);
unsigned long last_busy; int status;
int status = 0;
last_busy = udev->dev.power.last_busy;
usb_mark_last_busy(udev); usb_mark_last_busy(udev);
atomic_dec(&intf->pm_usage_cnt); atomic_dec(&intf->pm_usage_cnt);
pm_runtime_put_noidle(&intf->dev); status = pm_runtime_put(&intf->dev);
if (udev->dev.power.runtime_auto) {
/* Optimization: Don't schedule a delayed autosuspend if
* the timer is already running and the expiration time
* wouldn't change.
*
* We have to use the interface's timer. Attempts to
* schedule a suspend for the device would fail because
* the interface is still active.
*/
if (intf->dev.power.timer_expires == 0 ||
round_jiffies_up(last_busy) !=
round_jiffies_up(jiffies)) {
status = pm_schedule_suspend(&intf->dev,
jiffies_to_msecs(
round_jiffies_up_relative(
udev->autosuspend_delay)));
}
}
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&intf->dev.power.usage_count), __func__, atomic_read(&intf->dev.power.usage_count),
status); status);
...@@ -1651,7 +1605,6 @@ static int autosuspend_check(struct usb_device *udev) ...@@ -1651,7 +1605,6 @@ static int autosuspend_check(struct usb_device *udev)
{ {
int w, i; int w, i;
struct usb_interface *intf; struct usb_interface *intf;
unsigned long suspend_time, j;
/* Fail if autosuspend is disabled, or any interfaces are in use, or /* Fail if autosuspend is disabled, or any interfaces are in use, or
* any interface drivers require remote wakeup but it isn't available. * any interface drivers require remote wakeup but it isn't available.
...@@ -1691,17 +1644,6 @@ static int autosuspend_check(struct usb_device *udev) ...@@ -1691,17 +1644,6 @@ static int autosuspend_check(struct usb_device *udev)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
udev->do_remote_wakeup = w; udev->do_remote_wakeup = w;
/* If everything is okay but the device hasn't been idle for long
* enough, queue a delayed autosuspend request.
*/
j = ACCESS_ONCE(jiffies);
suspend_time = udev->dev.power.last_busy + udev->autosuspend_delay;
if (time_before(j, suspend_time)) {
pm_schedule_suspend(&udev->dev, jiffies_to_msecs(
round_jiffies_up_relative(suspend_time - j)));
return -EAGAIN;
}
return 0; return 0;
} }
...@@ -1719,17 +1661,8 @@ static int usb_runtime_suspend(struct device *dev) ...@@ -1719,17 +1661,8 @@ static int usb_runtime_suspend(struct device *dev)
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
/* If an interface fails the suspend, adjust the last_busy
* time so that we don't get another suspend attempt right
* away.
*/
if (status) {
udev->dev.power.last_busy = jiffies +
(udev->autosuspend_delay == 0 ? HZ/2 : 0);
}
/* Prevent the parent from suspending immediately after */ /* Prevent the parent from suspending immediately after */
else if (udev->parent) if (status == 0 && udev->parent)
usb_mark_last_busy(udev->parent); usb_mark_last_busy(udev->parent);
return status; return status;
...@@ -1756,7 +1689,7 @@ static int usb_runtime_idle(struct device *dev) ...@@ -1756,7 +1689,7 @@ static int usb_runtime_idle(struct device *dev)
* autosuspend checks. * autosuspend checks.
*/ */
if (autosuspend_check(udev) == 0) if (autosuspend_check(udev) == 0)
pm_runtime_suspend(dev); pm_runtime_autosuspend(dev);
return 0; return 0;
} }
......
...@@ -1803,6 +1803,7 @@ int usb_new_device(struct usb_device *udev) ...@@ -1803,6 +1803,7 @@ int usb_new_device(struct usb_device *udev)
/* Tell the runtime-PM framework the device is active */ /* Tell the runtime-PM framework the device is active */
pm_runtime_set_active(&udev->dev); pm_runtime_set_active(&udev->dev);
pm_runtime_use_autosuspend(&udev->dev);
pm_runtime_enable(&udev->dev); pm_runtime_enable(&udev->dev);
err = usb_enumerate_device(udev); /* Read descriptors */ err = usb_enumerate_device(udev); /* Read descriptors */
......
...@@ -124,12 +124,6 @@ void usb_detect_quirks(struct usb_device *udev) ...@@ -124,12 +124,6 @@ void usb_detect_quirks(struct usb_device *udev)
*/ */
usb_disable_autosuspend(udev); usb_disable_autosuspend(udev);
/* Autosuspend can also be disabled if the initial autosuspend_delay
* is negative.
*/
if (udev->autosuspend_delay < 0)
usb_autoresume_device(udev);
#endif #endif
/* For the present, all devices default to USB-PERSIST enabled */ /* For the present, all devices default to USB-PERSIST enabled */
......
...@@ -334,44 +334,20 @@ static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL); ...@@ -334,44 +334,20 @@ static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL);
static ssize_t static ssize_t
show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct usb_device *udev = to_usb_device(dev); return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000);
return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
} }
static ssize_t static ssize_t
set_autosuspend(struct device *dev, struct device_attribute *attr, set_autosuspend(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct usb_device *udev = to_usb_device(dev); int value;
int value, old_delay;
int rc;
if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ || if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/1000 ||
value <= - INT_MAX/HZ) value <= -INT_MAX/1000)
return -EINVAL; return -EINVAL;
value *= HZ;
usb_lock_device(udev);
old_delay = udev->autosuspend_delay;
udev->autosuspend_delay = value;
if (old_delay < 0) { /* Autosuspend wasn't allowed */
if (value >= 0)
usb_autosuspend_device(udev);
} else { /* Autosuspend was allowed */
if (value < 0) {
rc = usb_autoresume_device(udev);
if (rc < 0) {
count = rc;
udev->autosuspend_delay = old_delay;
}
} else {
usb_try_autosuspend_device(udev);
}
}
usb_unlock_device(udev); pm_runtime_set_autosuspend_delay(dev, value * 1000);
return count; return count;
} }
......
...@@ -445,7 +445,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, ...@@ -445,7 +445,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
INIT_LIST_HEAD(&dev->filelist); INIT_LIST_HEAD(&dev->filelist);
#ifdef CONFIG_PM #ifdef CONFIG_PM
dev->autosuspend_delay = usb_autosuspend_delay * HZ; pm_runtime_set_autosuspend_delay(&dev->dev,
usb_autosuspend_delay * 1000);
dev->connect_time = jiffies; dev->connect_time = jiffies;
dev->active_duration = -jiffies; dev->active_duration = -jiffies;
#endif #endif
......
...@@ -75,14 +75,12 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg) ...@@ -75,14 +75,12 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
#ifdef CONFIG_USB_SUSPEND #ifdef CONFIG_USB_SUSPEND
extern void usb_autosuspend_device(struct usb_device *udev); extern void usb_autosuspend_device(struct usb_device *udev);
extern void usb_try_autosuspend_device(struct usb_device *udev);
extern int usb_autoresume_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev);
extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_remote_wakeup(struct usb_device *dev);
#else #else
#define usb_autosuspend_device(udev) do {} while (0) #define usb_autosuspend_device(udev) do {} while (0)
#define usb_try_autosuspend_device(udev) do {} while (0)
static inline int usb_autoresume_device(struct usb_device *udev) static inline int usb_autoresume_device(struct usb_device *udev)
{ {
return 0; return 0;
......
...@@ -408,7 +408,6 @@ struct usb_tt; ...@@ -408,7 +408,6 @@ struct usb_tt;
* @quirks: quirks of the whole device * @quirks: quirks of the whole device
* @urbnum: number of URBs submitted for the whole device * @urbnum: number of URBs submitted for the whole device
* @active_duration: total time device is not suspended * @active_duration: total time device is not suspended
* @autosuspend_delay: in jiffies
* @connect_time: time device was first connected * @connect_time: time device was first connected
* @do_remote_wakeup: remote wakeup should be enabled * @do_remote_wakeup: remote wakeup should be enabled
* @reset_resume: needs reset instead of resume * @reset_resume: needs reset instead of resume
...@@ -481,7 +480,6 @@ struct usb_device { ...@@ -481,7 +480,6 @@ struct usb_device {
unsigned long active_duration; unsigned long active_duration;
#ifdef CONFIG_PM #ifdef CONFIG_PM
int autosuspend_delay;
unsigned long connect_time; unsigned long connect_time;
unsigned do_remote_wakeup:1; unsigned do_remote_wakeup:1;
......
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