Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/nick-nhusb]: src/sys/dev/usb Restructure the abort code for TD based tra...



details:   https://anonhg.NetBSD.org/src/rev/9d79037f54a9
branches:  nick-nhusb
changeset: 804653:9d79037f54a9
user:      skrll <skrll%NetBSD.org@localhost>
date:      Mon May 30 06:46:50 2016 +0000

description:
Restructure the abort code for TD based transfers (ctrl, bulk, intr).

In PR/22646 some TDs can be on the done queue when the abort start and,
if this is the case, they need to processed after the WDH interrupt.
Instead of waiting for WDH we release TDs that have been touched by the
HC and replace them with new ones.  Once WDH happens the floating TDs
will be returned to the free list.

diffstat:

 sys/dev/usb/ohci.c    |  231 +++++++++++++++++++++++++++++++++++++++----------
 sys/dev/usb/ohcivar.h |    9 +-
 2 files changed, 189 insertions(+), 51 deletions(-)

diffs (truncated from 516 to 300 lines):

diff -r e01b53085c5c -r 9d79037f54a9 sys/dev/usb/ohci.c
--- a/sys/dev/usb/ohci.c        Sun May 29 08:44:15 2016 +0000
+++ b/sys/dev/usb/ohci.c        Mon May 30 06:46:50 2016 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ohci.c,v 1.254.2.75 2016/05/29 08:44:31 skrll Exp $    */
+/*     $NetBSD: ohci.c,v 1.254.2.76 2016/05/30 06:46:50 skrll Exp $    */
 
 /*
  * Copyright (c) 1998, 2004, 2005, 2012 The NetBSD Foundation, Inc.
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.254.2.75 2016/05/29 08:44:31 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.254.2.76 2016/05/30 06:46:50 skrll Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -494,6 +494,7 @@
        memset(&std->td, 0, sizeof(ohci_td_t));
        std->nexttd = NULL;
        std->xfer = NULL;
+       std->held = NULL;
 
        return std;
 }
@@ -541,14 +542,17 @@
 
        DPRINTFN(8, "xfer %p nstd %d", xfer, nstd, 0, 0);
 
-       for (size_t j = 0; j < ox->ox_nstd;) {
+       for (size_t j = 0; j < ox->ox_nstd; j++) {
                ohci_soft_td_t *cur = ohci_alloc_std(sc);
                if (cur == NULL)
                        goto nomem;
 
-               ox->ox_stds[j++] = cur;
+               ox->ox_stds[j] = cur;
+               cur->held = &ox->ox_stds[j];
                cur->xfer = xfer;
                cur->flags = 0;
+               DPRINTFN(10, "xfer=%p new std=%p held at %p", ox, cur,
+                   cur->held, 0);
        }
 
        return 0;
@@ -794,6 +798,8 @@
        for (i = 0; i < OHCI_HASH_SIZE; i++)
                LIST_INIT(&sc->sc_hash_itds[i]);
 
+       TAILQ_INIT(&sc->sc_abortingxfers);
+
        sc->sc_xferpool = pool_cache_init(sizeof(struct ohci_xfer), 0, 0, 0,
            "ohcixfer", NULL, IPL_USB, NULL, NULL, NULL);
 
@@ -1325,12 +1331,26 @@
                 */
                softint_schedule(sc->sc_rhsc_si);
        }
+       if (eintrs & OHCI_SF) {
+               struct ohci_xfer *ox, *tmp;
+               TAILQ_FOREACH_SAFE(ox, &sc->sc_abortingxfers, ox_abnext, tmp) {
+                       DPRINTFN(10, "SF %p xfer %p", sc, ox, 0, 0);
+                       ox->ox_abintrs &= ~OHCI_SF;
+                       KASSERT(ox->ox_abintrs == 0);
+                       TAILQ_REMOVE(&sc->sc_abortingxfers, ox, ox_abnext);
+               }
+               cv_broadcast(&sc->sc_softwake_cv);
+
+               KASSERT(TAILQ_EMPTY(&sc->sc_abortingxfers));
+               DPRINTFN(10, "end SOF %p", sc, 0, 0, 0);
+               /* Don't remove OHIC_SF from eintrs so it is blocked below */
+       }
 
        if (eintrs != 0) {
                /* Block unprocessed interrupts. */
                OWRITE4(sc, OHCI_INTERRUPT_DISABLE, eintrs);
                sc->sc_eintrs &= ~eintrs;
-               DPRINTF("sc %p blocking intrs 0x%x", sc, eintrs, 0, 0);
+               DPRINTF("sc %p blocking/removing intrs 0x%x", sc, eintrs, 0, 0);
        }
 
        return 1;
