]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xhci: simplify and rework trb_in_td()
authorMathias Nyman <mathias.nyman@linux.intel.com>
Wed, 19 Nov 2025 14:23:57 +0000 (16:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Nov 2025 13:52:59 +0000 (14:52 +0100)
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 <mathias.nyman@linux.intel.com>
Link: https://patch.msgid.link/20251119142417.2820519-4-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-ring.c

index 7fccca7e1c629bcb8e544e19efec82eceadda47a..aa7fc4d6f97c151d199ced80da702908fdaaac45 100644 (file)
@@ -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);
 }
 
 /*