[PATCH v2 1/4] xhci: sideband: fix ring sg table for sub-page TRB segments

From: Wesley Cheng

Date: Fri Aug 28 2026 - 17:38:57 EST


xhci_ring_to_sgtable() sized its pages[] array from
PAGE_ALIGN(sz) >> PAGE_SHIFT and always passed offset 0 to
sg_alloc_table_from_pages(). Since TRB_SEGMENT_SIZE (4096) can be
smaller than PAGE_SIZE, multiple ring segments can be packed into the
same physical page, and the first segment can start at a nonzero
offset within its page - PAGE_ALIGN(sz) undercounts the number of
distinct pages actually needed in that case, and a hardcoded offset
of 0 silently drops the first segment's true offset.

Take max_t() against ring->num_segs so there's always at least one
pages[] entry per segment, and capture the first segment's page
offset from dma_get_sgtable() to pass to sg_alloc_table_from_pages()
instead of assuming it's zero.

Assisted-by: Claude:claude-sonnet-5Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Wesley Cheng <wesley.cheng@xxxxxxxxxxxxxxxx>
---
drivers/usb/host/xhci-sideband.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
index a5deeee4d5dc..1585c47720e0 100644
--- a/drivers/usb/host/xhci-sideband.c
+++ b/drivers/usb/host/xhci-sideband.c
@@ -24,10 +24,14 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
struct device *dev;
size_t sz;
int i;
+ unsigned int first_seg_offset = 0;

dev = xhci_to_hcd(sb->xhci)->self.sysdev;
sz = ring->num_segs * TRB_SEGMENT_SIZE;
n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
+
+ /* TRB_SEGMENT_SIZE may be smaller than PAGE_SIZE; need one entry per segment */
+ n_pages = max_t(unsigned int, n_pages, ring->num_segs);
pages = kvmalloc_objs(struct page *, n_pages);
if (!pages)
return NULL;
@@ -51,11 +55,13 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
TRB_SEGMENT_SIZE);
pages[i] = sg_page(sgt->sgl);
+ if (i == 0)
+ first_seg_offset = sgt->sgl->offset;
sg_free_table(sgt);
seg = seg->next;
}

- if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
+ if (sg_alloc_table_from_pages(sgt, pages, n_pages, first_seg_offset, sz, GFP_KERNEL))
goto err;

kvfree(pages);

--
2.34.1