ChangeSet 1.865.28.13, 2002/12/19 15:13:46-08:00, [EMAIL PROTECTED]

[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;
 }
 


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to