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 xfer methods so that (close...



details:   https://anonhg.NetBSD.org/src/rev/a764619d236b
branches:  nick-nhusb
changeset: 804454:a764619d236b
user:      skrll <skrll%NetBSD.org@localhost>
date:      Tue Nov 10 08:44:09 2015 +0000

description:
Restructure the xfer methods so that (close to) minimal work in done in softint
context.  Now all memory allocation is done in thread context.

Addresses kern/48308 for uhci(4), fixes a bunch of locking bugs around the
*TD free lists, and plugs some memory leaks on error conditions.

diffstat:

 sys/dev/usb/uhci.c    |  966 ++++++++++++++++++++++++++++++++++---------------
 sys/dev/usb/uhcivar.h |   30 +-
 2 files changed, 693 insertions(+), 303 deletions(-)

diffs (truncated from 1520 to 300 lines):

diff -r 0f432677b2fa -r a764619d236b sys/dev/usb/uhci.c
--- a/sys/dev/usb/uhci.c        Mon Nov 09 08:35:23 2015 +0000
+++ b/sys/dev/usb/uhci.c        Tue Nov 10 08:44:09 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: uhci.c,v 1.264.4.54 2015/11/09 08:35:23 skrll Exp $    */
+/*     $NetBSD: uhci.c,v 1.264.4.55 2015/11/10 08:44:09 skrll Exp $    */
 
 /*
  * Copyright (c) 1998, 2004, 2011, 2012 The NetBSD Foundation, Inc.
@@ -42,7 +42,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.264.4.54 2015/11/09 08:35:23 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.264.4.55 2015/11/10 08:44:09 skrll Exp $");
 
 #include "opt_usb.h"
 
@@ -139,20 +139,17 @@
                struct {
                        uhci_soft_qh_t *sqh;
                        usb_dma_t reqdma;
-                       uhci_soft_td_t *setup, *stat;
-                       u_int length;
+                       uhci_soft_td_t *setup;
+                       uhci_soft_td_t *stat;
                } ctrl;
                /* Interrupt pipe */
                struct {
                        int npoll;
-                       int isread;
                        uhci_soft_qh_t **qhs;
                } intr;
                /* Bulk pipe */
                struct {
                        uhci_soft_qh_t *sqh;
-                       u_int length;
-                       int isread;
                } bulk;
                /* Isochronous pipe */
                struct isoc {
@@ -168,6 +165,7 @@
 Static usbd_status     uhci_run(uhci_softc_t *, int, int);
 Static uhci_soft_td_t  *uhci_alloc_std(uhci_softc_t *);
 Static void            uhci_free_std(uhci_softc_t *, uhci_soft_td_t *);
+Static void            uhci_free_std_locked(uhci_softc_t *, uhci_soft_td_t *);
 Static uhci_soft_qh_t  *uhci_alloc_sqh(uhci_softc_t *);
 Static void            uhci_free_sqh(uhci_softc_t *, uhci_soft_qh_t *);
 #if 0
@@ -176,11 +174,15 @@
 Static void            uhci_exit_ctl_q(uhci_softc_t *, uhci_soft_qh_t *);
 #endif
 
-Static void            uhci_free_std_chain(uhci_softc_t *,
-                           uhci_soft_td_t *, uhci_soft_td_t *);
-Static usbd_status     uhci_alloc_std_chain(struct uhci_pipe *,
-                           uhci_softc_t *, int, int, uint16_t, usb_dma_t *,
-                           uhci_soft_td_t **, uhci_soft_td_t **);
+Static void            uhci_free_std_chain(uhci_softc_t *, uhci_soft_td_t *,
+                           uhci_soft_td_t *);
+Static usbd_status     uhci_alloc_std_chain(uhci_softc_t *, struct usbd_xfer *,
+                           int, int, uhci_soft_td_t **, uhci_soft_td_t **);
+Static void            uhci_free_stds(uhci_softc_t *, struct uhci_xfer *);
+
+Static void            uhci_reset_std_chain(uhci_softc_t *, struct usbd_xfer *,
+                           int, int, int *, uhci_soft_td_t *, uhci_soft_td_t **);
+
 Static void            uhci_poll_hub(void *);
 Static void            uhci_waitintr(uhci_softc_t *, struct usbd_xfer *);
 Static void            uhci_check_intr(uhci_softc_t *, struct uhci_xfer *);
@@ -209,24 +211,32 @@
 Static int             uhci_roothub_ctrl(struct usbd_bus *,
                            usb_device_request_t *, void *, int);
 
+Static int             uhci_device_ctrl_init(struct usbd_xfer *);
+Static void            uhci_device_ctrl_fini(struct usbd_xfer *);
 Static usbd_status     uhci_device_ctrl_transfer(struct usbd_xfer *);
 Static usbd_status     uhci_device_ctrl_start(struct usbd_xfer *);
 Static void            uhci_device_ctrl_abort(struct usbd_xfer *);
 Static void            uhci_device_ctrl_close(struct usbd_pipe *);
 Static void            uhci_device_ctrl_done(struct usbd_xfer *);
 
+Static int             uhci_device_intr_init(struct usbd_xfer *);
+Static void            uhci_device_intr_fini(struct usbd_xfer *);
 Static usbd_status     uhci_device_intr_transfer(struct usbd_xfer *);
 Static usbd_status     uhci_device_intr_start(struct usbd_xfer *);
 Static void            uhci_device_intr_abort(struct usbd_xfer *);
 Static void            uhci_device_intr_close(struct usbd_pipe *);
 Static void            uhci_device_intr_done(struct usbd_xfer *);
 
+Static int             uhci_device_bulk_init(struct usbd_xfer *);
+Static void            uhci_device_bulk_fini(struct usbd_xfer *);
 Static usbd_status     uhci_device_bulk_transfer(struct usbd_xfer *);
 Static usbd_status     uhci_device_bulk_start(struct usbd_xfer *);
 Static void            uhci_device_bulk_abort(struct usbd_xfer *);
 Static void            uhci_device_bulk_close(struct usbd_pipe *);
 Static void            uhci_device_bulk_done(struct usbd_xfer *);
 
+Static int             uhci_device_isoc_init(struct usbd_xfer *);
+Static void            uhci_device_isoc_fini(struct usbd_xfer *);
 Static usbd_status     uhci_device_isoc_transfer(struct usbd_xfer *);
 Static usbd_status     uhci_device_isoc_start(struct usbd_xfer *);
 Static void            uhci_device_isoc_abort(struct usbd_xfer *);
@@ -332,6 +342,8 @@
 };
 
 const struct usbd_pipe_methods uhci_device_ctrl_methods = {
+       .upm_init =     uhci_device_ctrl_init,
+       .upm_fini =     uhci_device_ctrl_fini,
        .upm_transfer = uhci_device_ctrl_transfer,
        .upm_start =    uhci_device_ctrl_start,
        .upm_abort =    uhci_device_ctrl_abort,
@@ -341,6 +353,8 @@
 };
 
 const struct usbd_pipe_methods uhci_device_intr_methods = {
+       .upm_init =     uhci_device_intr_init,
+       .upm_fini =     uhci_device_intr_fini,
        .upm_transfer = uhci_device_intr_transfer,
        .upm_start =    uhci_device_intr_start,
        .upm_abort =    uhci_device_intr_abort,
@@ -350,6 +364,8 @@
 };
 
 const struct usbd_pipe_methods uhci_device_bulk_methods = {
+       .upm_init =     uhci_device_bulk_init,
+       .upm_fini =     uhci_device_bulk_fini,
        .upm_transfer = uhci_device_bulk_transfer,
        .upm_start =    uhci_device_bulk_start,
        .upm_abort =    uhci_device_bulk_abort,
@@ -359,6 +375,8 @@
 };
 
 const struct usbd_pipe_methods uhci_device_isoc_methods = {
+       .upm_init =     uhci_device_isoc_init,
+       .upm_fini =     uhci_device_isoc_fini,
        .upm_transfer = uhci_device_isoc_transfer,
        .upm_start =    uhci_device_isoc_start,
        .upm_abort =    uhci_device_isoc_abort,
@@ -438,6 +456,10 @@
        UWRITE2(sc, UHCI_FRNUM, 0);             /* set frame number to 0 */
        UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0)); /* set frame list*/
 
