summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Pecio <michal.pecio@gmail.com>2024-02-29 17:14:35 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-03-02 22:25:27 +0300
commit91edf5a0c2fb3c1f59b709c5cda34b5f765be458 (patch)
treec0c6b9e9c5f6a99cf7077d4d317b26e1973fbfea
parent2e8dd2ded2a18c9ef83ad1c84547948fc64b6155 (diff)
downloadlinux-91edf5a0c2fb3c1f59b709c5cda34b5f765be458.tar.xz
xhci: fix matching completion events with TDs
A trb_in_td() call is used to determine if a completion event matches any TRB of the currently executing TD. This function is told to start searching right after the last finished TD, which is not at all where the currently expected TD is guaranteed to begin, because some TDs in between may have been cancelled. Not only is a pointless work performed, but a bug resulting in the HC executing cancelled TDs was seen to trick the driver into associating events from a TD just cancelled with an unrelated future TD. Since the ring is being traversed for the specific purpose of finding a match with the current TD, always start from its first TRB. This is the most reliable bit of information that we posses. Tracking of HC's work progress is not affected, except for cases when a misattributed event would have moved dequeue past a pending TD. Signed-off-by: Michal Pecio <michal.pecio@gmail.com> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20240229141438.619372-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/xhci-ring.c7
1 files changed, 3 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 95ed26114ee8..b2116501048c 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2813,7 +2813,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td_num--;
/* Is this a TRB in the currently executing TD? */
- ep_seg = trb_in_td(xhci, ep_ring->deq_seg, ep_ring->dequeue,
+ ep_seg = trb_in_td(xhci, td->start_seg, td->first_trb,
td->last_trb, ep_trb_dma, false);
/*
@@ -2881,9 +2881,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
"part of current TD ep_index %d "
"comp_code %u\n", ep_index,
trb_comp_code);
- trb_in_td(xhci, ep_ring->deq_seg,
- ep_ring->dequeue, td->last_trb,
- ep_trb_dma, true);
+ trb_in_td(xhci, td->start_seg, td->first_trb,
+ td->last_trb, ep_trb_dma, true);
return -ESHUTDOWN;
}
}