[PATCH] xhci: keep ep0 blocked after disconnect until Address Device reloads it
From: Yiyu Zhou
Date: Tue Oct 27 2026 - 04:50:49 EST
After a root port disconnect or link error, xhci_handle_halted_endpoint()
sets EP_DROP_PENDING instead of resetting the endpoint, and gives back
its TDs. Their TRBs stay on the ring, and the endpoint's dequeue
pointer still points at the halted TD.
If the device comes back before hub_wq handles the disconnect, a driver
may reset it on the same slot. usbhid does this after 1 s of
interrupt-in errors. xhci_discover_or_reset_device() then clears
EP_DROP_PENDING on every endpoint. The other endpoints are disabled by
Reset Device and get new rings, but ep0 keeps its ring and dequeue
pointer. For USB 2 devices, usbcore by default sends a 64 byte
GET_DESCRIPTOR before SET_ADDRESS, and so before Address Device reloads
the ep0 context. That request is queued behind the stale TRBs. With a
Keychron M5 the reset stalled for 11 s, and the xHC then ran the old
TDs ("Event dma ... for ep 0 ... not part of TD"), writing into buffers
of URBs that had already been given back.
Keep EP_DROP_PENDING set on ep0 through Reset Device, and clear it when
an Address Device command succeeds. That command loads the ep0 context
from the input context, whose dequeue pointer is our enqueue pointer, so
the xHC skips the stale TRBs. Until then, ep0 requests fail with
-ENODEV. If Address Device comes first, as for SuperSpeed devices, the
device recovers in place. Otherwise the reset fails quickly, and
usbcore re-enumerates the device on a new slot, as after a real
disconnect. The same applies when Reset Device fails because the slot
was never addressed: the next enumeration attempt uses a new slot
instead of the stale ring.
Fixes: 042aad8d0db6 ("xhci: prevent endpoint recovery after roothub disconnect")
Fixes: 7e0ef4332ed9 ("xhci: avoid xHC endpoint changes after disconnect or link error.")
Reported-by: Dennis Wissel <dennis@xxxxxxxx>
Closes: https://lore.kernel.org/linux-usb/4MjB9K-2SoiA85WbI9iEig@xxxxxxxx/
Tested-by: Dennis Wissel <dennis@xxxxxxxx>
Signed-off-by: Yiyu Zhou <yiyu@xxxxxxxxxxx>
---
This is the RFC from the regression thread [1], unchanged apart from
Dennis's Tested-by. It applies on top of 1f36cc9fa949 ("xhci: give
back all TDs when skipping endpoint reset after disconnect") in
for-usb-linus and depends on it: without that patch, ep0 requests are
not rejected while EP_DROP_PENDING is set, so they would wait for their
timeouts. 7.3 needs both, so they should probably go to stable
together.
I used an LLM to trace the code and draft the patch.
[1] https://lore.kernel.org/linux-usb/20261026203944.2235991-1-yiyu@xxxxxxxxxxx/
drivers/usb/host/xhci.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index fa3012bf4c0f..15f4ee4aa2c3 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4091,7 +4091,13 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
ret = 0;
command_cleanup:
- for (i = 0; i < EP_CTX_PER_DEV; i++)
+ /*
+ * Reset Device leaves the ep0 ring and dequeue pointer as they were.
+ * After a disconnect or link error, ep0 may still point at TRBs of TDs
+ * that were given back, so keep it blocked until Address Device
+ * reloads its context.
+ */
+ for (i = 1; i < EP_CTX_PER_DEV; i++)
virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING;
xhci_free_command(xhci, reset_device_cmd);
@@ -4463,6 +4469,16 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
}
if (ret)
goto out;
+
+ /*
+ * Address Device loaded the ep0 context from the input context, so
+ * the xHC now starts at our enqueue pointer, past any TRBs of TDs
+ * given back after a disconnect or link error. Unblock ep0.
+ */
+ spin_lock_irqsave(&xhci->lock, flags);
+ virt_dev->eps[0].ep_state &= ~EP_DROP_PENDING;
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Op regs DCBAA ptr = %#016llx", temp_64);
base-commit: 1f36cc9fa9496189e95fe5762cde8a74e0cd9d68
--
2.55.0