ChangeSet 1.865.28.13, 2002/12/19 15:13:46-08:00, david-b@pacbell.net [PATCH] ehci, qtd submit and completions This ought to address a number of the problems with the recent "dummy td" update as well as some older ones: - Slims down the qh_append_tds() to remove two pairs of "should be duplicate" logic so that * qh_make() only creates and initializes; * qh_append_tds() calls it earlier; * always appends with dummy, no routine qh updates. - Reworked qh_completions() ... simpler, better. * two notable FIXMEs gone, and a bug related to how they interacted with scatterlist i/o * fixed bugs (including one oops) exposed by using dummies more. Passes basic testing: most 'usbtest' cases, usb2 hub with keyboard and CF adapter, storage enumeration. So it seems less troublesome, though it's still not as happy as I've seen it. However, "testusb -at12" (running 'write unlink' tests) still fails for me, and usb-storage gets unhappy when it decides (why? and unsuccessfully) to reset high speed devices. I'm still chasing those problems, which seem to come from higher up in the stack. diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c --- a/drivers/usb/host/ehci-mem.c Sun Dec 22 00:39:14 2002 +++ b/drivers/usb/host/ehci-mem.c Sun Dec 22 00:39:14 2002 @@ -58,7 +58,7 @@ /* Allocate the key transfer structures from the previously allocated pool */ -static void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) +static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) { memset (qtd, 0, sizeof *qtd); qtd->qtd_dma = dma; diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Sun Dec 22 00:39:14 2002 +++ b/drivers/usb/host/ehci-q.c Sun Dec 22 00:39:14 2002 @@ -80,7 +80,7 @@ /* update halted (but potentially linked) qh */ -static void +static inline void qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) { qh->hw_current = 0; @@ -94,6 +94,8 @@ /*-------------------------------------------------------------------------*/ +#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) + static inline void qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, @@ -106,7 +108,15 @@ urb->actual_length += length - QTD_LENGTH (token); /* don't modify error codes */ - if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) { + if (unlikely (urb->status != -EINPROGRESS)) + return; + + /* force cleanup after short read; not always an error */ + if (unlikely (IS_SHORT_READ (token))) + urb->status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ urb->status = -EOVERFLOW; @@ -158,13 +168,6 @@ } } } - - /* force cleanup after short read; not necessarily an error */ - if (unlikely (urb->status == -EINPROGRESS - && QTD_LENGTH (token) != 0 - && QTD_PID (token) == 1)) { - urb->status = -EREMOTEIO; - } } static void @@ -215,26 +218,31 @@ * Chases up to qh->hw_current. Returns number of completions called, * indicating how much "real" work we did. */ +#define HALT_BIT cpu_to_le32(QTD_STS_HALT) static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs) { - struct ehci_qtd *qtd, *last; - struct list_head *next, *qtd_list = &qh->qtd_list; - int unlink = 0, stopped = 0; + struct ehci_qtd *last = 0; + struct list_head *entry, *tmp; + int stopped = 0; unsigned count = 0; if (unlikely (list_empty (&qh->qtd_list))) return count; - /* scan QTDs till end of list, or we reach an active one */ - for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list), - last = 0, next = 0; - next != qtd_list; - last = qtd, qtd = list_entry (next, - struct ehci_qtd, qtd_list)) { - struct urb *urb = qtd->urb; + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe (entry, tmp, &qh->qtd_list) { + struct ehci_qtd *qtd; + struct urb *urb; u32 token = 0; + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + /* clean up any state from previous QTD ...*/ if (last) { if (likely (last->urb != urb)) { @@ -244,74 +252,59 @@ ehci_qtd_free (ehci, last); last = 0; } - next = qtd->qtd_list.next; - /* QTDs at tail may be active if QH+HC are running, - * or when unlinking some urbs queued to this QH - */ + /* hardware copies qtd out of qh overlay */ rmb (); token = le32_to_cpu (qtd->hw_token); stopped = stopped || (qh->qh_state == QH_STATE_IDLE) - || (__constant_cpu_to_le32 (QTD_STS_HALT) - & qh->hw_token) != 0 - || (ehci->hcd.state == USB_STATE_HALT) - || (qh->hw_current == ehci->async->hw_alt_next); - - // FIXME Remove the automagic unlink mode. - // Drivers can now clean up safely; it's their job. - // - // FIXME Removing it should fix the short read scenarios - // with "huge" urb data (more than one 16+KByte td) with - // the short read someplace other than the last data TD. - // Except the control case: 'retrigger' status ACKs. - - /* fault: unlink the rest, since this qtd saw an error? */ - if (unlikely ((token & QTD_STS_HALT) != 0)) { - unlink = 1; - /* status copied below */ - - /* QH halts only because of fault (above) or unlink (here). */ - } else if (unlikely (stopped != 0)) { - - /* unlinking everything because of HC shutdown? */ - if (ehci->hcd.state == USB_STATE_HALT) { - unlink = 1; - - /* explicit unlink, maybe starting here? */ - } else if (qh->qh_state == QH_STATE_IDLE - && (urb->status == -ECONNRESET - || urb->status == -ESHUTDOWN - || urb->status == -ENOENT)) { - unlink = 1; - - /* QH halted to unlink urbs _after_ this? */ - } else if (!unlink && (token & QTD_STS_ACTIVE) != 0) { - qtd = 0; - continue; - } + || (HALT_BIT & qh->hw_token) != 0 + || (ehci->hcd.state == USB_STATE_HALT); - /* unlink the rest? once we start unlinking, after - * a fault or explicit unlink, we unlink all later - * urbs. usb spec requires that for faults... - */ - if (unlink && urb->status == -EINPROGRESS) - urb->status = -ECONNRESET; + /* always clean up qtds the hc de-activated */ + if ((token & QTD_STS_ACTIVE) == 0) { - /* Else QH is active, so we must not modify QTDs - * that HC may be working on. No more qtds to check. - */ - } else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) { - next = qtd_list; - qtd = 0; - continue; - } + /* magic dummy for short reads; won't advance */ + if (IS_SHORT_READ (token) + && ehci->async->hw_alt_next + == qh->hw_current) + goto halt; + /* stop scanning when we reach qtds the hc is using */ + } else if (likely (!stopped)) { + last = 0; + break; + + } else { + /* ignore active qtds unless some previous qtd + * for the urb faulted (including short read) or + * its urb was canceled. we may patch qh or qtds. + */ + if ((token & QTD_STS_ACTIVE) + && urb->status == -EINPROGRESS) { + last = 0; + continue; + } + if ((HALT_BIT & qh->hw_token) == 0) { +halt: + qh->hw_token |= HALT_BIT; + wmb (); + stopped = 1; + } + } + + /* remove it from the queue */ spin_lock (&urb->lock); qtd_copy_status (ehci, urb, qtd->length, token); spin_unlock (&urb->lock); + 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; + } list_del (&qtd->qtd_list); + last = qtd; } /* last urb's completion might still need calling */ @@ -321,14 +314,18 @@ ehci_qtd_free (ehci, last); } - /* reactivate queue after error and driver's cleanup */ - if (unlikely (stopped && !list_empty (&qh->qtd_list))) { - qh_update (ehci, qh, list_entry (qh->qtd_list.next, - struct ehci_qtd, qtd_list)); + /* update qh after fault cleanup */ + if (unlikely ((HALT_BIT & qh->hw_token) != 0)) { + qh_update (ehci, qh, + list_empty (&qh->qtd_list) + ? qh->dummy + : list_entry (qh->qtd_list.next, + struct ehci_qtd, qtd_list)); } return count; } +#undef HALT_BIT /*-------------------------------------------------------------------------*/ @@ -536,10 +533,9 @@ * there are additional complications: c-mask, maybe FSTNs. */ static struct ehci_qh * -ehci_qh_make ( +qh_make ( struct ehci_hcd *ehci, struct urb *urb, - struct list_head *qtd_list, int flags ) { struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); @@ -649,27 +645,13 @@ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + /* init as halted, toggle clear, advance to dummy */ qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_le32 (info1); qh->hw_info2 = cpu_to_le32 (info2); - - /* initialize sw and hw queues with these qtds */ - if (!list_empty (qtd_list)) { - struct ehci_qtd *qtd; - - /* hc's list view ends with dummy td; we might update it */ - qtd = list_entry (qtd_list->prev, struct ehci_qtd, qtd_list); - qtd->hw_next = QTD_NEXT (qh->dummy->qtd_dma); - - list_splice (qtd_list, &qh->qtd_list); - qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - qh_update (ehci, qh, qtd); - } else { - qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END; - } - - /* initialize data toggle state */ - clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh); + qh_update (ehci, qh, qh->dummy); + qh->hw_token = cpu_to_le32 (QTD_STS_HALT); + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); return qh; } #undef hb_mult @@ -736,6 +718,11 @@ struct ehci_qh *qh = 0; qh = (struct ehci_qh *) *ptr; + if (unlikely (qh == 0)) { + /* can't sleep here, we have ehci->lock... */ + qh = qh_make (ehci, urb, SLAB_ATOMIC); + *ptr = qh; + } if (likely (qh != 0)) { struct ehci_qtd *qtd; @@ -766,8 +753,34 @@ } } - /* append to tds already queued to this qh? */ - if (unlikely (!list_empty (&qh->qtd_list) && qtd)) { + /* FIXME: changing config or interface setting is not + * supported yet. preferred fix is for usbcore to tell + * us to clear out each endpoint's state, but... + */ + + /* usb_clear_halt() means qh data toggle gets reset */ + if (unlikely (!usb_gettoggle (urb->dev, + (epnum & 0x0f), !(epnum & 0x10))) + && !usb_pipecontrol (urb->pipe)) { + /* "never happens": drivers do stall cleanup right */ + if (qh->qh_state != QH_STATE_IDLE + && (cpu_to_le32 (QTD_STS_HALT) + & qh->hw_token) == 0) + ehci_warn (ehci, "clear toggle dev%d " + "ep%d%s: not idle\n", + usb_pipedevice (urb->pipe), + epnum & 0x0f, + usb_pipein (urb->pipe) + ? "in" : "out"); + /* else we know this overlay write is safe */ + clear_toggle (urb->dev, + epnum & 0x0f, !(epnum & 0x10), qh); + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_completions() usually modify the overlay. + */ + if (likely (qtd != 0)) { struct ehci_qtd *dummy; dma_addr_t dma; u32 token; @@ -785,8 +798,10 @@ dma = dummy->qtd_dma; *dummy = *qtd; dummy->qtd_dma = dma; + list_del (&qtd->qtd_list); list_add (&dummy->qtd_list, qtd_list); + __list_splice (qtd_list, qh->qtd_list.prev); ehci_qtd_init (qtd, qtd->qtd_dma); qtd->hw_alt_next = ehci->async->hw_alt_next; @@ -802,36 +817,9 @@ wmb (); dummy->hw_token = token; - /* no URB queued */ - } else { - /* usb_clear_halt() means qh data toggle gets reset */ - if (unlikely (!usb_gettoggle (urb->dev, - (epnum & 0x0f), - !(epnum & 0x10)))) { - clear_toggle (urb->dev, - epnum & 0x0f, !(epnum & 0x10), qh); - } - - /* make sure hc sees current dummy at the end */ - if (qtd) { - struct ehci_qtd *last_qtd; - - last_qtd = list_entry (qtd_list->prev, - struct ehci_qtd, qtd_list); - last_qtd->hw_next = QTD_NEXT ( - qh->dummy->qtd_dma); - qh_update (ehci, qh, qtd); - } + urb->hcpriv = qh_get (qh); } - list_splice (qtd_list, qh->qtd_list.prev); - - } else { - /* can't sleep here, we have ehci->lock... */ - qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - *ptr = qh; } - if (qh) - urb->hcpriv = qh_get (qh); return qh; }