Skip to content

Commit

Permalink
USB: EHCI & UHCI: fix race between root-hub suspend and port resume
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Jan 20, 2010
1 parent 1b9a38b commit cec3a53
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 2 deletions.
20 changes: 19 additions & 1 deletion drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,26 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
del_timer_sync(&ehci->watchdog);
del_timer_sync(&ehci->iaa_watchdog);

port = HCS_N_PORTS (ehci->hcs_params);
spin_lock_irq (&ehci->lock);

/* 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.
*/
if (hcd->self.root_hub->do_remote_wakeup) {
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (ehci->reset_done[port] != 0) {
spin_unlock_irq(&ehci->lock);
ehci_dbg(ehci, "suspend failed because "
"port %d is resuming\n",
port + 1);
return -EBUSY;
}
}
}

/* stop schedules, clean any completed work */
if (HC_IS_RUNNING(hcd->state)) {
ehci_quiesce (ehci);
Expand All @@ -138,6 +155,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
*/
ehci->bus_suspended = 0;
ehci->owned_ports = 0;
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
Expand Down
15 changes: 14 additions & 1 deletion drivers/usb/host/uhci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit cec3a53

Please sign in to comment.