Module Name: src Committed By: skrll Date: Wed Dec 28 10:25:06 UTC 2016
Modified Files: src/sys/dev/usb [nick-nhusb]: uhci.c uhcivar.h Log Message: Improve transfer abort To generate a diff of this commit: cvs rdiff -u -r1.264.4.78 -r1.264.4.79 src/sys/dev/usb/uhci.c cvs rdiff -u -r1.52.14.18 -r1.52.14.19 src/sys/dev/usb/uhcivar.h 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/uhci.c diff -u src/sys/dev/usb/uhci.c:1.264.4.78 src/sys/dev/usb/uhci.c:1.264.4.79 --- src/sys/dev/usb/uhci.c:1.264.4.78 Mon Dec 5 10:55:18 2016 +++ src/sys/dev/usb/uhci.c Wed Dec 28 10:25:06 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: uhci.c,v 1.264.4.78 2016/12/05 10:55:18 skrll Exp $ */ +/* $NetBSD: uhci.c,v 1.264.4.79 2016/12/28 10:25:06 skrll Exp $ */ /* * Copyright (c) 1998, 2004, 2011, 2012 The NetBSD Foundation, Inc. @@ -42,7 +42,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.264.4.78 2016/12/05 10:55:18 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.264.4.79 2016/12/28 10:25:06 skrll Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -574,8 +574,6 @@ uhci_init(uhci_softc_t *sc) callout_init(&sc->sc_poll_handle, CALLOUT_MPSAFE); - cv_init(&sc->sc_softwake_cv, "uhciab"); - /* Set up the bus struct. */ sc->sc_bus.ub_methods = &uhci_bus_methods; sc->sc_bus.ub_pipesize = sizeof(struct uhci_pipe); @@ -639,8 +637,6 @@ uhci_detach(struct uhci_softc *sc, int f callout_halt(&sc->sc_poll_handle, NULL); callout_destroy(&sc->sc_poll_handle); - cv_destroy(&sc->sc_softwake_cv); - mutex_destroy(&sc->sc_lock); mutex_destroy(&sc->sc_intr_lock); @@ -1422,11 +1418,6 @@ uhci_softintr(void *v) DPRINTF("ux %p", ux, 0, 0, 0); usb_transfer_complete(&ux->ux_xfer); } - - if (sc->sc_softwake) { - sc->sc_softwake = 0; - cv_broadcast(&sc->sc_softwake_cv); - } } /* Check for an interrupt. */ @@ -1482,8 +1473,6 @@ uhci_check_intr(uhci_softc_t *sc, struct if (!(status & UHCI_TD_ACTIVE)) { done: DPRINTFN(12, "ux=%p done", ux, 0, 0, 0); - - callout_stop(&xfer->ux_callout); uhci_idone(ux, cqp); return; } @@ -1554,18 +1543,30 @@ uhci_check_intr(uhci_softc_t *sc, struct void uhci_idone(struct uhci_xfer *ux, ux_completeq_t *cqp) { + UHCIHIST_FUNC(); UHCIHIST_CALLED(); struct usbd_xfer *xfer = &ux->ux_xfer; uhci_softc_t *sc __diagused = UHCI_XFER2SC(xfer); struct uhci_pipe *upipe = UHCI_PIPE2UPIPE(xfer->ux_pipe); uhci_soft_td_t *std; uint32_t status = 0, nstatus; + bool polling = sc->sc_bus.ub_usepolling; int actlen; KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); - UHCIHIST_FUNC(); UHCIHIST_CALLED(); DPRINTFN(12, "ux=%p", ux, 0, 0, 0); + /* + * Make sure the timeout handler didn't run or ran to the end + * and set the transfer status. + */ + callout_halt(&xfer->ux_callout, polling ? NULL : &sc->sc_lock); + if (xfer->ux_status == USBD_CANCELLED || + xfer->ux_status == USBD_TIMEOUT) { + DPRINTF("aborted xfer=%p", xfer, 0, 0, 0); + return; + } + #ifdef DIAGNOSTIC #ifdef UHCI_DEBUG if (ux->ux_isdone) { @@ -1699,26 +1700,32 @@ uhci_idone(struct uhci_xfer *ux, ux_comp void uhci_timeout(void *addr) { + UHCIHIST_FUNC(); UHCIHIST_CALLED(); struct usbd_xfer *xfer = addr; - struct uhci_xfer *uxfer = UHCI_XFER2UXFER(xfer); uhci_softc_t *sc = UHCI_XFER2SC(xfer); + bool timeout = false; - UHCIHIST_FUNC(); UHCIHIST_CALLED(); - - DPRINTF("uxfer %p", uxfer, 0, 0, 0); + DPRINTF("xfer %p", xfer, 0, 0, 0); + mutex_enter(&sc->sc_lock); if (sc->sc_dying) { - mutex_enter(&sc->sc_lock); - uhci_abort_xfer(xfer, USBD_TIMEOUT); mutex_exit(&sc->sc_lock); return; } + if (xfer->ux_status != USBD_CANCELLED) { + xfer->ux_status = USBD_TIMEOUT; + timeout = true; + } + mutex_exit(&sc->sc_lock); - /* Execute the abort in a process context. */ - usb_init_task(&xfer->ux_aborttask, uhci_timeout_task, xfer, - USB_TASKQ_MPSAFE); - usb_add_task(uxfer->ux_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, uhci_timeout_task, xfer, + USB_TASKQ_MPSAFE); + usb_add_task(dev, &xfer->ux_aborttask, USB_TASKQ_HC); + } } void @@ -1732,6 +1739,7 @@ uhci_timeout_task(void *addr) DPRINTF("xfer=%p", xfer, 0, 0, 0); mutex_enter(&sc->sc_lock); + KASSERT(xfer->ux_status == USBD_TIMEOUT); uhci_abort_xfer(xfer, USBD_TIMEOUT); mutex_exit(&sc->sc_lock); } @@ -2323,16 +2331,18 @@ uhci_device_bulk_abort(struct usbd_xfer } /* - * Abort a device request. - * If this routine is called at splusb() it guarantees that the request - * will be removed from the hardware scheduling and that the callback - * for it will be called with USBD_CANCELLED status. + * Cancel or timeout a device request. We have two cases to deal with + * + * 1) A driver wants to stop scheduled or inflight transfers + * 2) A transfer has timed out + * * It's impossible to guarantee that the requested transfer will not - * have happened since the hardware runs concurrently. - * If the transaction has already happened we rely on the ordinary - * interrupt processing to process it. - * XXX This is most probably wrong. - * XXXMRG this doesn't make sense anymore. + * have (partially) happened since the hardware runs concurrently. + * + * Transfer state is protected by the bus lock and we set the transfer status + * as soon as either of the above happens (with bus lock held). + * + * To allow the hardware time to notice we simply wait. */ void uhci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) @@ -2378,10 +2388,20 @@ uhci_abort_xfer(struct usbd_xfer *xfer, xfer->ux_hcflags |= UXFER_ABORTING; /* - * Step 1: Make interrupt routine and hardware ignore xfer. + * 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. + */ + 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: Make interrupt routine and hardware ignore xfer. */ - xfer->ux_status = status; /* make software ignore it */ - callout_stop(&xfer->ux_callout); uhci_del_intr_list(sc, ux); DPRINTF("stop ux=%p", ux, 0, 0, 0); @@ -2398,19 +2418,15 @@ uhci_abort_xfer(struct usbd_xfer *xfer, } /* - * Step 2: Wait until we know hardware has finished any possible + * Step 3: Wait until we know hardware has finished any possible * use of the xfer. Also make sure the soft interrupt routine * has run. */ /* Hardware finishes in 1ms */ usb_delay_ms_locked(upipe->pipe.up_dev->ud_bus, 2, &sc->sc_lock); - sc->sc_softwake = 1; - usb_schedsoftintr(&sc->sc_bus); - DPRINTF("cv_wait", 0, 0, 0, 0); - cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); /* - * Step 3: Execute callback. + * Step 4: Execute callback. */ DPRINTF("callback", 0, 0, 0, 0); #ifdef DIAGNOSTIC Index: src/sys/dev/usb/uhcivar.h diff -u src/sys/dev/usb/uhcivar.h:1.52.14.18 src/sys/dev/usb/uhcivar.h:1.52.14.19 --- src/sys/dev/usb/uhcivar.h:1.52.14.18 Sat Apr 30 10:34:14 2016 +++ src/sys/dev/usb/uhcivar.h Wed Dec 28 10:25:06 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: uhcivar.h,v 1.52.14.18 2016/04/30 10:34:14 skrll Exp $ */ +/* $NetBSD: uhcivar.h,v 1.52.14.19 2016/12/28 10:25:06 skrll Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -151,7 +151,6 @@ typedef struct uhci_softc { kmutex_t sc_lock; kmutex_t sc_intr_lock; - kcondvar_t sc_softwake_cv; uhci_physaddr_t *sc_pframes; usb_dma_t sc_dma; @@ -174,8 +173,6 @@ typedef struct uhci_softc { uint8_t sc_saved_sof; uint16_t sc_saved_frnum; - char sc_softwake; - char sc_isreset; char sc_suspend; char sc_dying;