From a1fd802c6bb48cc046bdd90859831350ac949ff2 Mon Sep 17 00:00:00 2001 From: mpi Date: Sat, 12 Jul 2014 20:13:48 +0000 Subject: [PATCH] Protect the freelists of transfer descriptors with the appropriate spl 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 | 35 +++++++++++++++++------------------ sys/dev/usb/ohci.c | 30 +++++++++++++++++++----------- sys/dev/usb/uhci.c | 24 ++++++++++++++---------- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c index 7fc7f792330..9da65060f24 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -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 #include #include -#include #include #include @@ -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); } diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index 29a9987b297..ef52e302e2d 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -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); } diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c index a2ca6f10226..4c3ae9cc666 100644 --- a/sys/dev/usb/uhci.c +++ b/sys/dev/usb/uhci.c @@ -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); } -- 2.20.1