[PATCH 2/2] xhci: check for a pending command completion during command timeout

From: Paul Menzel

Date: Fri May 22 2026 - 05:01:14 EST


From: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx>

It's possible a command times out even if xHC hardware already completed
the command. Driver is unaware of the command completion if interrupt
handler is blocked for a long time.

Check if there is an unhandled command completion on the event ring during
command timeout.

In this case just give the command additional time to complete. There's no
point in aborting the command ring to move past a stuck command.

Signed-off-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx>
Signed-off-by: George D Sworo <george.d.sworo@xxxxxxxxx>
Link: https://chromium.googlesource.com/chromiumos/third_party/kernel/+/478ab723af9414b0a2a2fbc59ac34f5d319a4fc3
[pmenzel: one adaptation for mainline 7.1: next_trb() uses the
2-argument form next_trb(&seg, &deq) — the mainline 7.1 signature
dropped the xhci and ring arguments present in the 6.12 source the
patch was ported from. xhci_pending_interrupt() is used directly as
it is now committed as the preceding prerequisite.]
Assisted-by: Claude Sonnet 4.6
[pmenzel: No devices with the problem available, but no regressions on
Dell XPS 13 9360 and QEMU 7.2.0.

qemu-system-x86_64 -enable-kvm -cpu host -m 3G -device qemu-xhci,id=xhci -device usb-storage,bus=xhci.0

xHCI host controller initialised cleanly, USB 3.0 SuperSpeed root
hubs and USB mass storage device enumerated without errors.
The specific race (command timeout with blocked interrupt handler)
cannot easily be forced in QEMU, but no regressions in the normal
command path were observed.]
Cc: Matt DeVillier <matt.devillier@xxxxxxxxx>
Signed-off-by: Paul Menzel <pmenzel@xxxxxxxxxxxxx>
---
drivers/usb/host/xhci-ring.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e47e644b296e..7f023558af22 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1714,6 +1714,28 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED, 0);
}

+static bool xhci_pending_command_completion(struct xhci_hcd *xhci)
+{
+ struct xhci_segment *seg = xhci->interrupters[0]->event_ring->deq_seg;
+ union xhci_trb *deq = xhci->interrupters[0]->event_ring->dequeue;
+ u32 deq_flags = le32_to_cpu(deq->event_cmd.flags);
+ u32 cycle = xhci->interrupters[0]->event_ring->cycle_state;
+ int i = 0;
+
+ /* Check if event ring contains an unhandled command completion */
+ while ((deq_flags & TRB_CYCLE) == cycle) {
+ if ((deq_flags & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_COMPLETION))
+ return true;
+ if (last_trb_on_ring(xhci->interrupters[0]->event_ring, seg, deq))
+ cycle ^= 1;
+ next_trb(&seg, &deq);
+ deq_flags = le32_to_cpu(deq->event_cmd.flags);
+ if (i++ > TRBS_PER_SEGMENT)
+ break;
+ }
+ return false;
+}
+
void xhci_handle_command_timeout(struct work_struct *work)
{
struct xhci_hcd *xhci;
@@ -1736,6 +1758,14 @@ void xhci_handle_command_timeout(struct work_struct *work)
return;
}

+ /* Did hw complete the command but event handler was blocked? */
+ if (xhci_pending_interrupt(xhci) > 0 &&
+ xhci_pending_command_completion(xhci)) {
+ xhci_dbg(xhci, "Command timeout with unhandled command completion\n");
+ xhci_mod_cmd_timer(xhci);
+ goto time_out_completed;
+ }
+
cmd_field3 = le32_to_cpu(xhci->current_cmd->command_trb->generic.field[3]);
usbsts = readl(&xhci->op_regs->status);
xhci_dbg(xhci, "Command timeout, USBSTS:%s\n", xhci_decode_usbsts(str, usbsts));
--
2.53.0