From: Mathias Nyman Date: Wed, 19 Nov 2025 14:23:57 +0000 (+0200) Subject: xhci: simplify and rework trb_in_td() X-Git-Tag: v6.19-rc1~63^2~58 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86dcf43be869fe683923f577ddc3d04ec37ff581;p=thirdparty%2Fkernel%2Flinux.git xhci: simplify and rework trb_in_td() The trb_in_td() checking is quite complex, especially when checking for TRBs in ranges that can span several segments. Simplify the search by creating a position index for each TRB on the ring, and just compare the position indexes. Add a more generic dma_in_range() helper that checks if a trb dma address is in the range between a start and end trb and call it from trb_in_td() Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20251119142417.2820519-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7fccca7e1c629..aa7fc4d6f97c1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -160,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) } } +static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb) +{ + return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs); +} + /* Updates trb to point to the next TRB in the ring, and updates seg if the next * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. @@ -299,55 +304,34 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, inc_enq_past_link(xhci, ring, chain); } -/* - * If the suspect DMA address is a TRB in this TD, this function returns that - * TRB's segment. Otherwise it returns 0. - */ -static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +static bool dma_in_range(dma_addr_t dma, + struct xhci_segment *start_seg, union xhci_trb *start_trb, + struct xhci_segment *end_seg, union xhci_trb *end_trb) { - dma_addr_t start_dma; - dma_addr_t end_seg_dma; - dma_addr_t end_trb_dma; - struct xhci_segment *cur_seg; + unsigned int pos, start, end; + struct xhci_segment *pos_seg; + union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg); - start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); - cur_seg = td->start_seg; + /* Is the trb dma address even part of the whole ring? */ + if (!pos_trb) + return false; - do { - if (start_dma == 0) - return NULL; - /* We may get an event for a Link TRB in the middle of a TD */ - end_seg_dma = xhci_trb_virt_to_dma(cur_seg, - &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); - /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb); - - if (end_trb_dma > 0) { - /* The end TRB is in this segment, so suspect should be here */ - if (start_dma <= end_trb_dma) { - if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma) - return cur_seg; - } else { - /* Case for one segment with - * a TD wrapped around to the top - */ - if ((suspect_dma >= start_dma && - suspect_dma <= end_seg_dma) || - (suspect_dma >= cur_seg->dma && - suspect_dma <= end_trb_dma)) - return cur_seg; - } - return NULL; - } - /* Might still be somewhere in this segment */ - if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma) - return cur_seg; + pos = trb_to_pos(pos_seg, pos_trb); + start = trb_to_pos(start_seg, start_trb); + end = trb_to_pos(end_seg, end_trb); - cur_seg = cur_seg->next; - start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); - } while (cur_seg != td->start_seg); + /* end position is smaller than start, search range wraps around */ + if (end < start) + return !(pos > end && pos < start); - return NULL; + return (pos >= start && pos <= end); +} + +/* If the suspect DMA address is a TRB in this TD, this function returns true */ +static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +{ + return dma_in_range(suspect_dma, td->start_seg, td->start_trb, + td->end_seg, td->end_trb); } /*