[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