git: a6b40d746b4b - main - LinuxKPI: Add dma_length field to struct scatterlist

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Sun, 13 Sep 2026 17:26:04 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=a6b40d746b4b727b308f2437b41f8032b921a8e5

commit a6b40d746b4b727b308f2437b41f8032b921a8e5
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2026-09-13 17:25:12 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2026-09-13 17:25:12 +0000

    LinuxKPI: Add dma_length field to struct scatterlist
    
    On Linux `dma_length` field of `struct scatterlist` is present on the
    arches where DMA mapping code is able to coalesce adjacent segments
    of physical address space. It contains total length of coalesced
    segments while `length` field contains non-coalesced length of each
    segment. On other arches `dma_length` is aliased to `length` field with
    `sg_dma_len` macro. As FreeBSD does not merge scatterlist segments it
    do not have `dma_length` field. It is appered that at least i915kms
    driver depends on existence of `dma_length` field.
    
    Add the field and disable it by default. To enable add to Makefile
    
    .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" || \\
        ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "powerpc"
    CFLAGS+=        -DCONFIG_NEED_SG_DMA_LENGTH
    .endif
    
    Reported by:    Ryan Fahy
    Reviewed by:    bz
    MFC after:      1 month
    GHI:            https://github.com/freebsd/drm-kmod/issues/315
    Differential Revision:  https://reviews.freebsd.org/D59630
---
 .../linuxkpi/common/include/linux/scatterlist.h    | 26 ++++++++++++++--------
 sys/compat/linuxkpi/common/src/linux_pci.c         |  1 +
 2 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/scatterlist.h b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
index dd8ea31d4872..9dcf50b5e45b 100644
--- a/sys/compat/linuxkpi/common/include/linux/scatterlist.h
+++ b/sys/compat/linuxkpi/common/include/linux/scatterlist.h
@@ -50,6 +50,7 @@ struct scatterlist {
 	unsigned int offset;
 	unsigned int length;
 	dma_addr_t dma_address;
+	unsigned int dma_length;
 	struct bus_dmamap *dma_map;	/* FreeBSD specific */
 };
 
@@ -89,13 +90,18 @@ struct sg_dma_page_iter {
 	((struct scatterlist *) ((sg)->page_link & ~SG_PAGE_LINK_MASK))
 
 #define	sg_dma_address(sg)	(sg)->dma_address
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+#define	sg_dma_len(sg)		(sg)->dma_length
+#else
 #define	sg_dma_len(sg)		(sg)->length
+#endif
 
 #define	for_each_sg_page(sgl, iter, nents, pgoffset)			\
-	for (_sg_iter_init(sgl, iter, nents, pgoffset);			\
-	     (iter)->sg; _sg_iter_next(iter))
+	for (_sg_iter_init(sgl, iter, nents, pgoffset, false);		\
+	     (iter)->sg; _sg_iter_next(iter, false))
 #define	for_each_sg_dma_page(sgl, iter, nents, pgoffset) 		\
-	for_each_sg_page(sgl, &(iter)->base, nents, pgoffset)
+	for (_sg_iter_init(sgl, &(iter)->base, nents, pgoffset, true);	\
+	     (iter)->base.sg; _sg_iter_next(&(iter)->base, true))
 
 #define	for_each_sg(sglist, sg, sgmax, iter)				\
 	for (iter = 0, sg = (sglist); iter < (sgmax); iter++, sg = sg_next(sg))
@@ -477,13 +483,14 @@ __sg_page_iter_start(struct sg_page_iter *piter,
 }
 
 static inline void
-_sg_iter_next(struct sg_page_iter *iter)
+_sg_iter_next(struct sg_page_iter *iter, bool is_dma)
 {
 	struct scatterlist *sg;
-	unsigned int pgcount;
+	unsigned int pgcount, sglength;
 
 	sg = iter->sg;
-	pgcount = (sg->offset + sg->length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	sglength = is_dma ? sg_dma_len(sg) : sg->length;
+	pgcount = (sg->offset + sglength + PAGE_SIZE - 1) >> PAGE_SHIFT;
 
 	++iter->sg_pgoffset;
 	while (iter->sg_pgoffset >= pgcount) {
@@ -492,7 +499,8 @@ _sg_iter_next(struct sg_page_iter *iter)
 		--iter->maxents;
 		if (sg == NULL || iter->maxents == 0)
 			break;
-		pgcount = (sg->offset + sg->length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+		sglength = is_dma ? sg_dma_len(sg) : sg->length;
+		pgcount = (sg->offset + sglength + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	}
 	iter->sg = sg;
 }
@@ -536,13 +544,13 @@ __sg_page_iter_next(struct sg_page_iter *piter)
 
 static inline void
 _sg_iter_init(struct scatterlist *sgl, struct sg_page_iter *iter,
-    unsigned int nents, unsigned long pgoffset)
+    unsigned int nents, unsigned long pgoffset, bool is_dma)
 {
 	if (nents) {
 		iter->sg = sgl;
 		iter->sg_pgoffset = pgoffset - 1;
 		iter->maxents = nents;
-		_sg_iter_next(iter);
+		_sg_iter_next(iter, is_dma);
 	} else {
 		iter->sg = NULL;
 		iter->sg_pgoffset = 0;
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index c1ab63b6c902..c912dc80961a 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -2084,6 +2084,7 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
 		    ("More than one segment (nseg=%d)", nseg + 1));
 
 		sg_dma_address(sg) = seg.ds_addr;
+		sg->dma_length = sg->length;
 	}
 
 	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) != 0)