diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-07-31 10:40:22 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-08-07 19:05:13 -0400 |
commit | 7a0f0d951273eee889c2441846842348ebc00a2a (patch) | |
tree | 268d5316e3c3d3bd0957d39d4233036790736965 /drivers/usb | |
parent | 01105a246345f011fde64d24a601090b646e9e4c (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/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 6 |
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; |