Skip to content

Commit

Permalink
USB: EHCI: fix two new bugs related to Clear-TT-Buffer
Browse files Browse the repository at this point in the history
This patch (as1273) fixes two(!) bugs introduced by the new
Clear-TT-Buffer implementation in ehci-hcd.

	It is now possible for an idle QH to have some URBs on its
	queue -- this will happen if a Clear-TT-Buffer is pending for
	the QH's endpoint.  Consequently we should not issue a warning
	when someone tries to unlink an URB from an idle QH; instead
	we should process the request immediately.

	The refcounts for QHs could get messed up, because
	submit_async() would increment the refcount when calling
	qh_link_async() and qh_link_async() would then refuse to link
	the QH into the schedule if a Clear-TT-Buffer was pending.
	Instead we should increment the refcount only when the QH
	actually is added to the schedule.  The current code tries to
	be clever by leaving the refcount alone if an unlink is
	immediately followed by a relink; the patch changes this to an
	unconditional decrement and increment (although they occur in
	the opposite order).

Signed-off-by: Alan Stern <[email protected]>
CC: David Brownell <[email protected]>
Tested-by: Manuel Lauss <[email protected]>
Tested-by: Matthijs Kooijman <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
AlanStern authored and gregkh committed Aug 7, 2009
1 parent 01105a2 commit 7a0f0d9
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 4 deletions.
3 changes: 2 additions & 1 deletion drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,8 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
/* already started */
break;
case QH_STATE_IDLE:
WARN_ON(1);
/* QH might be waiting for a Clear-TT-Buffer */
qh_completions(ehci, qh);
break;
}
break;
Expand Down
6 changes: 3 additions & 3 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
head->qh_next.qh = qh;
head->hw_next = dma;

qh_get(qh);
qh->xacterrs = QH_XACTERR_MAX;
qh->qh_state = QH_STATE_LINKED;
/* qtd completions reported later by interrupt */
Expand Down Expand Up @@ -1080,7 +1081,7 @@ submit_async (
* the HC and TT handle it when the TT has a buffer ready.
*/
if (likely (qh->qh_state == QH_STATE_IDLE))
qh_link_async (ehci, qh_get (qh));
qh_link_async(ehci, qh);
done:
spin_unlock_irqrestore (&ehci->lock, flags);
if (unlikely (qh == NULL))
Expand Down Expand Up @@ -1115,15 +1116,14 @@ static void end_unlink_async (struct ehci_hcd *ehci)
&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
qh_link_async (ehci, qh);
else {
qh_put (qh); // refcount from async list

/* it's not free to turn the async schedule on/off; leave it
* active but idle for a while once it empties.
*/
if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)
&& ehci->async->qh_next.qh == NULL)
timer_action (ehci, TIMER_ASYNC_OFF);
}
qh_put(qh); /* refcount from async list */

if (next) {
ehci->reclaim = NULL;
Expand Down

0 comments on commit 7a0f0d9

Please sign in to comment.