[PATCH 2/5] usb: xhci: Guarantee URB giveback on Ring Underrun/Overrun
Michal Pecio <[email protected]> Tue, 4 Aug 2026 12:03:14 +0200
| Newsgroups | org.kernel.vger.linux-usb,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
In this case we know that the xHC has released ownership of all missed TDs, we only don't know which were missed and which were queued later. URBs are queued atomically, so we can safely give back all TDs of the currently executing URB. Unlike the previous policy, this does actually ensure that the class driver will learn about the error and won't see all of its URBs still in progress when all TDs are missed on xHCI 1.0. Signed-off-by: Michal Pecio <[email protected]> --- drivers/usb/host/xhci-ring.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 38a0f895553a..8eed56b72c30 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2630,6 +2630,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, unsigned int slot_id; int ep_index; struct xhci_td *td = NULL; + struct urb *missed_urb = NULL; dma_addr_t ep_trb_dma; union xhci_trb *ep_trb; int status = -EINPROGRESS; @@ -2849,26 +2850,31 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; /* - * TD was missed, skip it. Core already initialized frame->status - * to -EXDEV and frame->actual_length to 0, nothing more to do. + * If skip flag is still set at xrun, we are on xHCI 1.0 and our TRB + * pointer is zero again. All missed TDs can be given back, but we + * don't know which were missed and which were queued after the xrun + * occurred. We can safely give back the first pending URB. */ - xhci_dequeue_td(xhci, td, ep_ring, 0); + if (ring_xrun_event) { + if (!missed_urb) + missed_urb = td->urb; - if (!list_empty(&ep_ring->td_list)) { - if (ring_xrun_event) { - /* - * If we are here, we are on xHCI 1.0 host with no - * idea how many TDs were missed or where the xrun - * occurred. New TDs may have been added after the - * xrun, so skip only one TD to be safe. - */ - xhci_dbg(xhci, "Skipped one TD for slot %u ep %u", + if (td->urb != missed_urb) { + xhci_dbg(xhci, "Skipped one URB for slot %u ep %u", slot_id, ep_index); return 0; } - continue; } + /* + * TD was missed, skip it. Core already initialized frame->status + * to -EXDEV and frame->actual_length to 0, nothing more to do. + */ + xhci_dequeue_td(xhci, td, ep_ring, 0); + + if (!list_empty(&ep_ring->td_list)) + continue; + xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n", slot_id, ep_index); ep->skip = false; -- 2.48.1