--- a/sys/bus/u4b/serial/ulpt.c +++ b/sys/bus/u4b/serial/ulpt.c @@ -422,29 +422,35 @@ ulpt_open(struct usb_fifo *fifo, int fflags static int unlpt_open(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); + int error = 0; + + /* DF-5436: /dev/ulptN and /dev/unlptN are two independent cdevs + * sharing one softc; open(2) on them is only serialized per vnode, + * so the sc_fflags gate must itself be atomic. */ + lockmgr(&sc->sc_lock, LK_EXCLUSIVE); if (sc->sc_fflags & fflags) { - return (EBUSY); + error = EBUSY; + goto done; } if (fflags & FREAD) { /* clear stall first */ - lockmgr(&sc->sc_lock, LK_EXCLUSIVE); usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]); - lockmgr(&sc->sc_lock, LK_RELEASE); if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_RD]), ULPT_IFQ_MAXLEN)) { - return (ENOMEM); + error = ENOMEM; + goto done; } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_RX] = fifo; } if (fflags & FWRITE) { /* clear stall first */ - lockmgr(&sc->sc_lock, LK_EXCLUSIVE); usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]); - lockmgr(&sc->sc_lock, LK_RELEASE); if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_WR]), ULPT_IFQ_MAXLEN)) { - return (ENOMEM); + error = ENOMEM; + goto done; } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_TX] = fifo; } sc->sc_fflags |= fflags & (FREAD | FWRITE); - return (0); +done: + lockmgr(&sc->sc_lock, LK_RELEASE); + return (error); } static void ulpt_close(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); + lockmgr(&sc->sc_lock, LK_EXCLUSIVE); sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); - if (fflags & (FREAD | FWRITE)) { + if (fflags & FREAD) { + sc->sc_fifo_open[USB_FIFO_RX] = NULL; + usb_fifo_free_buffer(fifo); + } + if (fflags & FWRITE) { + sc->sc_fifo_open[USB_FIFO_TX] = NULL; usb_fifo_free_buffer(fifo); } + lockmgr(&sc->sc_lock, LK_RELEASE); }