I don't have any kdb test machines with EHCI.  Could someone test this?

thanks
mh


diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index dca7b17..52b4651 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -594,6 +594,7 @@ qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh 
*qh, struct urb *kdburb
        int                     do_status = 0;
        u8                      state;
        u32                     halt = HALT_BIT(ehci);
+       struct ehci_qh_hw       *hw = qh->hw;
 
         /* verify params are valid */
         if (!qh || !kdburb)
@@ -612,6 +613,11 @@ qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh 
*qh, struct urb *kdburb
        qh->qh_state = QH_STATE_COMPLETING;
        stopped = (state == QH_STATE_IDLE);
 
+ rescan:
+       last = NULL;
+       last_status = -EINPROGRESS;
+       qh->needs_rescan = 0;
+
        /* remove de-activated QTDs from front of queue.
         * after faults (including short reads), cleanup this urb
         * then let the queue advance.
@@ -621,7 +627,6 @@ qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh 
*qh, struct urb *kdburb
                struct ehci_qtd *qtd;
                struct urb      *urb;
                u32             token = 0;
-               int             qtd_status;
 
                qtd = list_entry (entry, struct ehci_qtd, qtd_list);
                urb = qtd->urb;
@@ -651,10 +656,10 @@ qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh 
*qh, struct urb *kdburb
                                         spin_unlock (&ehci->lock);
 
                                count++;
+                               last_status = -EINPROGRESS;
                        }
                        ehci_qtd_free (ehci, last);
                        last = NULL;
-                       last_status = -EINPROGRESS;
                }
 
                /* ignore urbs submitted during completions we reported */
@@ -663,7 +668,7 @@ qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh 
*qh, struct urb *kdburb
 
                /* hardware copies qtd out of qh overlay */
                rmb ();
-               token = hc32_to_cpu(ehci, qtd->hw_token);
+               token = hc32_to_cpu(ehci, hw->hw_token);
 
                /* always clean up qtds the hc de-activated */
                if ((token & QTD_STS_ACTIVE) == 0) {
@@ -710,37 +715,60 @@ qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh 
*qh, struct urb *kdburb
                        /* token in overlay may be most current */
                        if (state == QH_STATE_IDLE
                                        && cpu_to_hc32(ehci, qtd->qtd_dma)
-                                               == qh->hw_current)
-                               token = hc32_to_cpu(ehci, qh->hw_token);
+                                               == hw->hw_current)
+                               token = hc32_to_cpu(ehci, hw->hw_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 & qh->hw_token) == 0) {
+                       if ((halt & hw->hw_token) == 0) {
 halt:
-                               qh->hw_token |= halt;
+                               hw->hw_token |= halt;
                                wmb ();
                        }
                }
 
-               /* remove it from the queue */
-               qtd_status = qtd_copy_status(ehci, urb, qtd->length, token);
-               if (unlikely(qtd_status == -EREMOTEIO)) {
-                       do_status = (!urb->unlinked &&
-                                       usb_pipecontrol(urb->pipe));
-                       qtd_status = 0;
+               if (likely(last_status == -EINPROGRESS)) {
+                        last_status = qtd_copy_status(ehci, urb,
+                                        qtd->length, token);
+                        if (last_status == -EREMOTEIO
+                                        && (qtd->hw_alt_next
+                                                & EHCI_LIST_END(ehci)))
+                                last_status = -EINPROGRESS;
+
+                        /* As part of low/full-speed endpoint-halt processing
+                         * we must clear the TT buffer (11.17.5).
+                         */
+                        if (unlikely(last_status != -EINPROGRESS &&
+                                        last_status != -EREMOTEIO)) {
+                                /* The TT's in some hubs malfunction when they
+                                 * receive this request following a STALL (they
+                                 * stop sending isochronous packets).  Since a
+                                 * STALL can't leave the TT buffer in a busy
+                                 * state (if you believe Figures 11-48 - 11-51
+                                 * in the USB 2.0 spec), we won't clear the TT
+                                 * buffer in this case.  Strictly speaking this
+                                 * is a violation of the spec.
+                                 */
+                                if (last_status != -EPIPE)
+                                        ehci_clear_tt_buffer(ehci, qh, urb,
+                                                        token);
+                       }
                }
-               if (likely(last_status == -EINPROGRESS))
-                       last_status = qtd_status;
 
                if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
                        last = list_entry (qtd->qtd_list.prev,
                                        struct ehci_qtd, qtd_list);
                        last->hw_next = qtd->hw_next;
                }
+
+               /* remove qtd; it's recycled after possible urb completion */
                list_del (&qtd->qtd_list);
                last = qtd;
+
+               /* reinit the xacterr counter for the next qtd */
+               qh->xacterrs = 0;
        }
 
        /* last urb's completion might still need calling */
@@ -767,6 +795,21 @@ halt:
                ehci_qtd_free (ehci, last);
        }
 
+        /* Do we need to rescan for URBs dequeued during a giveback? */
+        if (unlikely(qh->needs_rescan)) {
+                /* If the QH is already unlinked, do the rescan now. */
+                if (state == QH_STATE_IDLE)
+                        goto rescan;
+               
+                /* Otherwise we have to wait until the QH is fully unlinked.
+                 * Our caller will start an unlink if qh->needs_rescan is
+                 * set.  But if an unlink has already started, nothing needs
+                 * to be done.
+                 */
+                if (state != QH_STATE_LINKED)
+                        qh->needs_rescan = 0;
+        }
+
        /* restore original state; caller must unlink or relink */
        qh->qh_state = state;
 
@@ -774,21 +817,26 @@ halt:
         * it after fault cleanup, or recovering from silicon wrongly
         * overlaying the dummy qtd (which reduces DMA chatter).
         */
-       if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
+       if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) {
                switch (state) {
                case QH_STATE_IDLE:
                        qh_refresh(ehci, qh);
                        break;
                case QH_STATE_LINKED:
-                       /* should be rare for periodic transfers,
-                        * except maybe high bandwidth ...
-                        */
-                       if ((cpu_to_hc32(ehci, QH_SMASK)
-                                       & qh->hw_info2) != 0) {
-                               intr_deschedule (ehci, qh);
-                               (void) qh_schedule (ehci, qh);
-                       } else
-                               unlink_async (ehci, qh);
+                       /* We won't refresh a QH that's linked (after the HC
+                         * stopped the queue).  That avoids a race:
+                         *  - HC reads first part of QH;
+                         *  - CPU updates that first part and the token;
+                         *  - HC reads rest of that QH, including token
+                         * Result:  HC gets an inconsistent image, and then
+                         * DMAs to/from the wrong memory (corrupting it).
+                         *
+                         * That should be rare for interrupt transfers,
+                         * except maybe high bandwidth ...
+                         */
+
+                        /* Tell the caller to start an unlink */
+                        qh->needs_rescan = 1;
                        break;
                /* otherwise, unlink already started */
                }

_______________________________________________
kdb mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/kdb

Reply via email to