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