--- f51345f6619fea5dfd9a6fca478abc9b36cafdb6 +++ c0d4b39a118089ad1b259864e2081fcc003f109e @@ -315,6 +315,7 @@ qh_completions (struct ehci_hcd *ehci, s int stopped; unsigned count = 0; u8 state; + const __le32 halt = HALT_BIT(ehci); struct ehci_qh_hw *hw = qh->hw; if (unlikely (list_empty (&qh->qtd_list))) @@ -421,6 +422,7 @@ qh_completions (struct ehci_hcd *ehci, s && !(qtd->hw_alt_next & EHCI_LIST_END(ehci))) { stopped = 1; + goto halt; } /* stop scanning when we reach qtds the hc is using */ @@ -454,6 +456,16 @@ qh_completions (struct ehci_hcd *ehci, s */ ehci_clear_tt_buffer(ehci, qh, urb, token); } + + /* force halt for unlinked or blocked qh, so we'll + * patch the qh later and so that completions can't + * activate it while we "know" it's stopped. + */ + if ((halt & hw->hw_token) == 0) { +halt: + hw->hw_token |= halt; + wmb (); + } } /* unless we already know the urb's status, collect qtd status @@ -1224,27 +1236,24 @@ static void start_unlink_async (struct e static void scan_async (struct ehci_hcd *ehci) { - bool stopped; struct ehci_qh *qh; enum ehci_timer_action action = TIMER_IO_WATCHDOG; ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); timer_action_done (ehci, TIMER_ASYNC_SHRINK); rescan: - stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); qh = ehci->async->qh_next.qh; if (likely (qh != NULL)) { do { /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list) && (stopped || - qh->stamp != ehci->stamp)) { + if (!list_empty (&qh->qtd_list) + && qh->stamp != ehci->stamp) { int temp; /* unlinks could happen here; completion * reporting drops the lock. rescan using * the latest schedule, but don't rescan - * qhs we already finished (no looping) - * unless the controller is stopped. + * qhs we already finished (no looping). */ qh = qh_get (qh); qh->stamp = ehci->stamp; @@ -1265,9 +1274,9 @@ rescan: */ if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim && (stopped || - ((ehci->stamp - qh->stamp) & 0x1fff) - >= EHCI_SHRINK_FRAMES * 8)) + if (!ehci->reclaim + && ((ehci->stamp - qh->stamp) & 0x1fff) + >= (EHCI_SHRINK_FRAMES * 8)) start_unlink_async(ehci, qh); else action = TIMER_ASYNC_SHRINK;