diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-08-19 12:22:06 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:37 -0400 |
commit | 3a44494e233c0fdd818d485cfea8998500543589 (patch) | |
tree | 04b66268a2efc1236d3f9e477a04dc2d3ec87582 /drivers/usb/host/ehci-q.c | |
parent | 04c4ab17c7c39603c5017bee20d3b8ccb2f19816 (diff) |
USB: EHCI: rescan the queue after an unlink
This patch (as1280) fixes an obscure bug in ehci-hcd's dequeuing logic
for async URBs. If a later URB is unlinked and the completion
routine unlinks an earlier URB, then the earlier URB won't be given
back in a timely manner because the endpoint queue isn't rescanned as
it should be.
Similar bugs occur if an endpoint is reset or a halt is cleared while
a completion routine is running, because the subroutines don't test
for the COMPLETING state.
All these problems are solved by adding a new needs_rescan flag to the
ehci_qh structure. If the flag is set while scanning through an idle
QH, the scan will be repeated. If the QH isn't idle then an unlink
cycle will be initiated, and the proper action will be taken when it
becomes idle.
Also, an unnecessary test is removed from qh_link_async(): That
routine is never called if the QH's state isn't IDLE.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-q.c')
-rw-r--r-- | drivers/usb/host/ehci-q.c | 43 |
1 files changed, 36 insertions, 7 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 377ed530b920..57a84795c43f 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -310,13 +310,13 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); | |||
310 | static unsigned | 310 | static unsigned |
311 | qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | 311 | qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) |
312 | { | 312 | { |
313 | struct ehci_qtd *last = NULL, *end = qh->dummy; | 313 | struct ehci_qtd *last, *end = qh->dummy; |
314 | struct list_head *entry, *tmp; | 314 | struct list_head *entry, *tmp; |
315 | int last_status = -EINPROGRESS; | 315 | int last_status; |
316 | int stopped; | 316 | int stopped; |
317 | unsigned count = 0; | 317 | unsigned count = 0; |
318 | u8 state; | 318 | u8 state; |
319 | __le32 halt = HALT_BIT(ehci); | 319 | const __le32 halt = HALT_BIT(ehci); |
320 | struct ehci_qh_hw *hw = qh->hw; | 320 | struct ehci_qh_hw *hw = qh->hw; |
321 | 321 | ||
322 | if (unlikely (list_empty (&qh->qtd_list))) | 322 | if (unlikely (list_empty (&qh->qtd_list))) |
@@ -327,11 +327,20 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
327 | * they add urbs to this qh's queue or mark them for unlinking. | 327 | * they add urbs to this qh's queue or mark them for unlinking. |
328 | * | 328 | * |
329 | * NOTE: unlinking expects to be done in queue order. | 329 | * NOTE: unlinking expects to be done in queue order. |
330 | * | ||
331 | * It's a bug for qh->qh_state to be anything other than | ||
332 | * QH_STATE_IDLE, unless our caller is scan_async() or | ||
333 | * scan_periodic(). | ||
330 | */ | 334 | */ |
331 | state = qh->qh_state; | 335 | state = qh->qh_state; |
332 | qh->qh_state = QH_STATE_COMPLETING; | 336 | qh->qh_state = QH_STATE_COMPLETING; |
333 | stopped = (state == QH_STATE_IDLE); | 337 | stopped = (state == QH_STATE_IDLE); |
334 | 338 | ||
339 | rescan: | ||
340 | last = NULL; | ||
341 | last_status = -EINPROGRESS; | ||
342 | qh->needs_rescan = 0; | ||
343 | |||
335 | /* remove de-activated QTDs from front of queue. | 344 | /* remove de-activated QTDs from front of queue. |
336 | * after faults (including short reads), cleanup this urb | 345 | * after faults (including short reads), cleanup this urb |
337 | * then let the queue advance. | 346 | * then let the queue advance. |
@@ -507,6 +516,21 @@ halt: | |||
507 | ehci_qtd_free (ehci, last); | 516 | ehci_qtd_free (ehci, last); |
508 | } | 517 | } |
509 | 518 | ||
519 | /* Do we need to rescan for URBs dequeued during a giveback? */ | ||
520 | if (unlikely(qh->needs_rescan)) { | ||
521 | /* If the QH is already unlinked, do the rescan now. */ | ||
522 | if (state == QH_STATE_IDLE) | ||
523 | goto rescan; | ||
524 | |||
525 | /* Otherwise we have to wait until the QH is fully unlinked. | ||
526 | * Our caller will start an unlink if qh->needs_rescan is | ||
527 | * set. But if an unlink has already started, nothing needs | ||
528 | * to be done. | ||
529 | */ | ||
530 | if (state != QH_STATE_LINKED) | ||
531 | qh->needs_rescan = 0; | ||
532 | } | ||
533 | |||
510 | /* restore original state; caller must unlink or relink */ | 534 | /* restore original state; caller must unlink or relink */ |
511 | qh->qh_state = state; | 535 | qh->qh_state = state; |
512 | 536 | ||
@@ -535,8 +559,10 @@ halt: | |||
535 | & hw->hw_info2) != 0) { | 559 | & hw->hw_info2) != 0) { |
536 | intr_deschedule (ehci, qh); | 560 | intr_deschedule (ehci, qh); |
537 | (void) qh_schedule (ehci, qh); | 561 | (void) qh_schedule (ehci, qh); |
538 | } else | 562 | } else { |
539 | unlink_async (ehci, qh); | 563 | /* Tell the caller to start an unlink */ |
564 | qh->needs_rescan = 1; | ||
565 | } | ||
540 | break; | 566 | break; |
541 | /* otherwise, unlink already started */ | 567 | /* otherwise, unlink already started */ |
542 | } | 568 | } |
@@ -916,6 +942,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
916 | if (unlikely(qh->clearing_tt)) | 942 | if (unlikely(qh->clearing_tt)) |
917 | return; | 943 | return; |
918 | 944 | ||
945 | WARN_ON(qh->qh_state != QH_STATE_IDLE); | ||
946 | |||
919 | /* (re)start the async schedule? */ | 947 | /* (re)start the async schedule? */ |
920 | head = ehci->async; | 948 | head = ehci->async; |
921 | timer_action_done (ehci, TIMER_ASYNC_OFF); | 949 | timer_action_done (ehci, TIMER_ASYNC_OFF); |
@@ -934,8 +962,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
934 | } | 962 | } |
935 | 963 | ||
936 | /* clear halt and/or toggle; and maybe recover from silicon quirk */ | 964 | /* clear halt and/or toggle; and maybe recover from silicon quirk */ |
937 | if (qh->qh_state == QH_STATE_IDLE) | 965 | qh_refresh(ehci, qh); |
938 | qh_refresh (ehci, qh); | ||
939 | 966 | ||
940 | /* splice right after start */ | 967 | /* splice right after start */ |
941 | qh->qh_next = head->qh_next; | 968 | qh->qh_next = head->qh_next; |
@@ -1220,6 +1247,8 @@ rescan: | |||
1220 | qh = qh_get (qh); | 1247 | qh = qh_get (qh); |
1221 | qh->stamp = ehci->stamp; | 1248 | qh->stamp = ehci->stamp; |
1222 | temp = qh_completions (ehci, qh); | 1249 | temp = qh_completions (ehci, qh); |
1250 | if (qh->needs_rescan) | ||
1251 | unlink_async(ehci, qh); | ||
1223 | qh_put (qh); | 1252 | qh_put (qh); |
1224 | if (temp != 0) { | 1253 | if (temp != 0) { |
1225 | goto rescan; | 1254 | goto rescan; |