DF-5287 / fix.diff
--- a/sys/bus/u4b/usb_dev.h +++ b/sys/bus/u4b/usb_dev.h @@ -124,6 +124,7 @@ uint16_t dev_ep_index; /* our device endpoint index */ uint8_t flag_sleeping; /* set if FIFO is sleeping */ uint8_t flag_iscomplete; /* set if a USB transfer is complete */ + uint8_t flag_setup_busy; /* a ugen_transfer_setup() is in progress */ uint8_t flag_iserror; /* set if FIFO error happened */ uint8_t flag_isselect; /* set if FIFO is selected */ uint8_t flag_flushing; /* set if FIFO is flushing data */ --- a/sys/bus/u4b/usb_generic.c +++ b/sys/bus/u4b/usb_generic.c @@ -142,6 +142,14 @@ uint8_t iface_index = ep->iface_index; int error; + KKASSERT(lockowned(f->priv_lock)); + + /* only one setup in progress at a time (DF-5287) */ + if (f->flag_setup_busy) { + return (0); /* loser of the race: nothing to do */ + } + f->flag_setup_busy = 1; + lockmgr(f->priv_lock, LK_RELEASE); /* @@ -151,6 +159,14 @@ error = usbd_transfer_setup(udev, &iface_index, f->xfer, setup, n_setup, f, f->priv_lock); if (error == 0) { + /* + * Swap the FIFO buffers under priv_lock (DF-5287): the + * previous code called usb_fifo_alloc_buffer() -> + * usb_fifo_free_buffer() with no lock held, freeing the + * fifo mbuf storage while live callbacks iterated the + * queues. + */ + lockmgr(f->priv_lock, LK_EXCLUSIVE); if (f->xfer[0]->nframes == 1) { error = usb_fifo_alloc_buffer(f, @@ -160,11 +176,21 @@ f->xfer[0]->max_frame_size, 2 * f->xfer[0]->nframes); } + + f->flag_setup_busy = 0; + if (error) { + /* "usbd_transfer_unsetup()" sleeps and takes the + * lock itself -- drop it around the call, then + * restore the caller's "locked on return" state. */ + lockmgr(f->priv_lock, LK_RELEASE); usbd_transfer_unsetup(f->xfer, n_setup); + lockmgr(f->priv_lock, LK_EXCLUSIVE); } + return (error); } lockmgr(f->priv_lock, LK_EXCLUSIVE); + f->flag_setup_busy = 0; return (error); } |