aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-06-25 14:02:35 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 17:35:38 -0400
commitff2f07874362d34684296f2bd5547a099f33c6d4 (patch)
treef41b8c3fcdd0556f4542058433d82f260928727d /drivers/usb/core
parentee0b9be829803e3ff5adec7456bd59a08425ffa1 (diff)
USB: fix race between root-hub wakeup & controller suspend
This patch (as1395) adds code to hcd_pci_suspend() for handling wakeup races. This is another general race pattern, similar to the "open vs. unregister" race we're all familiar with. Here, the race is between suspending a device and receiving a wakeup request from one of the device's suspended children. In particular, if a root-hub wakeup is requested at about the same time as the corresponding USB controller is suspended, and if the controller is enabled for wakeup, then the controller should either fail to suspend or else wake right back up again. During system sleep this won't happen very much, especially since host controllers generally aren't enabled for wakeup during sleep. However it is definitely an issue for runtime PM. Something like this will be needed to prevent the controller from autosuspending while waiting for a root-hub resume to take place. (That is, in fact, the common case, for which there is an extra test.) Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hcd-pci.c12
-rw-r--r--drivers/usb/core/hcd.c5
2 files changed, 16 insertions, 1 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index e387e394f876..352577baa53d 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -388,8 +388,20 @@ static int hcd_pci_suspend(struct device *dev)
388 if (hcd->driver->pci_suspend) { 388 if (hcd->driver->pci_suspend) {
389 bool do_wakeup = device_may_wakeup(dev); 389 bool do_wakeup = device_may_wakeup(dev);
390 390
391 /* Optimization: Don't suspend if a root-hub wakeup is
392 * pending and it would cause the HCD to wake up anyway.
393 */
394 if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
395 return -EBUSY;
391 retval = hcd->driver->pci_suspend(hcd, do_wakeup); 396 retval = hcd->driver->pci_suspend(hcd, do_wakeup);
392 suspend_report_result(hcd->driver->pci_suspend, retval); 397 suspend_report_result(hcd->driver->pci_suspend, retval);
398
399 /* Check again in case wakeup raced with pci_suspend */
400 if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
401 if (hcd->driver->pci_resume)
402 hcd->driver->pci_resume(hcd, false);
403 retval = -EBUSY;
404 }
393 if (retval) 405 if (retval)
394 return retval; 406 return retval;
395 } 407 }
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index f2fe7c8e991d..0358c05e6e8a 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1940,6 +1940,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
1940 1940
1941 dev_dbg(&rhdev->dev, "usb %s%s\n", 1941 dev_dbg(&rhdev->dev, "usb %s%s\n",
1942 (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); 1942 (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
1943 clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
1943 if (!hcd->driver->bus_resume) 1944 if (!hcd->driver->bus_resume)
1944 return -ENOENT; 1945 return -ENOENT;
1945 if (hcd->state == HC_STATE_RUNNING) 1946 if (hcd->state == HC_STATE_RUNNING)
@@ -1993,8 +1994,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
1993 unsigned long flags; 1994 unsigned long flags;
1994 1995
1995 spin_lock_irqsave (&hcd_root_hub_lock, flags); 1996 spin_lock_irqsave (&hcd_root_hub_lock, flags);
1996 if (hcd->rh_registered) 1997 if (hcd->rh_registered) {
1998 set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
1997 queue_work(pm_wq, &hcd->wakeup_work); 1999 queue_work(pm_wq, &hcd->wakeup_work);
2000 }
1998 spin_unlock_irqrestore (&hcd_root_hub_lock, flags); 2001 spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
1999} 2002}
2000EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); 2003EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);