Linux USB
 help / color / mirror / Atom feed
From: Mathias Nyman <mathias.nyman@linux.intel.com>
To: dylan_robinson@motu.com, michal.pecio@gmail.com
Cc: mathias.nyman@linux.intel.com, linux-usb@vger.kernel.org,
	mathias.nyman@intel.com, stern@rowland.harvard.edu
Subject: [RFT PATCHv3 3/3] xhci: tune urb->start_frame in ring overrun and underrun cases
Date: Thu, 21 May 2026 18:27:14 +0300	[thread overview]
Message-ID: <20260521152715.288995-3-mathias.nyman@linux.intel.com> (raw)
In-Reply-To: <20260521152715.288995-1-mathias.nyman@linux.intel.com>

Make sure a new urb->start_frame is calculated for isoc URBs submitted late
mid stream, i.e. isoc URB is submitted to an empty ring after a ring
underrun or overrun event.

If URB was queud late, but before xhci driver handles the ring
underrun/overrun event then kick the urb->start_frame forward by one
ESIT for that delayed URB.

the gap in urb->start_frame allows class drivers to detect the glitch
in isochronous transfers.

if xHCI uses Contiguous Frame ID Capability (CFC) then xHC will
synchronize each TD to its frame ID, and the late TDs of the URB
will be skipped instead of lagging the transfer, so don't touch
urb->start_frame or ep->next_uframe in CFC cases

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 drivers/usb/host/xhci-ring.c | 76 ++++++++++++++++++++++--------------
 drivers/usb/host/xhci.h      |  1 +
 2 files changed, 48 insertions(+), 29 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 55a10d1e70cb..fa431640861d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2624,6 +2624,40 @@ static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci,
 		return false;
 	}
 }
+/* called for isoc enpoints to handle overrun and underrun transfer events */
+static int handle_tx_event_overunderrun(struct xhci_virt_ep *ep)
+{
+	struct xhci_td *td;
+	struct urb *urb;
+	u32 uinterval;
+
+	/* don't touch ep->next_uframe and urb->start_frame if CFC frame sync is used */
+	if (!ep->last_td_used_sia)
+		return 0;   // what about skip??
+
+	/* re-calculate urb->start_frame on next urb enqueue */
+	if (list_empty(&ep->ring->td_list)) {
+		ep->next_uframe = -2; // FIXME define something, or use suitable -EXXX
+		ep->skip = false;  // maybe, or is it the wrong place?
+		return 0;
+	}
+
+	/* ep has tds, but queued late for its ESIT, adjust start_frame and next_uframe */
+	/* FIXME may have several URBs queued (unlikely) */
+	td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
+	urb = td->urb;
+	uinterval = urb->interval;
+
+	if (urb->dev->speed == USB_SPEED_LOW || urb->dev->speed == USB_SPEED_FULL)
+		uinterval *= 8;
+
+	ep->next_uframe += uinterval;
+	urb->start_frame += urb->interval;
+
+	return 0;
+	/* FIXME: ep_ring->old_trb_comp_code = trb_comp_code; */
+	/* FIXME: if (ep->skip) */
+}
 
 /*
  * If this function returns an error condition, it means it got a Transfer
@@ -2644,7 +2678,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 	int status = -EINPROGRESS;
 	struct xhci_ep_ctx *ep_ctx;
 	u32 trb_comp_code;
-	bool ring_xrun_event = false;
 
 	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
 	ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2754,11 +2787,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		 * Underrun Event for OUT Isoch endpoint.
 		 */
 		xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
-		ring_xrun_event = true;
 		break;
 	case COMP_RING_OVERRUN:
 		xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
