Protect the freelists of transfer descriptors with the appropriate spl
authormpi <mpi@openbsd.org>
Sat, 12 Jul 2014 20:13:48 +0000 (20:13 +0000)
committermpi <mpi@openbsd.org>
Sat, 12 Jul 2014 20:13:48 +0000 (20:13 +0000)
so that we do not end up allocating two times new descriptors.

This happens if a thread finds an empty list, start allocating, got
interrupted and the interrupt also finds an empty list.

Fix an issue reported by Nils Frohberg.

ok yuo@, pirofti@

sys/dev/usb/ehci.c
sys/dev/usb/ohci.c
sys/dev/usb/uhci.c

index 7fc7f79..9da6506 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ehci.c,v 1.161 2014/07/12 08:21:10 mpi Exp $ */
+/*     $OpenBSD: ehci.c,v 1.162 2014/07/12 20:13:48 mpi Exp $ */
 /*     $NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $        */
 
 /*
@@ -57,7 +57,6 @@
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdivar.h>
 #include <dev/usb/usb_mem.h>
-#include <dev/usb/usb_quirks.h>
 
 #include <dev/usb/ehcireg.h>
 #include <dev/usb/ehcivar.h>
@@ -2316,22 +2315,20 @@ ehci_root_ctrl_done(struct usbd_xfer *xfer)
 struct ehci_soft_qh *
 ehci_alloc_sqh(struct ehci_softc *sc)
 {
-       struct ehci_soft_qh *sqh;
+       struct ehci_soft_qh *sqh = NULL;
        usbd_status err;
        int i, offs;
        struct usb_dma dma;
+       int s;
 
+       s = splusb();
        if (sc->sc_freeqhs == NULL) {
                DPRINTFN(2, ("ehci_alloc_sqh: allocating chunk\n"));
                err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK,
                    EHCI_PAGE_SIZE, &dma);
-#ifdef EHCI_DEBUG
-               if (err)
-                       printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err);
-#endif
                if (err)
-                       return (NULL);
-               for(i = 0; i < EHCI_SQH_CHUNK; i++) {
+                       goto out;
+               for (i = 0; i < EHCI_SQH_CHUNK; i++) {
                        offs = i * EHCI_SQH_SIZE;
                        sqh = KERNADDR(&dma, offs);
                        sqh->physaddr = DMAADDR(&dma, offs);
@@ -2346,14 +2343,21 @@ ehci_alloc_sqh(struct ehci_softc *sc)
        memset(&sqh->qh, 0, sizeof(struct ehci_qh));
        sqh->next = NULL;
        sqh->prev = NULL;
+
+out:
+       splx(s);
        return (sqh);
 }
 
 void
 ehci_free_sqh(struct ehci_softc *sc, struct ehci_soft_qh *sqh)
 {
+       int s;
+
+       s = splusb();
        sqh->next = sc->sc_freeqhs;
        sc->sc_freeqhs = sqh;
+       splx(s);
 }
 
 struct ehci_soft_qtd *
@@ -2365,17 +2369,13 @@ ehci_alloc_sqtd(struct ehci_softc *sc)
        struct usb_dma dma;
        int s;
 
+       s = splusb();
        if (sc->sc_freeqtds == NULL) {
                DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n"));
                err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK,
                    EHCI_PAGE_SIZE, &dma);
-#ifdef EHCI_DEBUG
                if (err)
-                       printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err);
-#endif
-               if (err)
-                       return (NULL);
-               s = splusb();
+                       goto out;
                for(i = 0; i < EHCI_SQTD_CHUNK; i++) {
                        offs = i * EHCI_SQTD_SIZE;
                        sqtd = KERNADDR(&dma, offs);
@@ -2385,16 +2385,15 @@ ehci_alloc_sqtd(struct ehci_softc *sc)
                        sqtd->nextqtd = sc->sc_freeqtds;
                        sc->sc_freeqtds = sqtd;
                }
-               splx(s);
        }
 
-       s = splusb();
        sqtd = sc->sc_freeqtds;
        sc->sc_freeqtds = sqtd->nextqtd;
        memset(&sqtd->qtd, 0, sizeof(struct ehci_qtd));
        sqtd->nextqtd = NULL;
-       splx(s);
 
+out:
+       splx(s);
        return (sqtd);
 }
 
index 29a9987..ef52e30 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ohci.c,v 1.135 2014/07/10 20:57:40 mpi Exp $ */
+/*     $OpenBSD: ohci.c,v 1.136 2014/07/12 20:13:48 mpi Exp $ */
 /*     $NetBSD: ohci.c,v 1.139 2003/02/22 05:24:16 tsutsui Exp $       */
 /*     $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $       */
 
