Re: [RFC PATCH 1/2] xhci: prevent automatic endpoint restart after stall or error

Alan Stern
2026-04-18T14:56:59Z

On Sat, Apr 18, 2026 at 11:21:46AM +0200, Michal Pecio wrote:
> On Fri, 17 Apr 2026 22:34:58 -0400, Alan Stern wrote:
> > Okay, good, we'll require all HCDs to reset control endpoints
> > automatically after every error and stall.
>
> Are they not doing it?

As far as I know, they are. But it's not currently a requirement. In
fact, I don't know of any place in the source code where all the special
requirements for HCDs are listed. Probably the best locations are the
kerneldoc for usb_submit_urb() and usb_unlink_urb().

> Say that something like lsusb encounters protocol stall while another
> URB from the class driver is pending, will the other URB time out just
> because host endpoint halted on an earlier one?

Even if that were true now (although I don't think it is), it won't be
true once this new mechanism is added.

However, I did change my mind about one thing. It would be a good idea
for a control endpoint's queue to stop when a transaction error (not a
stall) occurs, until usb_endpoint_reset() is called. Or maybe
usb_restart_endpoint(), a new callback that I will add. (It's needed
for HCDs that stop the queue when there's a short packet and
URB_SHORT_NOT_OK is set; in this situation the endpoint doesn't need to
be reset, because the host side is still in sync with the device side.
The queue merely has to be restarted.)

This will allow the core's centralized backoff-and-retry approach to
prevent ep0 resubmissions and failures from getting into a tight loop
when a device disconnects.

> > > Currently, by the time the URB is given back, its endpoint is
> > > already in a "stopped but runnable" state and its sequence state is
> > > zeroed. And it may have already been restarted if there are more
> > > pending URBs.
> >
> > Ah, I was going to ask about that. This will be different from the
> > way bulk and interrupt endpoints will behave, but I think it is
> > acceptable. Control endpoints aren't used for anything that requires
> > high throughput; if a driver wants an error to prevent later
> > transfers from starting right away then it can simply avoid
> > submitting those later transfers until the earlier ones have
> > completed.

So this would have to be changed. It will be okay for the control
endpoint to be in a "stopped but runnable" state, but not okay for it to
be restarted. Not until the core explicitly restarts it.

> Or it could unlink if the async giveback race is fixed with a new
> callback separate from endpoint_reset(), but IDK if any demand exists.
>
> Same thing with "chain unlinking" - unlink one URB and expect others
> not to execute so that unlink completion can unlink them later. Looks
> odd, but it's guaranteed by kerneldocs. And currently broken.

I'm not sure what async giveback race you're talking about. In any
case, the proper way to unlink a sequence of URBs is in reverse order of
submission. That way there is no possibility for the HCD to skip some
URBs and then execute the later ones before they can be unlinked. This
is how usb_flush_endpoint() will work.

And I intend to change the kerneldoc.

> > > > Recovery from a transaction error on a bulk or interrupt endpoint
> > > > involves sending a Clear-Halt request to the device. But if the
> > > > error was caused by some sort of transient interference that
> > > > hasn't ended yet, the Clear-Halt might itself fail with the same
> > > > error. To handle this, we should retry the Clear-Halt at
> > > > increasing time intervals. At what point should the core give up?
> > >
> > > Good question, I don't know. One thing I noticed is that Windows
> > > does tend to lose patience with completely unresponsive devices and
> > > kicks them out, but I don't know the exact criteria.
> >
> > Two reasonable possibilities are 250 ms (because that's about how
> > long an intermediate hub might take to notify the core about a
> > disconnect) or 5 seconds (the normal timeout for control transfers).
> > Of course, 5 seconds is an awfully long time to wait for a mouse or
> > keyboard to recover, so maybe something in between would be best.
>
> What happens after giving up? If control requests don't work, most
> likely nothing works anyway. Reset may work, or not if it's bad cable.

As I see it, there are two possibilities: (1) Try doing a reset. (2) Do
nothing at all, leaving the device in a non-working state until the user
unplugs it.

Remember that the vast majority of transaction errors that will occur
will be the result of a device being unplugged while it is in use. What
we do in this case doesn't matter much.

> Retrying too long may cause class drivers to time out on pending URBs,
> not sure if it matters. Drivers may have no way to distinguish this
> from any other timeout, not sure if this matters either.

I suspect it doesn't.

> > I will set things up so that an extraneous clear-halt (such as one
> > submitted by the driver) will prevent the core from doing its own.
> > This leaves the possibility of the core clearing the halt and
> > restarting the endpoint and then the driver doing it again, while the
> > endpoint is running and the queue is nonempty. Hopefully drivers
> > avoid doing this.
>
> Yes, that's just dodgy, what would such driver even expect to happen?
> An URB may be in progress and then what? On xHCI we would need to throw
> out this URB, so it simply isn't supported.
>
> > But if it helps, I could print a warning if usb_clear_halt() is
> > called for an endpoint that isn't stopped and has a nonempty queue.
>
> Not sure what core considers a "stopped" endpoint. FYI, xhci-hcd
> logs dev_err() when reset is attempted while URBs are running.

Similar things can be done in the other HCDs. Good enough.

> > > A related issue is clearing TT buffers. AFAIK this has no retries,
> > > it fails silently and leaves the endpoint potentially broken, and
> > > it is waited for to complete in case of usb_set_interface().
> >
> > Is there anything we can do besides calling usb_clear_halt() and
> > usb_reset_endpoint()? If not, and data loss is unavoidable, then so
> > be it.
>
> If this "clear-halt by usbcore" materializes and survives confrotation
> with the real world, it could make sense to look into combining TT
> clearing with it. It's a similar thing, but tracked separately now.

That hadn't occurred to me, but sure.

> One thing that could reduce data loss is never giving up on those
> control requests, or resetting/disconnecting the device if giving up.
> It's a general problem that control requests can fail and nobody has
> much idea what to do then. Some drivers ignore errors. If the device
> returns to operation, it may end up running in an unknown state.
>
> This is apparently rare enough that nobody complains, though on
> low- and full-speed it's relatively easy to produce artificially with
> a particularly defective cable.

Yes. It seems that there isn't much the kernel can do; user
intervention will be needed.

And come to think of it, usb_reset_and_verify_device() does a logical
disconnect if the reset fails. So if (1) above doesn't work, there's
nothing left to do. In any case, we can settle this issue later on.

Alan Stern