-		ring_xrun_event = true;
 		break;
 	case COMP_MISSED_SERVICE_ERROR:
 		/*
@@ -2823,6 +2854,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 	if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
 		return 0;
 
+	if (trb_comp_code == COMP_RING_UNDERRUN || trb_comp_code == COMP_RING_OVERRUN)
+		return handle_tx_event_overunderrun(ep);
+
 	if (list_empty(&ep_ring->td_list)) {
 		/*
 		 * Don't print wanings if ring is empty due to a stopped endpoint generating an
@@ -2832,7 +2866,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		 */
 		if (trb_comp_code != COMP_STOPPED &&
 		    trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
-		    !ring_xrun_event &&
 		    !xhci_spurious_success_tx_event(xhci, ep_ring)) {
 			xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
 				  slot_id, ep_index);
@@ -2856,20 +2889,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 
 				skip_isoc_td(xhci, td, ep, status);
 
-				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",
-								slot_id, ep_index);
-						return 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);
@@ -2878,10 +2899,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 				goto check_endpoint_halted;
 			}
 
-			/* TD was queued after xrun, maybe xrun was on a link, don't panic yet */
-			if (ring_xrun_event)
-				return 0;
-
 			/*
 			 * Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
 			 * TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
@@ -2926,10 +2943,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 
 	ep_ring->old_trb_comp_code = trb_comp_code;
 
-	/* Get out if a TD was queued at enqueue after the xrun occurred */
-	if (ring_xrun_event)
-		return 0;
-
 	trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma);
 
 	/*
@@ -4042,11 +4055,14 @@ static int xhci_get_isoc_start_frame(struct xhci_hcd *xhci, struct urb *urb,
 
 	// FIXME, used to check if !list_empty(&ep_ring->td_list)), is that reliable
 
-	/* Is this the first URB starting the whole isoc transfer */
+	/*  first URB starting the whole isoc transfer, or restart after over/under-run */
 	if (ep->next_uframe < 0) {
-		/* align first URB to next interval boundary, or at last to full frame */
-		start_uframe = mfindex + ist + XHCI_CFC_DELAY;
-		start_uframe = roundup(start_uframe, 8);
+		start_uframe = mfindex + ist;
+		/* if first URB then add additinal delay and align to full frame */
+		if (ep->next_uframe == -1) {
+			start_uframe += XHCI_CFC_DELAY;
+			start_uframe = roundup(start_uframe, 8);
+		}
 		start_uframe = roundup(start_uframe, uinterval) % MAX_UFRAMES;
 	} else {
 		/* URB is mid stream and expected to handle the next frame */
@@ -4166,8 +4182,10 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		if (xhci_isoc_td_uses_frame_id(xhci, urb, xep, i)) {
 			sia_frame_id = (start_uframe + i * uinterval) / 8;
 			sia_frame_id = TRB_FRAME_ID(sia_frame_id % MAX_FRAMES);
+			xep->last_td_used_sia = false;
 		} else {
 			sia_frame_id = TRB_SIA;
+			xep->last_td_used_sia = true;
 		}
 
 		/*
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index bcb7b8c877db..19d6f3104a15 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -702,6 +702,7 @@ struct xhci_virt_ep {
 	unsigned long		stop_time;
 	/* Isoch Frame ID checking storage */
 	int			next_uframe;
+	bool			last_td_used_sia;
 	/* Use new Isoch TRB layout needed for extended TBC support */
 	bool			use_extended_tbc;
 	/* set if this endpoint is controlled via sideband access*/
-- 
2.43.0


  parent reply	other threads:[~2026-05-21 15:27 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-04 21:25 [Bug 220748] New: usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set bugzilla-daemon
2025-11-05  8:40 ` [Bug 220748] " bugzilla-daemon
2025-11-05  8:48 ` bugzilla-daemon
2025-11-05  8:53 ` bugzilla-daemon
2025-11-05  9:06 ` bugzilla-daemon
2025-11-05 15:47 ` bugzilla-daemon
2025-11-05 18:28 ` bugzilla-daemon
2025-11-05 18:30 ` bugzilla-daemon
2025-11-05 20:16 ` bugzilla-daemon
2025-11-05 21:18 ` bugzilla-daemon
2025-11-06  3:52 ` bugzilla-daemon
2025-11-06  8:27 ` bugzilla-daemon
2025-11-06  8:35 ` bugzilla-daemon
2025-11-06 15:03 ` bugzilla-daemon
2025-11-08 10:33 ` bugzilla-daemon
2025-11-08 16:09 ` bugzilla-daemon
2025-11-10 10:23 ` bugzilla-daemon
2025-11-10 10:42 ` bugzilla-daemon
2026-05-04 23:53 ` bugzilla-daemon
2026-05-04 23:54 ` bugzilla-daemon
2026-05-05  1:14 ` bugzilla-daemon
2026-05-05  9:59 ` bugzilla-daemon
2026-05-05 17:09 ` bugzilla-daemon
2026-05-05 17:10 ` bugzilla-daemon
2026-05-05 17:10 ` bugzilla-daemon
2026-05-05 17:13 ` bugzilla-daemon
2026-05-06 13:32 ` bugzilla-daemon
2026-05-06 15:03 ` bugzilla-daemon
2026-05-07  2:38   ` Alan Stern
2026-05-07 16:17     ` Dylan Robinson
2026-05-07 17:24       ` Alan Stern
2026-05-07 21:16         ` Dylan Robinson
2026-05-08  3:02           ` Alan Stern
2026-05-08 17:20             ` Dylan Robinson
2026-05-09  1:25               ` Alan Stern
2026-05-09 22:12               ` Michal Pecio
2026-05-10 12:39                 ` Dylan Robinson
2026-05-11 19:21                   ` [RFT PATCH] xhci: fix frame id calculation for isoc transfer Mathias Nyman
2026-05-11 19:36                     ` Mathias Nyman
2026-05-12  9:08                     ` Michal Pecio
2026-05-13 14:30                       ` Mathias Nyman
2026-05-13 14:35                         ` [RFT PATCHv2 1/2] xhci: fix frame id calculation and checks for isoc URBs Mathias Nyman
2026-05-13 14:35                           ` [RFT PATCHv2 2/2] xhci: Set frame ID field of isoc TRB when starting an isoch stream Mathias Nyman
2026-05-14 21:16                         ` [RFT PATCH] xhci: fix frame id calculation for isoc transfer Dylan Robinson
2026-05-14 22:10                           ` Dylan Robinson
2026-05-15  4:32                           ` Michal Pecio
2026-05-15 18:13                             ` Dylan Robinson
2026-05-18  7:13                               ` Michal Pecio
2026-05-21 14:20                                 ` Dylan Robinson
2026-05-21 15:24                                   ` Mathias Nyman
2026-05-21 15:27                                     ` [RFT PATCHv3 1/3] xhci: fix frame id calculation and checks for isoc URBs Mathias Nyman
2026-05-21 15:27                                       ` [RFT PATCHv3 2/3] xhci: Set frame ID field of isoc TRB when starting an isoch stream Mathias Nyman
2026-05-21 15:27                                       ` Mathias Nyman [this message]
2026-05-07 21:54       ` [Bug 220748] usb: xhci_queue_isoc_tx_prepare ignore start_frame and always assumes URB_ISO_ASAP is set Michal Pecio
2026-05-08  3:09         ` Alan Stern
2026-05-08  9:41           ` Michal Pecio
2026-05-08 14:54             ` Alan Stern
2026-05-08 21:39         ` Dylan Robinson
2026-05-09 11:10           ` Michal Pecio
2026-05-09 20:18             ` Dylan Robinson
2026-05-11 19:15 ` bugzilla-daemon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260521152715.288995-3-mathias.nyman@linux.intel.com \
    --to=mathias.nyman@linux.intel.com \
    --cc=dylan_robinson@motu.com \
    --cc=linux-usb@vger.kernel.org \
    --cc=mathias.nyman@intel.com \
    --cc=michal.pecio@gmail.com \
    --cc=stern@rowland.harvard.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox