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

Wesley Cheng <[email protected]>
Newsgroups gmane.linux.sound,gmane.linux.usb.general,gmane.linux.kernel
Message-ID <[email protected]>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.