Skip to content

Commit

Permalink
USB: EHCI: fix regression during bus resume
Browse files Browse the repository at this point in the history
This patch (as1663) fixes a regression caused by commit
6e0c333 (USB: EHCI: unlink one async
QH at a time).  In order to avoid keeping multiple QHs in an unusable
intermediate state, that commit changed unlink_empty_async() so that
it unlinks only one empty QH at a time.

However, when the EHCI root hub is suspended, _all_ async QHs need to
be unlinked.  ehci_bus_suspend() used to do this by calling
unlink_empty_async(), but now this only unlinks one of the QHs, not
all of them.

The symptom is that when the root hub is resumed, USB communications
don't work for some period of time.  This is because ehci-hcd doesn't
realize it needs to restart the async schedule; it assumes that
because some QHs are already on the schedule, the schedule must be
running.

The easiest way to fix the problem is add a new function that unlinks
all the async QHs when the root hub is suspended.

This patch should be applied to all kernels that have the 6e0c333
commit.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-and-tested-by: Adrian Bassett <adrian.bassett@hotmail.co.uk>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Mar 15, 2013
1 parent 29f86e6 commit 2a40f32
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)

static void end_unlink_async(struct ehci_hcd *ehci);
static void unlink_empty_async(struct ehci_hcd *ehci);
static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
static void ehci_work(struct ehci_hcd *ehci);
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
ehci->rh_state = EHCI_RH_SUSPENDED;

end_unlink_async(ehci);
unlink_empty_async(ehci);
unlink_empty_async_suspended(ehci);
ehci_handle_intr_unlinks(ehci);
end_free_itds(ehci);

Expand Down
13 changes: 13 additions & 0 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,19 @@ static void unlink_empty_async(struct ehci_hcd *ehci)
}
}

/* The root hub is suspended; unlink all the async QHs */
static void unlink_empty_async_suspended(struct ehci_hcd *ehci)
{
struct ehci_qh *qh;

while (ehci->async->qh_next.qh) {
qh = ehci->async->qh_next.qh;
WARN_ON(!list_empty(&qh->qtd_list));
single_unlink_async(ehci, qh);
}
start_iaa_cycle(ehci, false);
}

/* makes sure the async qh will become idle */
/* caller must own ehci->lock */

Expand Down

0 comments on commit 2a40f32

Please sign in to comment.