aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2009-07-31 10:40:22 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-08-07 19:05:13 -0400
commit7a0f0d951273eee889c2441846842348ebc00a2a (patch)
tree268d5316e3c3d3bd0957d39d4233036790736965 /drivers
parent01105a246345f011fde64d24a601090b646e9e4c (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-hcd.c3
-rw-r--r--drivers/usb/host/ehci-q.c6
2 files changed, 5 insertions, 4 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 7d03549c3339..11c627ce6022 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -903,7 +903,8 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
903 /* already started */ 903 /* already started */
904 break; 904 break;
905 case QH_STATE_IDLE: 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 break; 908 break;
908 } 909 }
909 break; 910 break;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 9a1384747f3b..b27380505576 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -940,6 +940,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
940 head->qh_next.qh = qh; 940 head->qh_next.qh = qh;
941 head->hw_next = dma; 941 head->hw_next = dma;
942 942
943 qh_get(qh);
943 qh->xacterrs = QH_XACTERR_MAX; 944 qh->xacterrs = QH_XACTERR_MAX;
944 qh->qh_state = QH_STATE_LINKED; 945 qh->qh_state = QH_STATE_LINKED;
945 /* qtd completions reported later by interrupt */ 946 /* qtd completions reported later by interrupt */
@@ -1080,7 +1081,7 @@ submit_async (
1080 * the HC and TT handle it when the TT has a buffer ready. 1081 * the HC and TT handle it when the TT has a buffer ready.
1081 */ 1082 */
1082 if (likely (qh->qh_state == QH_STATE_IDLE)) 1083 if (likely (qh->qh_state == QH_STATE_IDLE))
1083 qh_link_async (ehci, qh_get (qh)); 1084 qh_link_async(ehci, qh);
1084 done: 1085 done:
1085 spin_unlock_irqrestore (&ehci->lock, flags); 1086 spin_unlock_irqrestore (&ehci->lock, flags);
1086 if (unlikely (qh == NULL)) 1087 if (unlikely (qh == NULL))
@@ -1115,8 +1116,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
1115 && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) 1116 && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
1116 qh_link_async (ehci, qh); 1117 qh_link_async (ehci, qh);
1117 else { 1118 else {
1118 qh_put (qh); // refcount from async list
1119
1120 /* it's not free to turn the async schedule on/off; leave it 1119 /* it's not free to turn the async schedule on/off; leave it
1121 * active but idle for a while once it empties. 1120 * active but idle for a while once it empties.
1122 */ 1121 */
@@ -1124,6 +1123,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
1124 && ehci->async->qh_next.qh == NULL) 1123 && ehci->async->qh_next.qh == NULL)
1125 timer_action (ehci, TIMER_ASYNC_OFF); 1124 timer_action (ehci, TIMER_ASYNC_OFF);
1126 } 1125 }
1126 qh_put(qh); /* refcount from async list */
1127 1127
1128 if (next) { 1128 if (next) {
1129 ehci->reclaim = NULL; 1129 ehci->reclaim = NULL;