git: a6b40d746b4b - main - LinuxKPI: Add dma_length field to struct scatterlist
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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)