Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: EHCI: fix two new bugs related to Clear-TT-Buffer

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 <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Tested-by: Manuel Lauss <manuel.lauss@gmail.com>
Tested-by: Matthijs Kooijman <matthijs@stdin.nl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>



authored by

Alan Stern and committed by
Greg Kroah-Hartman
7a0f0d95 01105a24

+5 -4
+2 -1
drivers/usb/host/ehci-hcd.c
··· 903 903 /* already started */ 904 904 break; 905 905 case QH_STATE_IDLE: 906 - WARN_ON(1); 906 + /* QH might be waiting for a Clear-TT-Buffer */ 907 + qh_completions(ehci, qh); 907 908 break; 908 909 } 909 910 break;
+3 -3
drivers/usb/host/ehci-q.c
··· 940 940 head->qh_next.qh = qh; 941 941 head->hw_next = dma; 942 942 943 + qh_get(qh); 943 944 qh->xacterrs = QH_XACTERR_MAX; 944 945 qh->qh_state = QH_STATE_LINKED; 945 946 /* qtd completions reported later by interrupt */ ··· 1081 1080 * the HC and TT handle it when the TT has a buffer ready. 1082 1081 */ 1083 1082 if (likely (qh->qh_state == QH_STATE_IDLE)) 1084 - qh_link_async (ehci, qh_get (qh)); 1083 + qh_link_async(ehci, qh); 1085 1084 done: 1086 1085 spin_unlock_irqrestore (&ehci->lock, flags); 1087 1086 if (unlikely (qh == NULL)) ··· 1116 1115 && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) 1117 1116 qh_link_async (ehci, qh); 1118 1117 else { 1119 - qh_put (qh); // refcount from async list 1120 - 1121 1118 /* it's not free to turn the async schedule on/off; leave it 1122 1119 * active but idle for a while once it empties. 1123 1120 */ ··· 1123 1124 && ehci->async->qh_next.qh == NULL) 1124 1125 timer_action (ehci, TIMER_ASYNC_OFF); 1125 1126 } 1127 + qh_put(qh); /* refcount from async list */ 1126 1128 1127 1129 if (next) { 1128 1130 ehci->reclaim = NULL;