Index: dev/usb/ehci.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/ehci.c,v retrieving revision 1.259 diff -p -u -u -r1.259 ehci.c --- dev/usb/ehci.c 6 Jun 2018 01:49:09 -0000 1.259 +++ dev/usb/ehci.c 3 Aug 2018 22:25:07 -0000 @@ -412,8 +412,7 @@ ehci_init(ehci_softc_t *sc) mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB); - cv_init(&sc->sc_softwake_cv, "ehciab"); - cv_init(&sc->sc_doorbell, "ehcidi"); + cv_init(&sc->sc_doorbell, "ehcidb"); sc->sc_xferpool = pool_cache_init(sizeof(struct ehci_xfer), 0, 0, 0, "ehcixfer", NULL, IPL_USB, NULL, NULL, NULL); @@ -757,8 +756,10 @@ Static void ehci_doorbell(void *addr) { ehci_softc_t *sc = addr; + EHCIHIST_FUNC(); EHCIHIST_CALLED(); mutex_enter(&sc->sc_lock); + sc->sc_dbanswered = true; cv_broadcast(&sc->sc_doorbell); mutex_exit(&sc->sc_lock); } @@ -859,11 +860,6 @@ ehci_softintr(void *v) !TAILQ_EMPTY(&sc->sc_intrhead)) callout_reset(&sc->sc_tmo_intrlist, hz, ehci_intrlist_timeout, sc); - - if (sc->sc_softwake) { - sc->sc_softwake = 0; - cv_broadcast(&sc->sc_softwake_cv); - } } Static void @@ -945,7 +941,6 @@ ehci_check_qh_intr(ehci_softc_t *sc, str } done: DPRINTFN(10, "ex=%#jx done", (uintptr_t)ex, 0, 0, 0); - callout_stop(&ex->ex_xfer.ux_callout); ehci_idone(ex, cq); } @@ -992,7 +987,6 @@ ehci_check_itd_intr(ehci_softc_t *sc, st return; done: DPRINTF("ex %#jx done", (uintptr_t)ex, 0, 0, 0); - callout_stop(&ex->ex_xfer.ux_callout); ehci_idone(ex, cq); } @@ -1030,7 +1024,6 @@ ehci_check_sitd_intr(ehci_softc_t *sc, s return; DPRINTFN(10, "ex=%#jx done", (uintptr_t)ex, 0, 0, 0); - callout_stop(&(ex->ex_xfer.ux_callout)); ehci_idone(ex, cq); } @@ -1038,19 +1031,24 @@ ehci_check_sitd_intr(ehci_softc_t *sc, s Static void ehci_idone(struct ehci_xfer *ex, ex_completeq_t *cq) { + EHCIHIST_FUNC(); EHCIHIST_CALLED(); struct usbd_xfer *xfer = &ex->ex_xfer; struct ehci_pipe *epipe = EHCI_XFER2EPIPE(xfer); struct ehci_softc *sc = EHCI_XFER2SC(xfer); ehci_soft_qtd_t *sqtd, *fsqtd, *lsqtd; uint32_t status = 0, nstatus = 0; int actlen = 0; + bool polling = sc->sc_bus.ub_usepolling; - EHCIHIST_FUNC(); EHCIHIST_CALLED(); - - KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); + KASSERT(polling || mutex_owned(&sc->sc_lock)); DPRINTF("ex=%#jx", (uintptr_t)ex, 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=%#jx", (uintptr_t)xfer, 0, 0, 0); @@ -1340,7 +1338,6 @@ ehci_detach(struct ehci_softc *sc, int f kmem_free(sc->sc_softitds, sc->sc_flsize * sizeof(ehci_soft_itd_t *)); cv_destroy(&sc->sc_doorbell); - cv_destroy(&sc->sc_softwake_cv); #if 0 /* XXX destroyed in ehci_pci.c as it controls ehci_intr access */ @@ -2172,22 +2169,27 @@ ehci_sync_hc(ehci_softc_t *sc) DPRINTF("dying", 0, 0, 0, 0); return; } + /* ask for doorbell */ EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); DPRINTF("cmd = 0x%08jx sts = 0x%08jx", EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); - error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); /* bell wait */ + sc->sc_dbanswered = false; + /* bell wait */ + while (!sc->sc_dbanswered) { + error = cv_timedwait(&sc->sc_doorbell, &sc->sc_lock, hz); - DPRINTF("cmd = 0x%08jx sts = 0x%08jx ... done", - EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); + DPRINTF("cmd = 0x%08jx sts = 0x%08jx ... done", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS), 0, 0); #ifdef DIAGNOSTIC - if (error == EWOULDBLOCK) { - printf("ehci_sync_hc: timed out\n"); - } else if (error) { - printf("ehci_sync_hc: cv_timedwait: error %d\n", error); - } + if (error == EWOULDBLOCK) { + printf("ehci_sync_hc: timed out\n"); + } else if (error) { + printf("ehci_sync_hc: cv_timedwait: error %d\n", error); + } #endif + } } Static void @@ -3103,16 +3105,19 @@ ehci_close_pipe(struct usbd_pipe *pipe, } /* - * 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. - * 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. + * 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 + * + * 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). + * + * Then we arrange for the hardware to tells us that it is not still + * processing the TDs by setting the QH halted bit and wait for the ehci + * door bell */ Static void ehci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) @@ -3136,8 +3141,9 @@ ehci_abort_xfer(struct usbd_xfer *xfer, if (sc->sc_dying) { /* If we're dying, just do the software part. */ - xfer->ux_status = status; /* make software ignore it */ - callout_stop(&xfer->ux_callout); + xfer->ux_status = status; + callout_halt(&xfer->ux_callout, &sc->sc_lock); + KASSERT(xfer->ux_status == status); usb_transfer_complete(xfer); return; } @@ -3148,10 +3154,8 @@ ehci_abort_xfer(struct usbd_xfer *xfer, */ if (xfer->ux_hcflags & UXFER_ABORTING) { DPRINTF("already aborting", 0, 0, 0, 0); -#ifdef DIAGNOSTIC - if (status == USBD_TIMEOUT) - printf("ehci_abort_xfer: TIMEOUT while aborting\n"); -#endif + KASSERT(status != USBD_TIMEOUT); + /* Override the status which might be USBD_TIMEOUT. */ xfer->ux_status = status; DPRINTF("waiting for abort to finish", 0, 0, 0, 0); @@ -3163,10 +3167,20 @@ ehci_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); ehci_del_intr_list(sc, exfer); usb_syncmem(&sqh->dma, @@ -3174,12 +3188,6 @@ ehci_abort_xfer(struct usbd_xfer *xfer, sizeof(sqh->qh.qh_qtd.qtd_status), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); qhstatus = sqh->qh.qh_qtd.qtd_status; - sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED); - usb_syncmem(&sqh->dma, - sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), - sizeof(sqh->qh.qh_qtd.qtd_status), - BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - if (exfer->ex_type == EX_CTRL) { fsqtd = exfer->ex_setup; lsqtd = exfer->ex_status; @@ -3187,32 +3195,36 @@ ehci_abort_xfer(struct usbd_xfer *xfer, fsqtd = exfer->ex_sqtdstart; lsqtd = exfer->ex_sqtdend; } - for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { - usb_syncmem(&sqtd->dma, - sqtd->offs + offsetof(ehci_qtd_t, qtd_status), - sizeof(sqtd->qtd.qtd_status), - BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); - sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED); - usb_syncmem(&sqtd->dma, - sqtd->offs + offsetof(ehci_qtd_t, qtd_status), - sizeof(sqtd->qtd.qtd_status), + if (!(qhstatus & htole32(EHCI_QTD_HALTED))) { + sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED); + usb_syncmem(&sqh->dma, + sqh->offs + offsetof(ehci_qh_t, qh_qtd.qtd_status), + sizeof(sqh->qh.qh_qtd.qtd_status), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); - if (sqtd == lsqtd) - break; + + for (sqtd = fsqtd; ; sqtd = sqtd->nextqtd) { + usb_syncmem(&sqtd->dma, + sqtd->offs + offsetof(ehci_qtd_t, qtd_status), + sizeof(sqtd->qtd.qtd_status), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED); + usb_syncmem(&sqtd->dma, + sqtd->offs + offsetof(ehci_qtd_t, qtd_status), + sizeof(sqtd->qtd.qtd_status), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + if (sqtd == lsqtd) + break; + } } /* - * Step 2: Wait until we know hardware has finished any possible - * use of the xfer. Also make sure the soft interrupt routine - * has run. + * Step 3: Wait until we know hardware has finished any possible + * use of the xfer. */ ehci_sync_hc(sc); - sc->sc_softwake = 1; - usb_schedsoftintr(&sc->sc_bus); - cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); /* - * Step 3: Remove any vestiges of the xfer from the hardware. + * Step 4: Remove any vestiges of the xfer from the hardware. * The complication here is that the hardware may have executed * beyond the xfer we're trying to abort. So as we're scanning * the TDs of this xfer we check if the hardware points to @@ -3253,7 +3265,7 @@ ehci_abort_xfer(struct usbd_xfer *xfer, } /* - * Step 4: Execute callback. + * Step 5: Execute callback. */ #ifdef DIAGNOSTIC exfer->ex_isdone = true; @@ -3289,8 +3301,10 @@ ehci_abort_isoc_xfer(struct usbd_xfer *x KASSERT(mutex_owned(&sc->sc_lock)); if (sc->sc_dying) { + /* If we're dying, just do the software part. */ xfer->ux_status = status; - callout_stop(&xfer->ux_callout); + callout_halt(&xfer->ux_callout, &sc->sc_lock); + KASSERT(xfer->ux_status == status); usb_transfer_complete(xfer); return; } @@ -3298,10 +3312,7 @@ ehci_abort_isoc_xfer(struct usbd_xfer *x if (xfer->ux_hcflags & UXFER_ABORTING) { DPRINTF("already aborting", 0, 0, 0, 0); -#ifdef DIAGNOSTIC - if (status == USBD_TIMEOUT) - printf("ehci_abort_isoc_xfer: TIMEOUT while aborting\n"); -#endif + KASSERT(status != USBD_TIMEOUT); xfer->ux_status = status; DPRINTF("waiting for abort to finish", 0, 0, 0, 0); @@ -3312,8 +3323,21 @@ ehci_abort_isoc_xfer(struct usbd_xfer *x } xfer->ux_hcflags |= UXFER_ABORTING; - xfer->ux_status = status; - callout_stop(&xfer->ux_callout); + /* + * 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. + */ ehci_del_intr_list(sc, exfer); if (xfer->ux_pipe->up_dev->ud_speed == USB_SPEED_HIGH) { @@ -3354,10 +3378,6 @@ ehci_abort_isoc_xfer(struct usbd_xfer *x } } - sc->sc_softwake = 1; - usb_schedsoftintr(&sc->sc_bus); - cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); - #ifdef DIAGNOSTIC exfer->ex_isdone = true; #endif @@ -3376,31 +3396,31 @@ done: Static void ehci_timeout(void *addr) { + EHCIHIST_FUNC(); EHCIHIST_CALLED(); struct usbd_xfer *xfer = addr; - struct ehci_xfer *exfer = EHCI_XFER2EXFER(xfer); - struct usbd_pipe *pipe = xfer->ux_pipe; - struct usbd_device *dev = pipe->up_dev; ehci_softc_t *sc = EHCI_XFER2SC(xfer); - EHCIHIST_FUNC(); EHCIHIST_CALLED(); - - DPRINTF("exfer %#jx", (uintptr_t)exfer, 0, 0, 0); + DPRINTF("xfer %#jx", (uintptr_t)xfer, 0, 0, 0); #ifdef EHCI_DEBUG - if (ehcidebug >= 2) + if (ehcidebug >= 2) { + struct usbd_pipe *pipe = xfer->ux_pipe; usbd_dump_pipe(pipe); + } #endif - if (sc->sc_dying) { - mutex_enter(&sc->sc_lock); - ehci_abort_xfer(xfer, USBD_TIMEOUT); + mutex_enter(&sc->sc_lock); + if (sc->sc_dying || xfer->ux_status != USBD_CANCELLED) { + if (!sc->sc_dying) + xfer->ux_status = USBD_TIMEOUT; mutex_exit(&sc->sc_lock); return; } + mutex_exit(&sc->sc_lock); /* Execute the abort in a process context. */ - usb_init_task(&exfer->ex_aborttask, ehci_timeout_task, xfer, + usb_init_task(&xfer->ux_aborttask, ehci_timeout_task, xfer, USB_TASKQ_MPSAFE); - usb_add_task(dev, &exfer->ex_aborttask, USB_TASKQ_HC); + usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, USB_TASKQ_HC); } Static void @@ -3414,6 +3434,7 @@ ehci_timeout_task(void *addr) DPRINTF("xfer=%#jx", (uintptr_t)xfer, 0, 0, 0); mutex_enter(&sc->sc_lock); + KASSERT(xfer->ux_status == USBD_TIMEOUT); ehci_abort_xfer(xfer, USBD_TIMEOUT); mutex_exit(&sc->sc_lock); } @@ -4468,7 +4489,6 @@ ehci_device_fs_isoc_transfer(struct usbd ehci_add_intr_list(sc, exfer); xfer->ux_status = USBD_IN_PROGRESS; - mutex_exit(&sc->sc_lock); return USBD_IN_PROGRESS; @@ -4862,7 +4882,6 @@ ehci_device_isoc_transfer(struct usbd_xf ehci_add_intr_list(sc, exfer); xfer->ux_status = USBD_IN_PROGRESS; - mutex_exit(&sc->sc_lock); return USBD_IN_PROGRESS; Index: dev/usb/ehcivar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/ehcivar.h,v retrieving revision 1.44 diff -p -u -u -r1.44 ehcivar.h --- dev/usb/ehcivar.h 9 Apr 2018 16:21:11 -0000 1.44 +++ dev/usb/ehcivar.h 3 Aug 2018 22:25:07 -0000 @@ -91,7 +91,6 @@ typedef struct ehci_soft_itd { struct ehci_xfer { struct usbd_xfer ex_xfer; - struct usb_task ex_aborttask; TAILQ_ENTRY(ehci_xfer) ex_next; /* list of active xfers */ enum { EX_NONE, @@ -165,6 +164,7 @@ typedef struct ehci_softc { device_t sc_dev; kmutex_t sc_lock; kmutex_t sc_intr_lock; + bool sc_dbanswered; kcondvar_t sc_doorbell; void *sc_doorbell_si; void *sc_pcd_si; @@ -208,8 +208,6 @@ typedef struct ehci_softc { uint8_t sc_istthreshold; /* ISOC Scheduling Threshold (uframes) */ struct usbd_xfer *sc_intrxfer; char sc_isreset[EHCI_MAX_PORTS]; - char sc_softwake; - kcondvar_t sc_softwake_cv; uint32_t sc_eintrs; ehci_soft_qh_t *sc_async_head; Index: dev/usb/ohci.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/ohci.c,v retrieving revision 1.281 diff -p -u -u -r1.281 ohci.c --- dev/usb/ohci.c 6 Jun 2018 01:49:09 -0000 1.281 +++ dev/usb/ohci.c 3 Aug 2018 22:25:07 -0000 @@ -382,8 +382,6 @@ ohci_detach(struct ohci_softc *sc, int f callout_halt(&sc->sc_tmo_rhsc, NULL); callout_destroy(&sc->sc_tmo_rhsc); - cv_destroy(&sc->sc_softwake_cv); - mutex_destroy(&sc->sc_lock); mutex_destroy(&sc->sc_intr_lock); @@ -788,7 +786,6 @@ ohci_init(ohci_softc_t *sc) mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB); - cv_init(&sc->sc_softwake_cv, "ohciab"); sc->sc_rhsc_si = softint_establish(SOFTINT_USB | SOFTINT_MPSAFE, ohci_rhsc_softint, sc); @@ -1387,12 +1384,23 @@ ohci_softintr(void *v) struct ohci_pipe *opipe; int len, cc; int i, j, actlen, iframes, uedir; - ohci_physaddr_t done; + ohci_physaddr_t done = 0; + bool polling = sc->sc_bus.ub_usepolling; - KASSERT(sc->sc_bus.ub_usepolling || mutex_owned(&sc->sc_lock)); + KASSERT(polling || mutex_owned(&sc->sc_lock)); OHCIHIST_FUNC(); OHCIHIST_CALLED(); + /* + * Only read hccadone if WDH is set - we might get here from places + * other than an interrupt + */ + if (!(OREAD4(sc, OHCI_INTERRUPT_STATUS) & OHCI_WDH)) { + DPRINTFN(10, "no WDH %#jx", (uintptr_t)sc, 0, 0, 0); + return; + } + + DPRINTFN(10, "WDH %#jx", (uintptr_t)sc, 0, 0, 0); usb_syncmem(&sc->sc_hccadma, offsetof(struct ohci_hcca, hcca_done_head), sizeof(sc->sc_hcca->hcca_done_head), BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); @@ -1459,14 +1467,21 @@ ohci_softintr(void *v) */ continue; } + + /* + * 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("cancel/timeout %#jx", (uintptr_t)xfer, 0, 0, 0); + /* Handled by abort routine. */ continue; } - callout_stop(&xfer->ux_callout); len = std->len; if (std->td.td_cbp != 0) @@ -1597,11 +1612,6 @@ ohci_softintr(void *v) } } - if (sc->sc_softwake) { - sc->sc_softwake = 0; - cv_broadcast(&sc->sc_softwake_cv); - } - DPRINTFN(10, "done", 0, 0, 0, 0); } @@ -1890,24 +1900,24 @@ void ohci_timeout(void *addr) { struct usbd_xfer *xfer = addr; - struct ohci_xfer *oxfer = OHCI_XFER2OXFER(xfer); ohci_softc_t *sc = OHCI_XFER2SC(xfer); OHCIHIST_FUNC(); OHCIHIST_CALLED(); - DPRINTF("oxfer=%#jx", (uintptr_t)oxfer, 0, 0, 0); + DPRINTF("xfer=%#jx", (uintptr_t)xfer, 0, 0, 0); - if (sc->sc_dying) { - mutex_enter(&sc->sc_lock); - ohci_abort_xfer(xfer, USBD_TIMEOUT); + mutex_enter(&sc->sc_lock); + if (sc->sc_dying || xfer->ux_status != USBD_CANCELLED) { + if (!sc->sc_dying) + xfer->ux_status = USBD_TIMEOUT; mutex_exit(&sc->sc_lock); return; } + mutex_exit(&sc->sc_lock); /* Execute the abort in a process context. */ - usb_init_task(&oxfer->abort_task, ohci_timeout_task, addr, + usb_init_task(&xfer->ux_aborttask, ohci_timeout_task, addr, USB_TASKQ_MPSAFE); - usb_add_task(xfer->ux_pipe->up_dev, &oxfer->abort_task, - USB_TASKQ_HC); + usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, USB_TASKQ_HC); } void @@ -2197,16 +2207,25 @@ ohci_close_pipe(struct usbd_pipe *pipe, } /* - * 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). + * + * Then we arrange for the hardware to tells us that it is not still + * processing the TDs by setting the sKip bit and requesting a SOF interrupt + * + * Once we see the SOF interrupt we can check the transfer TDs/iTDs to see if + * they've been processed and either + * a) if they're unused recover them for later use, or + * b) if they've been used allocate new TD/iTDs to replace those + * used. The softint handler will free the old ones. */ void ohci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) @@ -2228,7 +2247,7 @@ ohci_abort_xfer(struct usbd_xfer *xfer, if (sc->sc_dying) { /* If we're dying, just do the software part. */ - xfer->ux_status = status; /* make software ignore it */ + xfer->ux_status = status; callout_halt(&xfer->ux_callout, &sc->sc_lock); usb_transfer_complete(xfer); return; @@ -2254,11 +2273,26 @@ ohci_abort_xfer(struct usbd_xfer *xfer, } xfer->ux_hcflags |= UXFER_ABORTING; + /* + * 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 1: Make interrupt routine and hardware ignore xfer. + * Step 2: Unless the endpoint is already halted, we set the endpoint + * descriptor sKip bit and wait for hardware to complete processing. + * + * This includes ensuring that any TDs of the transfer that got onto + * the done list are also removed. We ensure this by waiting for + * both a WDH and SOF interrupt. */ - xfer->ux_status = status; /* make software ignore it */ - callout_stop(&xfer->ux_callout); DPRINTFN(1, "stop ed=%#jx", (uintptr_t)sed, 0, 0, 0); usb_syncmem(&sed->dma, sed->offs + offsetof(ohci_ed_t, ed_flags), sizeof(sed->ed.ed_flags), @@ -2270,14 +2304,10 @@ ohci_abort_xfer(struct usbd_xfer *xfer, /* * Step 2: Wait until we know hardware has finished any possible - * use of the xfer. Also make sure the soft interrupt routine - * has run. + * use of the xfer. */ /* Hardware finishes in 1ms */ usb_delay_ms_locked(opipe->pipe.up_dev->ud_bus, 20, &sc->sc_lock); - sc->sc_softwake = 1; - usb_schedsoftintr(&sc->sc_bus); - cv_wait(&sc->sc_softwake_cv, &sc->sc_lock); /* * Step 3: Remove any vestiges of the xfer from the hardware. @@ -2839,6 +2869,7 @@ ohci_device_ctrl_start(struct usbd_xfer DPRINTF("done", 0, 0, 0, 0); + xfer->ux_status = USBD_IN_PROGRESS; mutex_exit(&sc->sc_lock); return USBD_IN_PROGRESS; @@ -3047,6 +3078,8 @@ ohci_device_bulk_start(struct usbd_xfer callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), ohci_timeout, xfer); } + + xfer->ux_status = USBD_IN_PROGRESS; mutex_exit(&sc->sc_lock); return USBD_IN_PROGRESS; @@ -3232,6 +3265,7 @@ ohci_device_intr_start(struct usbd_xfer usb_syncmem(&sed->dma, sed->offs, sizeof(sed->ed), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + xfer->ux_status = USBD_IN_PROGRESS; mutex_exit(&sc->sc_lock); return USBD_IN_PROGRESS; Index: dev/usb/ohcivar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/ohcivar.h,v retrieving revision 1.59 diff -p -u -u -r1.59 ohcivar.h --- dev/usb/ohcivar.h 9 Apr 2018 16:21:11 -0000 1.59 +++ dev/usb/ohcivar.h 3 Aug 2018 22:25:07 -0000 @@ -118,9 +118,6 @@ typedef struct ohci_softc { int sc_flags; #define OHCIF_SUPERIO 0x0001 - char sc_softwake; - kcondvar_t sc_softwake_cv; - ohci_soft_ed_t *sc_freeeds; ohci_soft_td_t *sc_freetds; ohci_soft_itd_t *sc_freeitds; @@ -142,7 +139,6 @@ typedef struct ohci_softc { struct ohci_xfer { struct usbd_xfer xfer; - struct usb_task abort_task; /* ctrl */ ohci_soft_td_t *ox_setup; ohci_soft_td_t *ox_stat; Index: dev/usb/uhci.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/uhci.c,v retrieving revision 1.280 diff -p -u -u -r1.280 uhci.c --- dev/usb/uhci.c 9 Apr 2018 16:21:11 -0000 1.280 +++ dev/usb/uhci.c 3 Aug 2018 22:25:07 -0000 @@ -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 %#jx", (uintptr_t)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=%#jx done", (uintptr_t)ux, 0, 0, 0); - - callout_stop(&xfer->ux_callout); uhci_idone(ux, cqp); return; } @@ -1555,18 +1544,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=%#jx", (uintptr_t)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=%#jx", (uintptr_t)xfer, 0, 0, 0); + return; + } + #ifdef DIAGNOSTIC #ifdef UHCI_DEBUG if (ux->ux_isdone) { @@ -1701,26 +1702,25 @@ 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); - UHCIHIST_FUNC(); UHCIHIST_CALLED(); - - DPRINTF("uxfer %#jx", (uintptr_t)uxfer, 0, 0, 0); + DPRINTF("xfer %#jx", (uintptr_t)xfer, 0, 0, 0); - if (sc->sc_dying) { - mutex_enter(&sc->sc_lock); - uhci_abort_xfer(xfer, USBD_TIMEOUT); + mutex_enter(&sc->sc_lock); + if (sc->sc_dying || xfer->ux_status != USBD_CANCELLED) { + if (!sc->sc_dying) + xfer->ux_status = USBD_TIMEOUT; mutex_exit(&sc->sc_lock); return; } + mutex_exit(&sc->sc_lock); /* Execute the abort in a process context. */ - usb_init_task(&uxfer->ux_aborttask, uhci_timeout_task, xfer, + usb_init_task(&xfer->ux_aborttask, uhci_timeout_task, xfer, USB_TASKQ_MPSAFE); - usb_add_task(uxfer->ux_xfer.ux_pipe->up_dev, &uxfer->ux_aborttask, - USB_TASKQ_HC); + usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, USB_TASKQ_HC); } void @@ -1734,6 +1734,7 @@ uhci_timeout_task(void *addr) DPRINTF("xfer=%#jx", (uintptr_t)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); } @@ -2329,16 +2330,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) @@ -2384,10 +2387,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=%#jx", (uintptr_t)ux, 0, 0, 0); @@ -2404,19 +2417,14 @@ uhci_abort_xfer(struct usbd_xfer *xfer, } /* - * Step 2: Wait until we know hardware has finished any possible - * use of the xfer. Also make sure the soft interrupt routine - * has run. + * Step 3: Wait until we know hardware has finished any possible + * use of the xfer. */ /* 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 @@ -3332,18 +3340,15 @@ uhci_add_intr(uhci_softc_t *sc, uhci_sof eqh = vf->eqh; usb_syncmem(&eqh->dma, eqh->offs + offsetof(uhci_qh_t, qh_hlink), - sizeof(eqh->qh.qh_hlink), - BUS_DMASYNC_POSTWRITE); + sizeof(eqh->qh.qh_hlink), BUS_DMASYNC_POSTWRITE); sqh->hlink = eqh->hlink; sqh->qh.qh_hlink = eqh->qh.qh_hlink; usb_syncmem(&sqh->dma, sqh->offs + offsetof(uhci_qh_t, qh_hlink), - sizeof(sqh->qh.qh_hlink), - BUS_DMASYNC_PREWRITE); + sizeof(sqh->qh.qh_hlink), BUS_DMASYNC_PREWRITE); eqh->hlink = sqh; eqh->qh.qh_hlink = htole32(sqh->physaddr | UHCI_PTR_QH); usb_syncmem(&eqh->dma, eqh->offs + offsetof(uhci_qh_t, qh_hlink), - sizeof(eqh->qh.qh_hlink), - BUS_DMASYNC_PREWRITE); + sizeof(eqh->qh.qh_hlink), BUS_DMASYNC_PREWRITE); vf->eqh = sqh; vf->bandwidth++; } Index: dev/usb/uhcivar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/uhcivar.h,v retrieving revision 1.54 diff -p -u -u -r1.54 uhcivar.h --- dev/usb/uhcivar.h 9 Apr 2018 16:21:11 -0000 1.54 +++ dev/usb/uhcivar.h 3 Aug 2018 22:25:07 -0000 @@ -61,7 +61,6 @@ typedef union { struct uhci_xfer { struct usbd_xfer ux_xfer; - struct usb_task ux_aborttask; enum { UX_NONE, UX_CTRL, UX_BULK, UX_INTR, UX_ISOC } ux_type; @@ -152,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; @@ -175,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; Index: dev/usb/usbdi.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/usbdi.c,v retrieving revision 1.176 diff -p -u -u -r1.176 usbdi.c --- dev/usb/usbdi.c 31 Jul 2018 16:44:30 -0000 1.176 +++ dev/usb/usbdi.c 3 Aug 2018 22:25:07 -0000 @@ -912,7 +912,8 @@ usb_transfer_complete(struct usbd_xfer * xfer->ux_actlen); KASSERT(polling || mutex_owned(pipe->up_dev->ud_bus->ub_lock)); - KASSERT(xfer->ux_state == XFER_ONQU); + KASSERTMSG(xfer->ux_state == XFER_ONQU, "xfer %p state is %x", xfer, + xfer->ux_state); KASSERT(pipe != NULL); if (!repeat) { Index: dev/usb/usbdivar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/usbdivar.h,v retrieving revision 1.116 diff -p -u -u -r1.116 usbdivar.h --- dev/usb/usbdivar.h 29 Jun 2018 17:48:24 -0000 1.116 +++ dev/usb/usbdivar.h 3 Aug 2018 22:25:07 -0000 @@ -290,6 +290,7 @@ struct usbd_xfer { #define UXFER_ABORTWAIT 0x02 /* abort completion is being awaited. */ kcondvar_t ux_hccv; /* private use by the HC driver */ + struct usb_task ux_aborttask; struct callout ux_callout; }; Index: dev/usb/xhci.c =================================================================== RCS file: /cvsroot/src/sys/dev/usb/xhci.c,v retrieving revision 1.95 diff -p -u -u -r1.95 xhci.c --- dev/usb/xhci.c 18 Jul 2018 10:44:17 -0000 1.95 +++ dev/usb/xhci.c 3 Aug 2018 22:25:07 -0000 @@ -1705,13 +1705,14 @@ xhci_abort_xfer(struct usbd_xfer *xfer, (uintptr_t)xfer, (uintptr_t)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 %#jx dying %ju", (uintptr_t)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; } @@ -1738,10 +1739,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. @@ -2032,7 +2039,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: @@ -2042,12 +2049,8 @@ xhci_event_transfer(struct xhci_softc * } xfer->ux_status = err; - if ((trb_3 & XHCI_TRB_3_ED_BIT) != 0) { - if ((trb_0 & 0x3) == 0x0) { - callout_stop(&xfer->ux_callout); - usb_transfer_complete(xfer); - } - } else { + if ((trb_3 & XHCI_TRB_3_ED_BIT) == 0 || + (trb_0 & 0x3) == 0x0) { callout_stop(&xfer->ux_callout); usb_transfer_complete(xfer); } @@ -3801,17 +3804,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 && !xhci_polling_p(sc)) { + 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 && !xhci_polling_p(sc)) { - callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), - xhci_timeout, xfer); - } - return USBD_IN_PROGRESS; } @@ -3918,17 +3922,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 && !xhci_polling_p(sc)) { + 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 && !xhci_polling_p(sc)) { - callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), - xhci_timeout, xfer); - } - return USBD_IN_PROGRESS; } @@ -4027,6 +4032,11 @@ 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 && !polling) { + callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), + xhci_timeout, xfer); + } + if (!polling) mutex_enter(&tr->xr_lock); xhci_ring_put(sc, tr, xfer, xx->xx_trb, i); @@ -4035,11 +4045,6 @@ xhci_device_intr_start(struct usbd_xfer xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci); - if (xfer->ux_timeout && !polling) { - callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout), - xhci_timeout, xfer); - } - return USBD_IN_PROGRESS; } @@ -4093,31 +4098,35 @@ 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); - XHCIHIST_FUNC(); XHCIHIST_CALLED(); - - if (sc->sc_dying) { + mutex_enter(&sc->sc_lock); + if (sc->sc_dying || xfer->ux_status != USBD_CANCELLED) { + if (!sc->sc_dying) + xfer->ux_status = USBD_TIMEOUT; + mutex_exit(&sc->sc_lock); return; } + mutex_exit(&sc->sc_lock); - usb_init_task(&xx->xx_abort_task, xhci_timeout_task, addr, + /* Execute the abort in a process context. */ + usb_init_task(&xfer->ux_aborttask, xhci_timeout_task, xfer, USB_TASKQ_MPSAFE); - usb_add_task(xx->xx_xfer.ux_pipe->up_dev, &xx->xx_abort_task, - USB_TASKQ_HC); + usb_add_task(xfer->ux_pipe->up_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); } Index: dev/usb/xhcivar.h =================================================================== RCS file: /cvsroot/src/sys/dev/usb/xhcivar.h,v retrieving revision 1.9 diff -p -u -u -r1.9 xhcivar.h --- dev/usb/xhcivar.h 9 Apr 2018 16:21:11 -0000 1.9 +++ dev/usb/xhcivar.h 3 Aug 2018 22:25:07 -0000 @@ -35,7 +35,6 @@ struct xhci_xfer { struct usbd_xfer xx_xfer; - struct usb_task xx_abort_task; struct xhci_trb xx_trb[XHCI_XFER_NTRB]; }; @@ -85,7 +84,6 @@ struct xhci_softc { kmutex_t sc_lock; kmutex_t sc_intr_lock; - kcondvar_t sc_softwake_cv; pool_cache_t sc_xferpool; Index: external/bsd/dwc2/dwc2.c =================================================================== RCS file: /cvsroot/src/sys/external/bsd/dwc2/dwc2.c,v retrieving revision 1.49 diff -p -u -u -r1.49 dwc2.c --- external/bsd/dwc2/dwc2.c 9 Apr 2018 16:21:11 -0000 1.49 +++ external/bsd/dwc2/dwc2.c 3 Aug 2018 22:25:07 -0000 @@ -316,24 +316,23 @@ Static void dwc2_timeout(void *addr) { struct usbd_xfer *xfer = addr; - struct dwc2_xfer *dxfer = DWC2_XFER2DXFER(xfer); -// struct dwc2_pipe *dpipe = DWC2_XFER2DPIPE(xfer); struct dwc2_softc *sc = DWC2_XFER2SC(xfer); DPRINTF("dxfer=%p\n", dxfer); - if (sc->sc_dying) { - mutex_enter(&sc->sc_lock); - dwc2_abort_xfer(&dxfer->xfer, USBD_TIMEOUT); + mutex_enter(&sc->sc_lock); + if (sc->sc_dying || xfer->ux_status != USBD_CANCELLED) { + if (!sc->sc_dying) + xfer->ux_status = USBD_TIMEOUT; mutex_exit(&sc->sc_lock); return; } + mutex_exit(&sc->sc_lock); /* Execute the abort in a process context. */ - usb_init_task(&dxfer->abort_task, dwc2_timeout_task, addr, + usb_init_task(&xfer->ux_aborttask, dwc2_timeout_task, addr, USB_TASKQ_MPSAFE); - usb_add_task(dxfer->xfer.ux_pipe->up_dev, &dxfer->abort_task, - USB_TASKQ_HC); + usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_aborttask, USB_TASKQ_HC); } Static void @@ -345,6 +344,7 @@ dwc2_timeout_task(void *addr) DPRINTF("xfer=%p\n", xfer); mutex_enter(&sc->sc_lock); + KASSERT(xfer->ux_status == USBD_TIMEOUT); dwc2_abort_xfer(xfer, USBD_TIMEOUT); mutex_exit(&sc->sc_lock); } @@ -455,11 +455,12 @@ dwc2_abort_xfer(struct usbd_xfer *xfer, DPRINTF("xfer=%p\n", xfer); KASSERT(mutex_owned(&sc->sc_lock)); - KASSERT(!cpu_intr_p() && !cpu_softintr_p()); + ASSERT_SLEEPABLE(); if (sc->sc_dying) { xfer->ux_status = status; - callout_stop(&xfer->ux_callout); + callout_halt(&xfer->ux_callout, &sc->sc_lock); + KASSERT(xfer->ux_status == status); usb_transfer_complete(xfer); return; } @@ -479,16 +480,26 @@ dwc2_abort_xfer(struct usbd_xfer *xfer, /* * Step 1: Make the stack ignore it and stop the callout. */ - mutex_spin_enter(&hsotg->lock); xfer->ux_hcflags |= UXFER_ABORTING; - xfer->ux_status = status; /* make software ignore it */ - callout_stop(&xfer->ux_callout); + /* + * 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); + } + mutex_spin_enter(&hsotg->lock); /* XXXNH suboptimal */ TAILQ_FOREACH_SAFE(d, &sc->sc_complete, xnext, tmp) { if (d == dxfer) { TAILQ_REMOVE(&sc->sc_complete, dxfer, xnext); + break; } }