aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-04-03 15:24:18 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-09 18:43:21 -0400
commit879d38e6bc36d73b0ac40ec9b0d839fda9fa8b1a (patch)
treec3215008071631abac7e3a8c6972610de3d2a31e /drivers
parentc5d703dcc776cb542b41665f2b7e2ba054efb4a7 (diff)
USB: fix race between root-hub suspend and remote wakeup
This patch (as1533) fixes a race between root-hub suspend and remote wakeup. If a wakeup event occurs while a root hub is suspending, it might not cause the suspend to fail. Although the host controller drivers check for pending wakeup events at the start of their bus_suspend routines, they generally do not check for wakeup events while the routines are running. In addition, if a wakeup event occurs any time after khubd is frozen and before the root hub is fully suspended, it might not cause a system sleep transition to fail. For example, the host controller drivers do not fail root-hub suspends when a connect-change event is pending. To fix both these issues, this patch causes hcd_bus_suspend() to query the controller driver's hub_status_data method after a root hub is suspended, if the root hub is enabled for wakeup. Any pending status changes will count as wakeup events, causing the root hub to be resumed and the overall suspend to fail with -EBUSY. A significant point is that not all events are reflected immediately in the status bits. Both EHCI and UHCI controllers notify the CPU when remote wakeup begins on a port, but the port's suspend-change status bit doesn't get set until after the port has completed the transition out of the suspend state, some 25 milliseconds later. Consequently, the patch will interpret any nonzero return value from hub_status_data as indicating a pending event, even if none of the status bits are set in the data buffer. Follow-up patches make the necessary changes to ehci-hcd and uhci-hcd. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> CC: Sarah Sharp <sarah.a.sharp@linux.intel.com> CC: Chen Peter-B29397 <B29397@freescale.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hcd.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 9d7fc9a39933..140d3e11f212 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1978,6 +1978,18 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
1978 if (status == 0) { 1978 if (status == 0) {
1979 usb_set_device_state(rhdev, USB_STATE_SUSPENDED); 1979 usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
1980 hcd->state = HC_STATE_SUSPENDED; 1980 hcd->state = HC_STATE_SUSPENDED;
1981
1982 /* Did we race with a root-hub wakeup event? */
1983 if (rhdev->do_remote_wakeup) {
1984 char buffer[6];
1985
1986 status = hcd->driver->hub_status_data(hcd, buffer);
1987 if (status != 0) {
1988 dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n");
1989 hcd_bus_resume(rhdev, PMSG_AUTO_RESUME);
1990 status = -EBUSY;
1991 }
1992 }
1981 } else { 1993 } else {
1982 spin_lock_irq(&hcd_root_hub_lock); 1994 spin_lock_irq(&hcd_root_hub_lock);
1983 if (!HCD_DEAD(hcd)) { 1995 if (!HCD_DEAD(hcd)) {