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

Reply via email to