On Fri, Jan 01, 2021 at 06:56:21PM +0100, Marcus Glocker wrote: > It looks like with xhci(4) for some reason the bulk endpoints are > stalling after some operations, which isn't happening with ehci(4). I > currently can't see why this happens only with xhci(4). That's why > on your second attempt you see the timeouts happening, because the bulk > endpoints still are in a stalled state. > > Using usbd_clear_endpoint_stall() at the pipe closing routine simply > resets all the endpoints, which is why on the next attempt the > transfers are working fine again. That's probably why this was called > a workaround, because we should understand why the endpoints are > getting stalled in a first line with xhci(4). > > I can't tell why the issue disappears on your end when you use > debugging printfs in xhci_device_generic_start(). Looks like a > timing thing. If you like you can share your xhci(4) debugging diff, > and I'll check if I can see the same effect here.
In function xhci_device_generic_start() there are three printfs marked with `XXX slow XXX` comment and in blow diff only last one is commented out and that one is enough to make the problem go away. I think any of those tree `slow` printf() instances makes the problem go away. Please bare in mind `if (MMM_endpt == 0x88)` which narrow some printfs only to this specific endpoint. Hope that helps. If you need any more info, please let me know. diff refs/heads/openbsd refs/heads/xhci-stall blob - 6a27d3f64101e7f37fc4578e20c0b1f654bce0b8 blob + 74105be6385f36966d91bd01d9d241d22a9d0eb1 --- dev/usb/ugen.c +++ dev/usb/ugen.c @@ -270,6 +270,7 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", flag, mode, unit, endpt)); + printf("MMM ===== dv_xname=%s unit=%d endpt=%d [%s()|%s|%d]\n", sc->sc_dev.dv_xname, unit, endpt, __func__, __FILE_NAME__, __LINE__); if (sc == NULL || usbd_is_dying(sc->sc_udev)) return (ENXIO); @@ -481,6 +482,7 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct u_char buffer[UGEN_CHUNK]; DPRINTFN(5, ("%s: ugenread: %d\n", sc->sc_dev.dv_xname, endpt)); + printf("MMM dv_xname=%s endpt=%d [%s()|%s|%d]\n", sc->sc_dev.dv_xname, endpt, __func__, __FILE_NAME__, __LINE__); if (usbd_is_dying(sc->sc_udev)) return (EIO); @@ -499,8 +501,10 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct } #endif + printf("MMM dv_xname=%s endpt=%d xfertype=%d [%s()|%s|%d]\n", sc->sc_dev.dv_xname, endpt, UE_GET_XFERTYPE(sce->edesc->bmAttributes), __func__, __FILE_NAME__, __LINE__); switch (UE_GET_XFERTYPE(sce->edesc->bmAttributes)) { case UE_INTERRUPT: + printf("MMM start UE_INTERRUPT [%s()|%s|%d]\n", __func__, __FILE_NAME__, __LINE__); /* Block until activity occurred. */ s = splusb(); while (sce->q.c_cc == 0) { @@ -540,8 +544,10 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct if (error) break; } + printf("MMM end UE_INTERRUPT [%s()|%s|%d]\n", __func__, __FILE_NAME__, __LINE__); break; case UE_BULK: + printf("MMM start UE_BULK [%s()|%s|%d]\n", __func__, __FILE_NAME__, __LINE__); xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) return (ENOMEM); @@ -552,10 +558,12 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct flags |= USBD_CATCH; while ((n = ulmin(UGEN_BBSIZE, uio->uio_resid)) != 0) { DPRINTFN(1, ("ugenread: start transfer %zu bytes\n",n)); + /* printf("MMM dv_xname=%s endpt=%d n=%zu [%s()|%s|%d]\n", sc->sc_dev.dv_xname, endpt, n, __func__, __FILE_NAME__, __LINE__); */ usbd_setup_xfer(xfer, sce->pipeh, 0, buf, n, flags, sce->timeout, NULL); err = usbd_transfer(xfer); if (err) { + printf("MMM dv_xname=%s endpt=%d err=%d n=%zu timeout=%d flags=%d [%s()|%s|%d]\n", sc->sc_dev.dv_xname, endpt, err, n, sce->timeout, flags, __func__, __FILE_NAME__, __LINE__); usbd_clear_endpoint_stall(sce->pipeh); if (err == USBD_INTERRUPTED) error = EINTR; @@ -572,8 +580,10 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct break; } usbd_free_xfer(xfer); + printf("MMM end UE_BULK [%s()|%s|%d]\n", __func__, __FILE_NAME__, __LINE__); break; case UE_ISOCHRONOUS: + printf("MMM start UE_ISOCHRONOUS [%s()|%s|%d]\n", __func__, __FILE_NAME__, __LINE__); s = splusb(); while (sce->cur == sce->fill) { if (flag & IO_NDELAY) { @@ -613,6 +623,7 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct sce->cur = sce->ibuf; } splx(s); + printf("MMM end UE_ISOCHRONOUS [%s()|%s|%d]\n", __func__, __FILE_NAME__, __LINE__); break; @@ -631,8 +642,10 @@ ugenread(dev_t dev, struct uio *uio, int flag) sc = ugen_cd.cd_devs[UGENUNIT(dev)]; + printf("MMM>> dv_xname=%s endpt=%d flag=%d [%s()|%s|%d]\n", sc->sc_dev.dv_xname, endpt, flag, __func__, __FILE_NAME__, __LINE__); sc->sc_refcnt++; error = ugen_do_read(sc, endpt, uio, flag); + printf("MMM<< dv_xname=%s endpt=%d error=%d [%s()|%s|%d]\n", sc->sc_dev.dv_xname, endpt, error, __func__, __FILE_NAME__, __LINE__); if (--sc->sc_refcnt < 0) usb_detach_wakeup(&sc->sc_dev); return (error); blob - ecb17c3bf4ff58b0c1ccd3869d2f8b30195b7536 blob + 73161eff85ce058466822b609992e08306642c5e --- dev/usb/usbdi.c +++ dev/usb/usbdi.c @@ -306,8 +306,10 @@ usbd_transfer(struct usbd_xfer *xfer) printf("usbd_transfer: has old buffer!\n"); #endif err = usb_allocmem(bus, xfer->length, 0, 0, &xfer->dmabuf); - if (err) + if (err) { + printf("MMM polling=%d flags=%d running=%d usb_allocmem/err=%d [%s()|%s|%d]\n", polling, xfer->flags, pipe->running, err, __func__, __FILE_NAME__, __LINE__); return (err); + } xfer->rqflags |= URQ_AUTO_DMABUF; } @@ -327,12 +329,20 @@ usbd_transfer(struct usbd_xfer *xfer) } } - if (!(xfer->flags & USBD_SYNCHRONOUS)) + if (!(xfer->flags & USBD_SYNCHRONOUS)) { + /* printf("MMM polling=%d err=%d xfer->flags=%d non-USBD_SYNCHRONOUS [%s()|%s|%d]\n", polling, err, xfer->flags, __func__, __FILE_NAME__, __LINE__); */ return (err); + } + if (xfer->status != USBD_NOT_STARTED && xfer->status != USBD_IN_PROGRESS) { + /* printf("MMM polling=%d xfer->flags=%d xfer->status=%d err=%d [%s()|%s|%d]\n", polling, xfer->flags, xfer->status, err, __func__, __FILE_NAME__, __LINE__); */ + } + /* Sync transfer, wait for completion. */ - if (err != USBD_IN_PROGRESS) + if (err != USBD_IN_PROGRESS) { + /* printf("MMM polling=%d err=%d err!=USBD_IN_PROGRESS [%s()|%s|%d]\n", polling, err, __func__, __FILE_NAME__, __LINE__); */ return (err); + } s = splusb(); if (polling) { @@ -352,6 +362,7 @@ usbd_transfer(struct usbd_xfer *xfer) } if (timo < 0) { + printf("MMM polling=%d timeout [%s()|%s|%d]\n", polling, __func__, __FILE_NAME__, __LINE__); xfer->status = USBD_TIMEOUT; usb_transfer_complete(xfer); } @@ -360,15 +371,23 @@ usbd_transfer(struct usbd_xfer *xfer) flags = PRIBIO|(xfer->flags & USBD_CATCH ? PCATCH : 0); err = tsleep_nsec(xfer, flags, "usbsyn", INFSLP); + if (xfer->status != USBD_NOT_STARTED && xfer->status != USBD_NORMAL_COMPLETION) { + printf("MMM ! polling=%d flags=%d err=%d xfer->flags=%d xfer->status=%d xfer->done=%d [%s()|%s|%d]\n", polling, flags, err, xfer->flags, xfer->status, xfer->done, __func__, __FILE_NAME__, __LINE__); + } if (err && !xfer->done) { usbd_abort_pipe(pipe); - if (err == EINTR) + if (err == EINTR) { xfer->status = USBD_INTERRUPTED; - else + } else { + printf("MMM polling=%d timeout [%s()|%s|%d]\n", polling, __func__, __FILE_NAME__, __LINE__); xfer->status = USBD_TIMEOUT; + } } } } + if (xfer->status != USBD_NORMAL_COMPLETION) { + printf("MMM final polling=%d xfer->flags=%d xfer->status=%d [%s()|%s|%d]\n", polling, xfer->flags, xfer->status, __func__, __FILE_NAME__, __LINE__); + } splx(s); return (xfer->status); } @@ -716,7 +735,13 @@ usb_transfer_complete(struct usbd_xfer *xfer) struct usbd_bus *bus = pipe->device->bus; int polling = bus->use_polling; int status, flags; +/* + int MMM_endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; + if (MMM_endpt == 0x88) { + printf("MMM xHCI endpt=0x%x polling=%d [%s()|%s|%d]\n", MMM_endpt, polling, __func__, __FILE_NAME__, __LINE__); + } +*/ #if 0 /* XXX ohci_intr1() calls usb_transfer_complete() for RHSC. */ splsoftassert(IPL_SOFTUSB); @@ -735,13 +760,12 @@ usb_transfer_complete(struct usbd_xfer *xfer) if (polling) pipe->running = 0; -#ifdef DIAGNOSTIC if (xfer->actlen > xfer->length) { printf("%s: actlen > len %u > %u\n", __func__, xfer->actlen, xfer->length); + KASSERT(xfer->actlen <= xfer->length); xfer->actlen = xfer->length; } -#endif if (usbd_xfer_isread(xfer) && xfer->actlen != 0 && (xfer->flags & USBD_NO_COPY) == 0) blob - a016dd0e68da3fff63476e576fe48db816c810bc blob + 0c1bd1709db4d44cbeb38c41198d2ebfcbed458e --- dev/usb/xhci.c +++ dev/usb/xhci.c @@ -2328,12 +2328,17 @@ xhci_timeout(void *addr) { struct usbd_xfer *xfer = addr; struct xhci_softc *sc = (struct xhci_softc *)xfer->device->bus; + int MMM_endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; if (sc->sc_bus.dying) { xhci_timeout_task(addr); return; } + if (MMM_endpt == 0x88) { + printf("MMM xHCI endpt=0x%x [%s()|%s|%d]\n", MMM_endpt, __func__, __FILE_NAME__, __LINE__); + } + usb_init_task(&xfer->abort_task, xhci_timeout_task, addr, USB_TASK_TYPE_ABORT); usb_add_task(xfer->device, &xfer->abort_task); @@ -2343,9 +2348,18 @@ void xhci_timeout_task(void *addr) { struct usbd_xfer *xfer = addr; + struct usbd_xfer *xfer_loop; int s; + int MMM_endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; s = splusb(); + if (MMM_endpt == 0x88) { + printf("MMM xHCI endpt=0x%x [%s()|%s|%d]\n", MMM_endpt, __func__, __FILE_NAME__, __LINE__); + printf("%s: pipe=%p xfer=%p\n", __func__, xfer->pipe, xfer); + SIMPLEQ_FOREACH(xfer_loop, &xfer->pipe->queue, next) { + printf(" xfer=%p\n", xfer_loop); + } + } xhci_abort_xfer(xfer, USBD_TIMEOUT); splx(s); } @@ -2945,10 +2959,13 @@ usbd_status xhci_device_generic_transfer(struct usbd_xfer *xfer) { usbd_status err; + int MMM_endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; err = usb_insert_transfer(xfer); - if (err) + if (err) { + printf("MMM xHCI endpt=0x%x err=%d [%s()|%s|%d]\n", MMM_endpt, err, __func__, __FILE_NAME__, __LINE__); return (err); + } return (xhci_device_generic_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } @@ -2964,6 +2981,7 @@ xhci_device_generic_start(struct usbd_xfer *xfer) uint64_t paddr = DMAADDR(&xfer->dmabuf, 0); uint8_t toggle; int s, i, ntrb, zerotd = 0; + int MMM_endpt = xfer->pipe->endpoint->edesc->bEndpointAddress; KASSERT(!(xfer->rqflags & URQ_REQUEST)); @@ -2972,6 +2990,12 @@ xhci_device_generic_start(struct usbd_xfer *xfer) /* How many TRBs do we need for this transfer? */ ntrb = howmany(xfer->length, XHCI_TRB_MAXSIZE); + /* XXX slow XXX */ +/* + if (MMM_endpt == 0x88) { + printf("MMM xHCI endpt=0x%x xfer->status=%d ntrb=%d [%s()|%s|%d]\n", MMM_endpt, xfer->status, ntrb, __func__, __FILE_NAME__, __LINE__); + } +*/ /* If the buffer crosses a 64k boundary, we need one more. */ len = XHCI_TRB_MAXSIZE - (paddr & (XHCI_TRB_MAXSIZE - 1)); @@ -2979,11 +3003,18 @@ xhci_device_generic_start(struct usbd_xfer *xfer) ntrb = howmany(xfer->length - len, XHCI_TRB_MAXSIZE) + 1; else len = xfer->length; - + /* XXX slow XXX */ +/* + if (MMM_endpt == 0x88) { + printf("MMM xHCI endpt=0x%x xfer->status=%d ntrb=%d len=%d xfer->length=%d [%s()|%s|%d]\n", MMM_endpt, xfer->status, ntrb, len, xfer->length, __func__, __FILE_NAME__, __LINE__); + } +*/ /* If we need to append a zero length packet, we need one more. */ if ((xfer->flags & USBD_FORCE_SHORT_XFER || xfer->length == 0) && - (xfer->length % UE_GET_SIZE(mps) == 0)) + (xfer->length % UE_GET_SIZE(mps) == 0)) { zerotd = 1; + printf("MMM xHCI endpt=0x%x xfer->status=%d ntrb=%d len=%d xfer->length=%d zerotd=%d [%s()|%s|%d]\n", MMM_endpt, xfer->status, ntrb, len, xfer->length, zerotd, __func__, __FILE_NAME__, __LINE__); + } if (xp->free_trbs < (ntrb + zerotd)) return (USBD_NOMEM); @@ -3066,6 +3097,11 @@ xhci_device_generic_start(struct usbd_xfer *xfer) } splx(s); + /* XXX slow XXX */ + if (MMM_endpt == 0x88) { + printf("MMM xHCI endpt=0x%x xfer->status=%d [%s()|%s|%d]\n", MMM_endpt, xfer->status, __func__, __FILE_NAME__, __LINE__); + } + return (USBD_IN_PROGRESS); } -- Regards, Mikolaj