On 9/29/26 07:48, Karl Mehltretter wrote:
get_sg_table() merges physically contiguous pages without accounting for the mapping device's maximum segment size. This affects both importer mappings and the udmabuf misc device mapping used for CPU access.
With DMA_API_DEBUG enabled, DMA_BUF_IOCTL_SYNC on a 64 MiB udmabuf reports:
DMA-API: misc udmabuf: mapping sg segment longer than device claims to support [len=65884160] [max=65536]
Use sg_alloc_table_from_pages_segment() with the mapping device's maximum segment size. Return -EINVAL if the limit is smaller than PAGE_SIZE because the page-based allocator cannot honor it.
Before commit 5bf888673e0d ("udmabuf: Do not create malformed scatterlists"), each entry covered one page.
Fixes: 5bf888673e0d ("udmabuf: Do not create malformed scatterlists") Reviewed-by: Jason Gunthorpe jgg@nvidia.com
Reviewed-by: Christian König christian.koenig@amd.com
Assisted-by: LLM Signed-off-by: Karl Mehltretter kmehltretter@gmail.com
Notes: Changes in v2: - Return -EINVAL when the maximum segment size reported by the device is smaller than PAGE_SIZE instead of clamping it. (Christian) - Add Jason Gunthorpe's Reviewed-by tag. Tested on v7.3-rc4-70-gfe2ec83746e5 in QEMU (x86_64, TCG) with DMA_API_DEBUG (all_errors=1) and DMABUF_DEBUG, A/B against the same base: before after DMA_BUF_IOCTL_SYNC, 64 MiB udmabuf 1 report 0 vivid import, 4 MiB udmabuf 2 reports 0 vivid import, 2 MiB hugetlb udmabuf 2 reports 0 frames captured 5/5 5/5 vb2-dma-contig rejected the non-contiguous import in both runs. For v2, a focused importer advertising PAGE_SIZE / 2 returned -EINVAL under KASAN and DMA_API_DEBUG. No warning, BUG, or DMA-API report was emitted.
drivers/dma-buf/udmabuf.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index df6dd00462423..018937435356e 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -133,15 +133,22 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, { struct udmabuf *ubuf = buf->priv; struct sg_table *sg;
- unsigned int max_segment; int ret;
- max_segment = dma_get_max_seg_size(dev);
- /* The SG allocator requires a segment limit of at least PAGE_SIZE. */
- if (max_segment < PAGE_SIZE)
return ERR_PTR(-EINVAL);- sg = kzalloc_obj(*sg); if (!sg) return ERR_PTR(-ENOMEM);
- ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, 0,
ubuf->pagecount << PAGE_SHIFT,GFP_KERNEL);
- ret = sg_alloc_table_from_pages_segment(sg, ubuf->pages, ubuf->pagecount,
0, ubuf->pagecount << PAGE_SHIFT,max_segment, if (ret < 0) goto err_alloc;GFP_KERNEL);base-commit: fe2ec83746e501645709761605c2464a44fd2929