@@ -386,18 +386,20 @@ ohci_detach(struct device *self, int flags)
 struct ohci_soft_ed *
 ohci_alloc_sed(struct ohci_softc *sc)
 {
-       struct ohci_soft_ed *sed;
+       struct ohci_soft_ed *sed = NULL;
        usbd_status err;
        int i, offs;
        struct usb_dma dma;
+       int s;
 
+       s = splusb();
        if (sc->sc_freeeds == NULL) {
                DPRINTFN(2, ("ohci_alloc_sed: allocating chunk\n"));
                err = usb_allocmem(&sc->sc_bus, OHCI_SED_SIZE * OHCI_SED_CHUNK,
                          OHCI_ED_ALIGN, &dma);
                if (err)
-                       return (0);
-               for(i = 0; i < OHCI_SED_CHUNK; i++) {
+                       goto out;
+               for (i = 0; i < OHCI_SED_CHUNK; i++) {
                        offs = i * OHCI_SED_SIZE;
                        sed = KERNADDR(&dma, offs);
                        sed->physaddr = DMAADDR(&dma, offs);
@@ -409,51 +411,57 @@ ohci_alloc_sed(struct ohci_softc *sc)
        sc->sc_freeeds = sed->next;
        memset(&sed->ed, 0, sizeof(struct ohci_ed));
        sed->next = NULL;
+
+out:
+       splx(s);
        return (sed);
 }
 
 void
 ohci_free_sed(struct ohci_softc *sc, struct ohci_soft_ed *sed)
 {
+       int s;
+
+       s = splusb();
        sed->next = sc->sc_freeeds;
        sc->sc_freeeds = sed;
+       splx(s);
 }
 
 struct ohci_soft_td *
 ohci_alloc_std(struct ohci_softc *sc)
 {
-       struct ohci_soft_td *std;
+       struct ohci_soft_td *std = NULL;
        usbd_status err;
        int i, offs;
        struct usb_dma dma;
        int s;
 
+       s = splusb();
        if (sc->sc_freetds == NULL) {
                DPRINTFN(2, ("ohci_alloc_std: allocating chunk\n"));
                err = usb_allocmem(&sc->sc_bus, OHCI_STD_SIZE * OHCI_STD_CHUNK,
                          OHCI_TD_ALIGN, &dma);
                if (err)
-                       return (NULL);
-               s = splusb();
-               for(i = 0; i < OHCI_STD_CHUNK; i++) {
+                       goto out;
+               for (i = 0; i < OHCI_STD_CHUNK; i++) {
                        offs = i * OHCI_STD_SIZE;
                        std = KERNADDR(&dma, offs);
                        std->physaddr = DMAADDR(&dma, offs);
                        std->nexttd = sc->sc_freetds;
                        sc->sc_freetds = std;
                }
-               splx(s);
        }
 
-       s = splusb();
        std = sc->sc_freetds;
        sc->sc_freetds = std->nexttd;
        memset(&std->td, 0, sizeof(struct ohci_td));
        std->nexttd = NULL;
        std->xfer = NULL;
        ohci_hash_add_td(sc, std);
-       splx(s);
 
+out:
+       splx(s);
        return (std);
 }
 
index a2ca6f1..4c3ae9c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uhci.c,v 1.127 2014/07/12 18:48:52 tedu Exp $ */
+/*     $OpenBSD: uhci.c,v 1.128 2014/07/12 20:13:48 mpi Exp $  */
 /*     $NetBSD: uhci.c,v 1.172 2003/02/23 04:19:26 simonb Exp $        */
 /*     $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $       */
 
@@ -1445,19 +1445,19 @@ uhci_run(struct uhci_softc *sc, int run)
 struct uhci_soft_td *
 uhci_alloc_std(struct uhci_softc *sc)
 {
-       struct uhci_soft_td *std;
+       struct uhci_soft_td *std = NULL;
        usbd_status err;
        int i, offs;
        struct usb_dma dma;
        int s;
 
+       s = splusb();
        if (sc->sc_freetds == NULL) {
                DPRINTFN(2,("uhci_alloc_std: allocating chunk\n"));
                err = usb_allocmem(&sc->sc_bus, UHCI_STD_SIZE * UHCI_STD_CHUNK,
                          UHCI_TD_ALIGN, &dma);
                if (err)
-                       return (0);
-               s = splusb();
+                       goto out;
                for(i = 0; i < UHCI_STD_CHUNK; i++) {
                        offs = i * UHCI_STD_SIZE;
                        std = KERNADDR(&dma, offs);
@@ -1465,15 +1465,14 @@ uhci_alloc_std(struct uhci_softc *sc)
                        std->link.std = sc->sc_freetds;
                        sc->sc_freetds = std;
                }
-               splx(s);
        }
 
-       s = splusb();
        std = sc->sc_freetds;
        sc->sc_freetds = std->link.std;
        memset(&std->td, 0, sizeof(struct uhci_td));
-       splx(s);
 
+out:
+       splx(s);
        return (std);
 }
 
@@ -1500,18 +1499,20 @@ uhci_free_std(struct uhci_softc *sc, struct uhci_soft_td *std)
 struct uhci_soft_qh *
 uhci_alloc_sqh(struct uhci_softc *sc)
 {
-       struct uhci_soft_qh *sqh;
+       struct uhci_soft_qh *sqh = NULL;
        usbd_status err;
        int i, offs;
        struct usb_dma dma;
+       int s;
 
+       s = splusb();
        if (sc->sc_freeqhs == NULL) {
                DPRINTFN(2, ("uhci_alloc_sqh: allocating chunk\n"));
                err = usb_allocmem(&sc->sc_bus, UHCI_SQH_SIZE * UHCI_SQH_CHUNK,
                          UHCI_QH_ALIGN, &dma);
                if (err)
-                       return (0);
-               for(i = 0; i < UHCI_SQH_CHUNK; i++) {
+                       goto out;
+               for (i = 0; i < UHCI_SQH_CHUNK; i++) {
                        offs = i * UHCI_SQH_SIZE;
                        sqh = KERNADDR(&dma, offs);
                        sqh->physaddr = DMAADDR(&dma, offs);
@@ -1522,6 +1523,9 @@ uhci_alloc_sqh(struct uhci_softc *sc)
        sqh = sc->sc_freeqhs;
        sc->sc_freeqhs = sqh->hlink;
        memset(&sqh->qh, 0, sizeof(struct uhci_qh));
+
+out:
+       splx(s);
        return (sqh);
 }