+       /* Initialise mutex early for uhci_alloc_* */
+       mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
+       mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB);
+
        /*
         * Allocate a TD, inactive, that hangs from the last QH.
         * This is to avoid a bug in the PIIX that makes it run berserk
@@ -457,7 +479,7 @@
        /* Allocate the dummy QH marking the end and used for looping the QHs.*/
        lsqh = uhci_alloc_sqh(sc);
        if (lsqh == NULL)
-               return ENOMEM;
+               goto fail1;
        lsqh->hlink = NULL;
        lsqh->qh.qh_hlink = htole32(UHCI_PTR_T);        /* end of QH chain */
        lsqh->elink = std;
@@ -469,7 +491,7 @@
        /* Allocate the dummy QH where bulk traffic will be queued. */
        bsqh = uhci_alloc_sqh(sc);
        if (bsqh == NULL)
-               return ENOMEM;
+               goto fail2;
        bsqh->hlink = lsqh;
        bsqh->qh.qh_hlink = htole32(lsqh->physaddr | UHCI_PTR_QH);
        bsqh->elink = NULL;
@@ -481,7 +503,7 @@
        /* Allocate dummy QH where high speed control traffic will be queued. */
        chsqh = uhci_alloc_sqh(sc);
        if (chsqh == NULL)