@@ -1381,12 +1401,22 @@
        struct ohci_pipe *opipe;
        int len, cc;
        int i, j, actlen, iframes, uedir;
-       ohci_physaddr_t done;
+       ohci_physaddr_t done = 0;
 
        KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock));
 
        OHCIHIST_FUNC(); OHCIHIST_CALLED();
 
+       /*
+        * Only read hccadone if WDH is set - we might get here from places
+        * other than an interrupt
+        */
+       if (!(OREAD4(sc, OHCI_INTERRUPT_STATUS) & OHCI_WDH)) {
+               DPRINTFN(10, "no WDH %p", sc, 0, 0, 0);
+               return;
+       }
+
+       DPRINTFN(10, "WDH %p", sc, 0, 0, 0);
        usb_syncmem(&sc->sc_hccadma, offsetof(struct ohci_hcca, hcca_done_head),
            sizeof(sc->sc_hcca->hcca_done_head),
            BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
@@ -1440,8 +1470,8 @@
        for (std = sdone; std; std = stdnext) {
                xfer = std->xfer;
                stdnext = std->dnext;
-               DPRINTFN(10, "std=%p xfer=%p hcpriv=%p", std, xfer,
-                   xfer ? xfer->ux_hcpriv : 0, 0);
+               DPRINTFN(10, "std=%p xfer=%p hcpriv=%p dnext=%p", std, xfer,
+                   xfer ? xfer->ux_hcpriv : 0, stdnext);
                if (xfer == NULL) {
                        /*
                         * xfer == NULL: There seems to be no xfer associated
@@ -1451,13 +1481,25 @@
                         */
                        continue;
                }
+               if (std->held == NULL) {
+                       DPRINTFN(10, "std=%p held is null", std, 0, 0, 0);
+                       ohci_hash_rem_td(sc, std);
+                       ohci_free_std_locked(sc, std);
+                       continue;
+               }
+               /*
+                * Make sure the timeout handler didn't run or ran to the end
+                * and set the transfer status.
+                */
+               callout_halt(&xfer->ux_callout, &sc->sc_lock);
+
                if (xfer->ux_status == USBD_CANCELLED ||
                    xfer->ux_status == USBD_TIMEOUT) {
                        DPRINTF("cancel/timeout %p", xfer, 0, 0, 0);
+
                        /* Handled by abort routine. */
                        continue;
                }
-               callout_stop(&xfer->ux_callout);
 
                len = std->len;
                if (std->td.td_cbp != 0)
@@ -1586,11 +1628,6 @@
                }
        }
 
-       if (sc->sc_softwake) {
-               sc->sc_softwake = 0;
-               cv_broadcast(&sc->sc_softwake_cv);
-       }
-
        DPRINTFN(10, "done", 0, 0, 0, 0);
 }
 
@@ -1876,22 +1913,31 @@
 {
        struct usbd_xfer *xfer = addr;
        ohci_softc_t *sc = OHCI_XFER2SC(xfer);
+       bool timeout = false;
 
        OHCIHIST_FUNC(); OHCIHIST_CALLED();
        DPRINTF("xfer=%p", xfer, 0, 0, 0);
 
+       mutex_enter(&sc->sc_lock);
        if (sc->sc_dying) {
-               mutex_enter(&sc->sc_lock);
                ohci_abort_xfer(xfer, USBD_TIMEOUT);
                mutex_exit(&sc->sc_lock);
                return;
        }
 
-       /* Execute the abort in a process context. */
-       usb_init_task(&xfer->ux_aborttask, ohci_timeout_task, addr,
-           USB_TASKQ_MPSAFE);
-       usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask,
-           USB_TASKQ_HC);
+       if (xfer->ux_status != USBD_CANCELLED) {
+               xfer->ux_status = USBD_TIMEOUT;
+               timeout = true;
+       }
+       mutex_exit(&sc->sc_lock);
+ 
+       if (timeout) {
+               /* Execute the abort in a process context. */
+               usb_init_task(&xfer->ux_aborttask, ohci_timeout_task, addr,
+                   USB_TASKQ_MPSAFE);
+               usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask,
+                   USB_TASKQ_HC);
+       }
 }
 
 void
@@ -2063,6 +2109,7 @@
                                goto bad;
 
                        opipe->tail.itd = sitd;
+                       sitd->held = &opipe->tail.itd;
                        tdphys = sitd->physaddr;
                        fmt = OHCI_ED_FORMAT_ISO;
                        if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
@@ -2075,6 +2122,7 @@
                                goto bad;
 
                        opipe->tail.td = std;
+                       std->held = &opipe->tail.td;
                        tdphys = std->physaddr;
                        fmt = OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD;
                }
@@ -2181,16 +2229,25 @@
 }
 
 /*
- * Abort a device request.
- * If this routine is called at splusb() it guarantees that the request
- * will be removed from the hardware scheduling and that the callback
- * for it will be called with USBD_CANCELLED status.
+ * Cancel or timeour a device request.  We have two cases to deal with
+ *
+ * 1) A driver wants to stop scheduled or inflight transfers
+ * 2) A transfer has timed out
+ *
  * It's impossible to guarantee that the requested transfer will not
- * have happened since the hardware runs concurrently.
- * If the transaction has already happened we rely on the ordinary
- * interrupt processing to process it.
- * XXX This is most probably wrong.
- * XXXMRG this doesn't make sense anymore.
+ * have (partially) happened since the hardware runs concurrently.
+ *
+ * Transfer state is protected by the bus lock and we set the transfer status
+ * as soon as either of the above happens (with bus lock held).
+ *
+ * Then we arrange for the hardware to tells us that it is not still
+ * processing the TDs by setting the sKip bit and requesting a SOF interrupt
+ *
+ * Once we see the SOF interrupt we can check the transfer TDs/iTDs to see if
+ * they've been processed and either
+ *     a) if they're unused recover them for later use, or
+ *     b) if they've been used allocate new TD/iTDs to replace those
+ *         used.  The softint handler will free the old ones.
  */
 void
 ohci_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
@@ -2211,7 +2268,7 @@
 
        if (sc->sc_dying) {
                /* If we're dying, just do the software part. */
-               xfer->ux_status = status;       /* make software ignore it */
+               KASSERT(xfer->ux_status == status);
                callout_halt(&xfer->ux_callout, &sc->sc_lock);
                usb_transfer_complete(xfer);
                return;
@@ -2238,29 +2295,60 @@
        xfer->ux_hcflags |= UXFER_ABORTING;
 
        /*
-        * Step 1: Make interrupt routine and hardware ignore xfer.
+        * Step 1: When cancelling a transfer make sure the timeout handler
+        * didn't run or ran to the end and saw the USBD_CANCELLED status.
+        * Otherwise we must have got here via a timeout.
+        *
+        * If we timed out then
         */
-       xfer->ux_status = status;       /* make software ignore it */
-       callout_stop(&xfer->ux_callout);
+       if (status == USBD_CANCELLED) {
+               xfer->ux_status = status;
+               callout_halt(&xfer->ux_callout, &sc->sc_lock);
+       } else {
+               KASSERT(xfer->ux_status == USBD_TIMEOUT);
+       }
+
+       /*
+        * Step 2: Unless the endpoint is already halted, we set the endpoint
+        * descriptor sKip bit and wait for hardware to complete processing.
+        *
+        * This includes ensuring that any TDs of the transfer that got onto
+        * the done list are also removed.  We ensure this by waiting for
+        * both a WDH and SOF interrupt.
+        */
        DPRINTFN(1, "stop ed=%p", sed, 0, 0, 0);
        usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags),
            sizeof(sed->ed.ed_flags),
            BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
-       sed->ed.ed_flags |= HTOO32(OHCI_ED_SKIP); /* force hardware skip */
-       usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags),
-           sizeof(sed->ed.ed_flags),
-           BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
-
-       /*
-        * Step 2: Wait until we know hardware has finished any possible
-        * use of the xfer.  Also make sure the soft interrupt routine
-        * has run.
-        */
-       /* Hardware finishes in 1ms */
-       usb_delay_ms_locked(opipe->pipe.up_dev->ud_bus, 20, &sc->sc_lock);
-       sc->sc_softwake = 1;



Home | Main Index | Thread Index | Old Index