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