From e4a78f10e4d0ce06186fcfb804fd0c80946af48a Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Fri, 13 May 2022 10:14:40 +0000 Subject: [PATCH] xhci(4): Handle race between software abort and hardware stall. --- sys/dev/usb/xhci.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c index bea067c3b4b5..df60bcb18371 100644 --- a/sys/dev/usb/xhci.c +++ b/sys/dev/usb/xhci.c @@ -2253,6 +2253,10 @@ xhci_pipe_restart_async_task(void *cookie) struct xhci_ring * const tr = xs->xs_xr[dci]; struct usbd_xfer *xfer; + XHCIHIST_FUNC(); + XHCIHIST_CALLARGS("sc=%#jx pipe=%#jx", + (uintptr_t)sc, (uintptr_t)pipe, 0, 0); + mutex_enter(&sc->sc_lock); xhci_pipe_restart(pipe); @@ -2263,8 +2267,26 @@ xhci_pipe_restart_async_task(void *cookie) */ tr->is_halted = false; xfer = SIMPLEQ_FIRST(&pipe->up_queue); - if (xfer) - (*pipe->up_methods->upm_start)(xfer); + if (xfer) { + /* + * If the first xfer of the queue is not in progress, + * though, there may be a concurrent software abort + * that has already cancelled it and is now in the + * middle of a concurrent xhci_pipe_restart waiting to + * reacquire the pipe (bus) lock. So only restart the + * xfer if it's still USBD_IN_PROGRESS. + * + * Either way, xfers on the queue can't be in + * USBD_NOT_STARTED. + */ + KASSERT(xfer->ux_status != USBD_NOT_STARTED); + if (xfer->ux_status == USBD_IN_PROGRESS) { + (*pipe->up_methods->upm_start)(xfer); + } else { + DPRINTF("pipe restart race xfer=%#jx status=%jd", + (uintptr_t)xfer, xfer->ux_status, 0, 0); + } + } mutex_exit(&sc->sc_lock); }