Module Name: src Committed By: skrll Date: Thu Dec 29 09:52:00 UTC 2016
Modified Files: src/sys/dev/usb [nick-nhusb]: xhci.c Log Message: Mark device transfers as USBD_IN_PROGRESS appropriately and improve abort handling To generate a diff of this commit: cvs rdiff -u -r1.28.2.77 -r1.28.2.78 src/sys/dev/usb/xhci.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/xhci.c diff -u src/sys/dev/usb/xhci.c:1.28.2.77 src/sys/dev/usb/xhci.c:1.28.2.78 --- src/sys/dev/usb/xhci.c:1.28.2.77 Wed Oct 5 20:55:59 2016 +++ src/sys/dev/usb/xhci.c Thu Dec 29 09:52:00 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: xhci.c,v 1.28.2.77 2016/10/05 20:55:59 skrll Exp $ */ +/* $NetBSD: xhci.c,v 1.28.2.78 2016/12/29 09:52:00 skrll Exp $ */ /* * Copyright (c) 2013 Jonathan A. Kollasch @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.28.2.77 2016/10/05 20:55:59 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.28.2.78 2016/12/29 09:52:00 skrll Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -1560,12 +1560,13 @@ xhci_abort_xfer(struct usbd_xfer *xfer, xfer, xfer->ux_pipe, status, 0); KASSERT(mutex_owned(&sc->sc_lock)); + ASSERT_SLEEPABLE(); if (sc->sc_dying) { /* If we're dying, just do the software part. */ DPRINTFN(4, "xfer %p dying %u", xfer, xfer->ux_status, 0, 0); xfer->ux_status = status; - callout_stop(&xfer->ux_callout); + callout_halt(&xfer->ux_callout, &sc->sc_lock); usb_transfer_complete(xfer); return; } @@ -1592,10 +1593,16 @@ xhci_abort_xfer(struct usbd_xfer *xfer, xfer->ux_hcflags |= UXFER_ABORTING; /* - * Step 1: Stop xfer timeout timer. + * Step 1: When cancelling a transfer make sure the timeout handler + * didn't run or ran to the end and saw the USBD_CANCELLED status. + * Otherwise we must have got here via a timeout. */ - xfer->ux_status = status; - callout_stop(&xfer->ux_callout); + if (status == USBD_CANCELLED) { + xfer->ux_status = status; + callout_halt(&xfer->ux_callout, &sc->sc_lock); + } else { + KASSERT(xfer->ux_status == USBD_TIMEOUT); + } /* * Step 2: Stop execution of TD on the ring. @@ -1879,7 +1886,7 @@ xhci_event_transfer(struct xhci_softc * * UF_ENDPOINT_HALT). */ xfer->ux_status = err; - callout_stop(&xfer->ux_callout); + callout_halt(&xfer->ux_callout, &sc->sc_lock); xhci_clear_endpoint_stall_async(xfer); return; default: @@ -3629,17 +3636,18 @@ xhci_device_ctrl_start(struct usbd_xfer XHCI_TRB_3_IOC_BIT; xhci_trb_put(&xx->xx_trb[i++], parameter, status, control); + if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), + xhci_timeout, xfer); + } + xfer->ux_status = USBD_IN_PROGRESS; + mutex_enter(&tr->xr_lock); xhci_ring_put(sc, tr, xfer, xx->xx_trb, i); mutex_exit(&tr->xr_lock); xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci); - if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { - callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), - xhci_timeout, xfer); - } - return USBD_IN_PROGRESS; } @@ -3745,17 +3753,18 @@ xhci_device_bulk_start(struct usbd_xfer XHCI_TRB_3_IOC_BIT; xhci_trb_put(&xx->xx_trb[i++], parameter, status, control); + if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), + xhci_timeout, xfer); + } + xfer->ux_status = USBD_IN_PROGRESS; + mutex_enter(&tr->xr_lock); xhci_ring_put(sc, tr, xfer, xx->xx_trb, i); mutex_exit(&tr->xr_lock); xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci); - if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { - callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), - xhci_timeout, xfer); - } - return USBD_IN_PROGRESS; } @@ -3851,17 +3860,18 @@ xhci_device_intr_start(struct usbd_xfer XHCI_TRB_3_IOC_BIT; xhci_trb_put(&xx->xx_trb[i++], parameter, status, control); + if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), + xhci_timeout, xfer); + } + xfer->ux_status = USBD_IN_PROGRESS; + mutex_enter(&tr->xr_lock); xhci_ring_put(sc, tr, xfer, xx->xx_trb, i); mutex_exit(&tr->xr_lock); xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci); - if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) { - callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), - xhci_timeout, xfer); - } - return USBD_IN_PROGRESS; } @@ -3914,31 +3924,42 @@ xhci_device_intr_close(struct usbd_pipe static void xhci_timeout(void *addr) { + XHCIHIST_FUNC(); XHCIHIST_CALLED(); struct xhci_xfer * const xx = addr; struct usbd_xfer * const xfer = &xx->xx_xfer; struct xhci_softc * const sc = XHCI_XFER2SC(xfer); + bool timeout = false; - XHCIHIST_FUNC(); XHCIHIST_CALLED(); - + mutex_enter(&sc->sc_lock); if (sc->sc_dying) { + mutex_exit(&sc->sc_lock); return; } + if (xfer->ux_status != USBD_CANCELLED) { + xfer->ux_status = USBD_TIMEOUT; + timeout = true; + } + mutex_exit(&sc->sc_lock); - usb_init_task(&xfer->ux_aborttask, xhci_timeout_task, addr, - USB_TASKQ_MPSAFE); - usb_add_task(xx->xx_xfer.ux_pipe->up_dev, &xfer->ux_aborttask, - USB_TASKQ_HC); + if (timeout) { + struct usbd_device *dev = xfer->ux_pipe->up_dev; + + /* Execute the abort in a process context. */ + usb_init_task(&xfer->ux_aborttask, xhci_timeout_task, xfer, + USB_TASKQ_MPSAFE); + usb_add_task(dev, &xfer->ux_aborttask, USB_TASKQ_HC); + } } static void xhci_timeout_task(void *addr) { + XHCIHIST_FUNC(); XHCIHIST_CALLED(); struct usbd_xfer * const xfer = addr; struct xhci_softc * const sc = XHCI_XFER2SC(xfer); - XHCIHIST_FUNC(); XHCIHIST_CALLED(); - mutex_enter(&sc->sc_lock); + KASSERT(xfer->ux_status == USBD_TIMEOUT); xhci_abort_xfer(xfer, USBD_TIMEOUT); mutex_exit(&sc->sc_lock); }