diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2010-01-08 17:18:20 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-01-21 00:24:34 +0100 |
commit | cec3a53c7fe794237b582e8e77fc0e48465e65ee (patch) | |
tree | bc73dbdef0688d6c656d04f288e736cb9b60d9b9 /drivers/usb/host/uhci-hcd.c | |
parent | USB: EHCI: fix handling of unusual interrupt intervals (diff) | |
download | linux-cec3a53c7fe794237b582e8e77fc0e48465e65ee.tar.xz linux-cec3a53c7fe794237b582e8e77fc0e48465e65ee.zip |
USB: EHCI & UHCI: fix race between root-hub suspend and port resume
This patch (as1321) fixes a problem with EHCI and UHCI root-hub
suspends: If the suspend occurs while a port is trying to resume, the
resume doesn't finish and simply gets lost. When remote wakeup is
enabled, this is undesirable behavior.
The patch checks first to see if any port resumes are in progress, and
if they are then it fails the root-hub suspend with -EBUSY.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to '')
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 15 |
1 files changed, 14 insertions, 1 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 5cd0e48f67fb..99cd00fd3514 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -749,7 +749,20 @@ static int uhci_rh_suspend(struct usb_hcd *hcd) spin_lock_irq(&uhci->lock); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) rc = -ESHUTDOWN; - else if (!uhci->dead) + else if (uhci->dead) + ; /* Dead controllers tell no tales */ + + /* Once the controller is stopped, port resumes that are already + * in progress won't complete. Hence if remote wakeup is enabled + * for the root hub and any ports are in the middle of a resume or + * remote wakeup, we must fail the suspend. + */ + else if (hcd->self.root_hub->do_remote_wakeup && + uhci->resuming_ports) { + dev_dbg(uhci_dev(uhci), "suspend failed because a port " + "is resuming\n"); + rc = -EBUSY; + } else suspend_rh(uhci, UHCI_RH_SUSPENDED); spin_unlock_irq(&uhci->lock); return rc; |