Re: Regression: webcam freezing since Linux 6.15
Michal Pecio <[email protected]>
| Newsgroups | org.kernel.vger.linux-usb |
|---|---|
| Message-ID | <[email protected]> |
On Mon, 27 Jul 2026 13:23:48 -0700, Bart Nagel wrote: > At 2026-07-25 11:59:56 +0200, Michal Pecio wrote: > > What happens differently in absence of CPU load? > > - no Missed Service Errors anymore > > - ep_trb_dma (see below) becomes always zero > > - no obvious change, somehow the kernel gets more lucky > > The log is a whole lot quieter. With ffplay it appears that no > uvc_v4l2_poll messages are produced. I ran for a few minutes and was > seeing no missed service errors at all. OK, no MSEs so no failures, that's obvious. > OK, I've done this. I disconnected all USB devices but that webcam and > my keyboard, and the hubs those two are connected through (otherwise > would be a pain but let me know if it would be helpful). Thanks, this dump is good enough, no noise from other devices. Let's see if the attached patch fixes it, it will print BAILING OUT each time the failure would otherwise happen and it should prevent failures. I think the problem occurs when a Short Packet event is generated for the first TRB of a two-TRB TD. The TD is completed and later a Missed Service Error event erronously points to the second TRB of the same TD. The driver can't identify this TD (it's gone) and goes nuts. I can't explain why we aren't getting a second Short Packet for the second TRB before we get MSE. Your HW does generate Short Packet for both TRBs in other similar cases visible in this event ring dump. I suspect that all those non-zero ep_trb_dma in MSE events are more or less bogus on your hardware, although obviously not all of them are instances of the aforementioned bug, because then the driver would malfunction on every such event, and we have seen that it doesn't. But let's start with testing if my guess is anywhere close to correct... Regards, Michal
webcam-crash-debug-fix.patch
(text/x-patch, 2.9 KB)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 24fde00fbb3f..89adf76e43be 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2683,6 +2683,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (!ep_ring)
return handle_transferless_tx_event(xhci, ep, trb_comp_code);
+ td = list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_list);
+
/* Look for common error cases */
switch (trb_comp_code) {
/* Skip codes that require special handling depending on
@@ -2779,8 +2781,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
ep->skip = true;
xhci_dbg(xhci,
- "Miss service interval error for slot %u ep %u, set skip flag%s\n",
- slot_id, ep_index, ep_trb_dma ? ", skip now" : "");
+ "Miss service interval error for slot %u ep %u ep_trb_dma %llx td_dma %llx, set skip flag\n",
+ slot_id, ep_index, ep_trb_dma, td ? xhci_trb_virt_to_dma(td->start_seg, td->start_trb) : ~0);
break;
case COMP_NO_PING_RESPONSE_ERROR:
ep->skip = true;
@@ -2822,13 +2824,22 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* We wait for the final IOC event, but if we get an event
* anywhere outside this TD, just give it back already.
*/
- td = list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_list);
-
if (td && td->error_mid_td && !trb_in_td(td, ep_trb_dma)) {
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
xhci_dequeue_td(xhci, td, ep_ring, td->status);
}
+ if (ep_ring->old_trb_comp_code == COMP_SHORT_PACKET && ep_trb_dma == ep_ring->old_td_end_dma) {
+ if (trb_comp_code != COMP_SHORT_PACKET)
+ xhci_info(xhci, "Event %d for old TD end DMA %llx after %lldus\n",
+ trb_comp_code, ep_trb_dma,
+ (ktime_get_ns() - ep_ring->old_time_ns) / 1000);
+ if (trb_comp_code == COMP_MISSED_SERVICE_ERROR) {
+ xhci_err(xhci, "BAILING OUT\n");
+ return 0;
+ }
+ }
+
/* If the TRB pointer is NULL, missed TDs will be skipped on the next event */
if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
return 0;
@@ -2937,6 +2948,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
} while (ep->skip);
ep_ring->old_trb_comp_code = trb_comp_code;
+ ep_ring->old_td_end_dma = xhci_trb_virt_to_dma(td->end_seg, td->end_trb);
+ ep_ring->old_time_ns = ktime_get_ns();
/* Get out if a TD was queued at enqueue after the xrun occurred */
if (ring_xrun_event)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 3ce71211ee6f..b508904b34ba 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1363,6 +1363,8 @@ struct xhci_ring {
union xhci_trb *dequeue;
struct xhci_segment *deq_seg;
struct list_head td_list;
+ dma_addr_t old_td_end_dma;
+ u64 old_time_ns;
/*
* Write the cycle state into the TRB cycle field to give ownership of
* the TRB to the host controller (if we are the producer), or to check