-               return ENOMEM;
+               goto fail3;
        chsqh->hlink = bsqh;
        chsqh->qh.qh_hlink = htole32(bsqh->physaddr | UHCI_PTR_QH);
        chsqh->elink = NULL;
@@ -493,7 +515,7 @@
        /* Allocate dummy QH where control traffic will be queued. */
        clsqh = uhci_alloc_sqh(sc);
        if (clsqh == NULL)
-               return ENOMEM;
+               goto fail4;
        clsqh->hlink = chsqh;
        clsqh->qh.qh_hlink = htole32(chsqh->physaddr | UHCI_PTR_QH);
        clsqh->elink = NULL;
@@ -546,8 +568,6 @@
 
        callout_init(&sc->sc_poll_handle, CALLOUT_MPSAFE);
 
-       mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
-       mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB);
        cv_init(&sc->sc_softwake_cv, "uhciab");
 
        /* Set up the bus struct. */
@@ -563,6 +583,17 @@
        UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
                UHCI_INTR_IOCE | UHCI_INTR_SPIE);       /* enable interrupts */
        return err;
+
+fail4:
+       uhci_free_sqh(sc, chsqh);
+fail3:
+       uhci_free_sqh(sc, lsqh);
+fail2:
+       uhci_free_sqh(sc, lsqh);
+fail1:
+       uhci_free_std(sc, std);
+
+       return ENOMEM;
 }
 
 int
@@ -1385,7 +1416,7 @@
 void
 uhci_check_intr(uhci_softc_t *sc, struct uhci_xfer *ux)
 {
-       uhci_soft_td_t *std, *lstd;
+       uhci_soft_td_t *std, *fstd = NULL, *lstd = NULL;
        uint32_t status;
 
        UHCIHIST_FUNC(); UHCIHIST_CALLED();
@@ -1400,9 +1431,23 @@
                return;
        }
 
-       if (ux->ux_stdstart == NULL)
+       switch (ux->ux_type) {
+       case UX_CTRL:
+               fstd = ux->ux_setup;
+               lstd = ux->ux_stat;
+               break;
+       case UX_BULK:
+       case UX_INTR:
+       case UX_ISOC:
+               fstd = ux->ux_stdstart;
+               lstd = ux->ux_stdend;
+               break;
+       default:
+               KASSERT(false);
+               break;
+       }
+       if (fstd == NULL)
                return;
-       lstd = ux->ux_stdend;
 
        KASSERT(lstd != NULL);
 
@@ -1432,7 +1477,7 @@
         * short packet (SPD and not ACTIVE).
         */
        DPRINTFN(12, "active ux=%p", ux, 0, 0, 0);
-       for (std = ux->ux_stdstart; std != lstd; std = std->link.std) {
+       for (std = fstd; std != lstd; std = std->link.std) {
                usb_syncmem(&std->dma,
                    std->offs + offsetof(uhci_td_t, td_status),
                    sizeof(std->td.td_status),
@@ -1457,10 +1502,8 @@
                 * If the data phase of a control transfer is short, we need
                 * to complete the status stage
                 */
-               usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc;
-               uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
-
-               if ((status & UHCI_TD_SPD) && xfertype == UE_CONTROL) {
+
+               if ((status & UHCI_TD_SPD) && ux->ux_type == UX_CTRL) {
                        struct uhci_pipe *upipe =
                            UHCI_PIPE2UPIPE(xfer->ux_pipe);
                        uhci_soft_qh_t *sqh = upipe->ctrl.sqh;
@@ -1802,12 +1845,17 @@
 
        UHCIHIST_FUNC(); UHCIHIST_CALLED();
 
+       mutex_enter(&sc->sc_lock);
        if (sc->sc_freetds == NULL) {
                DPRINTFN(2, "allocating chunk", 0, 0, 0, 0);
+               mutex_exit(&sc->sc_lock);
+
                err = usb_allocmem(&sc->sc_bus, UHCI_STD_SIZE * UHCI_STD_CHUNK,
                          UHCI_TD_ALIGN, &dma);
                if (err)
                        return NULL;
+
+               mutex_enter(&sc->sc_lock);
                for (i = 0; i < UHCI_STD_CHUNK; i++) {
                        offs = i * UHCI_STD_SIZE;
                        std = KERNADDR(&dma, offs);
@@ -1820,25 +1868,40 @@
        }
        std = sc->sc_freetds;
        sc->sc_freetds = std->link.std;
+       mutex_exit(&sc->sc_lock);
+
        memset(&std->td, 0, sizeof(uhci_td_t));
+
        return std;
 }
 
+#define TD_IS_FREE 0x12345678



Home | Main Index | Thread Index | Old Index