diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2014-07-12 20:13:49 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2014-07-12 20:13:49 +0000 |
commit | 968d9aade2b89c5889776f17a59253ccf5d649e3 (patch) | |
tree | 268c9ca191e173db82a910e2e3ed9766d8157221 /sys/dev/usb/ohci.c | |
parent | 1642d378e61c0ceb1e92a1e50fda710584c4c633 (diff) |
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@
Diffstat (limited to 'sys/dev/usb/ohci.c')
-rw-r--r-- | sys/dev/usb/ohci.c | 30 |
1 files changed, 19 insertions, 11 deletions
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); } |