On Fri, Oct 18, 2019 at 12:54:06PM +0200, Stefan Sperling wrote:
> On Fri, Oct 18, 2019 at 11:37:01AM +0200, Martin Pieuchot wrote:
> > The question here is how an `xfer' with a status of NORMAL_COMPLETION
> > can be found in the pipe's queue?
>
> I don't know yet.
I have managed to track it down after realizing that the xfer which
triggers the problem is allocated very early on, in ulpt_open().
It it always is one of these (from ulpt.c):
sc->sc_in_xfer1 = usbd_alloc_xfer(sc->sc_udev);
sc->sc_in_xfer2 = usbd_alloc_xfer(sc->sc_udev);
Our friend which queues an xfer to the pipe after an error occured
is this xfer callback which lacks any error checking whatsoever:
static void
ulpt_input(struct usbd_xfer *xfer, void *priv, usbd_status status)
{
struct ulpt_softc *sc = priv;
DPRINTFN(2,("ulpt_input: got some data\n"));
/* Do it again. */
if (xfer == sc->sc_in_xfer1)
usbd_transfer(sc->sc_in_xfer2);
else
usbd_transfer(sc->sc_in_xfer1);
}
Proof that the problematic pipe is ulpt's sc->sc_in_pipe:
ulptwrite: transfer 16384 bytes
ulpt_input: xfer=0xfffffd811f8da0f0 status=0
(at this point sc_in_xfer1 has completed, and sc_in_xfer2 is queued)
xhci0: txerr? code 4
ulpt_input: xfer=0xfffffd811f8da2d0 status=13
(at this point sc_in_xfer2 has failed, and sc_in_xfer1 is queued)
usb0: usb_needs_explore
(the USB stack tries to detach the printer which has been turned off)
usb_add_task: task=0xffff800000093e80 state=0 type=1
usb_explore: usb0
usbd_do_request_flags
usbd_do_request_flags: new xfer=0xfffffd811f8da690
usbd_do_request_flags
usbd_do_request_flags: new xfer=0xfffffd811f8da690
ulpt_detach: sc=0xffff800000e63600
ulpt_detach: aborting pipe=0xffff800000ddd000
usbd_abort_pipe: pipe=0xffff800000ddd000
ulpt_detach: aborting pipe=0xffff800000dbc000
(ulpt attempts to abort sc->sc_in_pipe which has sc_in_xfer1 queued)
usbd_abort_pipe: pipe=0xffff800000dbc000
usbd_abort_pipe: pipe=0xffff800000dbc000 xfer=0xfffffd811f8da0f0
(methods=0xffffffff81f41508)
xhci_abort_xfer: xfer=0xfffffd811f8da0f0 status=NORMAL_COMPLETION err=CANCELLED
actlen=23 len=64 idx=-1
xhci_abort_xfer: already done
(endless loop as discussed before)
So I would suggest this diff:
diff b8f72638b63831677f92fc0c98e5d2d00058e226 /usr/src (staged changes)
blob - 9a6befb293d733e157123a8d07be191206a99a4c
blob + b5ac024f334b203411243c9148b801a69e289b8a
--- sys/dev/usb/ulpt.c
+++ sys/dev/usb/ulpt.c
@@ -433,7 +433,11 @@ ulpt_input(struct usbd_xfer *xfer, void *priv, usbd_st
{
struct ulpt_softc *sc = priv;
- DPRINTFN(2,("ulpt_input: got some data\n"));
+ DPRINTFN(2,("ulpt_input: xfer=%p status=%d\n", xfer, status));
+
+ if (status != USBD_NORMAL_COMPLETION)
+ return;
+
/* Do it again. */
if (xfer == sc->sc_in_xfer1)
usbd_transfer(sc->sc_in_xfer2);
Which results in a clean ulpt detach:
ulptwrite: transfer 16384 bytes
ulptwrite
usbd_do_request_flags
ulpt_status: status=0x18 err=0
ulptwrite: transfer 16384 bytes
xhci0: txerr? code 4
ulpt_input: xfer=0xfffffd811f8da2d0 status=13
xhci0: txerr? code 4
usbd_clear_endpoint_stall
usbd_do_request_flags
xhci0: txerr? code 4
ulptwrite: error=13
usb0: usb_needs_explore
usb_add_task: task=0xffff800000093e80 state=0 type=1
usb_explore: usb0
usbd_do_request_flags
usbd_do_request_flags
ulpt_detach: sc=0xffff800000e63900
ulpt_detach: aborting pipe=0xffff800000e73000
usbd_abort_pipe: pipe=0xffff800000e73000
ulpt_detach: aborting pipe=0xffff800000e80000
usbd_abort_pipe: pipe=0xffff800000e80000
xhci0: xhci_cmd_configure_ep dev 10
usbd_abort_pipe: pipe=0xffff800000e80000
xhci0: xhci_cmd_configure_ep dev 10
ulptclose: closed
ulpt0 detached
usbd_abort_pipe: pipe=0xffff800000ddd000
xhci0: xhci_cmd_configure_ep dev 10
xhci0: xhci_cmd_slot_control