From 95d2fec40c483d8a63e5ffa0fa88faeb2e0ed462 Mon Sep 17 00:00:00 2001 From: John Garry Date: Mon, 31 Aug 2026 09:36:20 +0000 Subject: [PATCH 01/11] dma-mapping: rename dma_opt_mapping_size() Function dma_opt_mapping_size() implies from its name that it returns a target or sweet spot DMA mapping size. However, it is just an upper limit optimal DMA mapping size. Above this size, DMA mapping performance may significantly degrade. Rename to dma_max_opt_mapping_size() to reflect the real behaviour. Also rename the internal DMA mapping symbols to align with this. The DMA API documentation already described this behaviour properly (so there is nothing to update). Signed-off-by: John Garry Link: https://lore.kernel.org/r/20260831093620.3481337-1-john.g.garry@oracle.com Signed-off-by: Marek Szyprowski --- Documentation/core-api/dma-api.rst | 2 +- drivers/iommu/dma-iommu.c | 2 +- drivers/nvme/host/pci.c | 2 +- drivers/scsi/scsi_transport_sas.c | 12 ++++++------ include/linux/dma-map-ops.h | 2 +- include/linux/dma-mapping.h | 4 ++-- include/linux/iommu-dma.h | 2 +- kernel/dma/mapping.c | 10 +++++----- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Documentation/core-api/dma-api.rst b/Documentation/core-api/dma-api.rst index ba23a472f794..69d4ebe4f96d 100644 --- a/Documentation/core-api/dma-api.rst +++ b/Documentation/core-api/dma-api.rst @@ -154,7 +154,7 @@ others should not be larger than the returned value. :: size_t - dma_opt_mapping_size(struct device *dev); + dma_max_opt_mapping_size(struct device *dev); Returns the maximum optimal size of a mapping for the device. diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 58c624513cd4..1b26c9ce4aab 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -1762,7 +1762,7 @@ unsigned long iommu_dma_get_merge_boundary(struct device *dev) return (1UL << __ffs(domain->pgsize_bitmap)) - 1; } -size_t iommu_dma_opt_mapping_size(void) +size_t iommu_dma_max_opt_mapping_size(void) { return iova_rcache_range(); } diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index da93b505d239..d87d268037d8 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3747,7 +3747,7 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev, */ dev->ctrl.max_hw_sectors = min_t(u32, NVME_MAX_BYTES >> SECTOR_SHIFT, - dma_opt_mapping_size(&pdev->dev) >> 9); + dma_max_opt_mapping_size(&pdev->dev) >> SECTOR_SHIFT); dev->ctrl.max_segments = NVME_MAX_SEGS; dev->ctrl.max_integrity_segments = 1; return dev; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index d689b9ed08a6..aaa47552a215 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -223,8 +223,8 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) /* * Set shost->opt_sectors from the DMA optimal mapping size, but only - * when dma_opt_mapping_size() is strictly less than dma_max_mapping_size(), - * indicating a genuine optimization hint from an IOMMU or DMA backend. + * when dma_max_opt_mapping_size() is strictly less than + * dma_max_mapping_size(), indicating a genuine optimization hint. * When the two are equal (e.g. IOMMU disabled / passthrough), no real * hint exists, so leave opt_sectors at 0 to avoid bogus optimal_io_size * values that break filesystem geometry (e.g. mkfs.xfs stripe alignment). @@ -232,16 +232,16 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) static void sas_dma_setup_opt_sectors(struct Scsi_Host *shost) { struct device *dma_dev = shost->dma_dev; - size_t opt = dma_opt_mapping_size(dma_dev); + size_t max_opt = dma_max_opt_mapping_size(dma_dev); size_t max = dma_max_mapping_size(dma_dev); unsigned int opt_sectors; - /* opt >= max means no real hint was provided by the DMA layer */ - if (opt >= max) + /* max_opt >= max means no real hint was provided by the DMA layer */ + if (max_opt >= max) return; /* Clamp to max_sectors to avoid overflow in sector arithmetic */ - opt_sectors = min_t(unsigned int, opt >> SECTOR_SHIFT, + opt_sectors = min_t(unsigned int, max_opt >> SECTOR_SHIFT, shost->max_sectors); /* Guard against zero before rounddown_pow_of_two() */ diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index 8fae2b7deb20..eee6de5188c7 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -60,7 +60,7 @@ struct dma_map_ops { int (*dma_supported)(struct device *dev, u64 mask); u64 (*get_required_mask)(struct device *dev); size_t (*max_mapping_size)(struct device *dev); - size_t (*opt_mapping_size)(void); + size_t (*max_opt_mapping_size)(void); unsigned long (*get_merge_boundary)(struct device *dev); }; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index a3e880649fa4..efde0e5a58dc 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -207,7 +207,7 @@ int dma_set_coherent_mask(struct device *dev, u64 mask); u64 dma_get_required_mask(struct device *dev); bool dma_addressing_limited(struct device *dev); size_t dma_max_mapping_size(struct device *dev); -size_t dma_opt_mapping_size(struct device *dev); +size_t dma_max_opt_mapping_size(struct device *dev); unsigned long dma_get_merge_boundary(struct device *dev); struct sg_table *dma_alloc_noncontiguous(struct device *dev, size_t size, enum dma_data_direction dir, gfp_t gfp, unsigned long attrs); @@ -326,7 +326,7 @@ static inline size_t dma_max_mapping_size(struct device *dev) { return 0; } -static inline size_t dma_opt_mapping_size(struct device *dev) +static inline size_t dma_max_opt_mapping_size(struct device *dev) { return 0; } diff --git a/include/linux/iommu-dma.h b/include/linux/iommu-dma.h index 060f6e23ab3c..fae5d50e4f27 100644 --- a/include/linux/iommu-dma.h +++ b/include/linux/iommu-dma.h @@ -39,7 +39,7 @@ int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt, void *cpu_addr, dma_addr_t dma_addr, size_t size, unsigned long attrs); unsigned long iommu_dma_get_merge_boundary(struct device *dev); -size_t iommu_dma_opt_mapping_size(void); +size_t iommu_dma_max_opt_mapping_size(void); size_t iommu_dma_max_mapping_size(struct device *dev); void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle, unsigned long attrs); diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index bf2651a70b7c..82e701648d60 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -1012,19 +1012,19 @@ size_t dma_max_mapping_size(struct device *dev) } EXPORT_SYMBOL_GPL(dma_max_mapping_size); -size_t dma_opt_mapping_size(struct device *dev) +size_t dma_max_opt_mapping_size(struct device *dev) { const struct dma_map_ops *ops = get_dma_ops(dev); size_t size = SIZE_MAX; if (use_dma_iommu(dev)) - size = iommu_dma_opt_mapping_size(); - else if (ops && ops->opt_mapping_size) - size = ops->opt_mapping_size(); + size = iommu_dma_max_opt_mapping_size(); + else if (ops && ops->max_opt_mapping_size) + size = ops->max_opt_mapping_size(); return min(dma_max_mapping_size(dev), size); } -EXPORT_SYMBOL_GPL(dma_opt_mapping_size); +EXPORT_SYMBOL_GPL(dma_max_opt_mapping_size); unsigned long dma_get_merge_boundary(struct device *dev) { From 641a9b9cc697eb6ea375a25aafc6884d1b485612 Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Thu, 3 Sep 2026 09:29:13 +0800 Subject: [PATCH 02/11] dma-mapping: use exact allocation for DMA pages DMA page allocation fallbacks use alloc_pages_node() with get_order(size), wasting the unused tail for non-power-of-two requests. Use alloc_pages_exact_nid() and free_pages_exact() so that a buddy fallback retains only requested pages. Signed-off-by: Qingfang Deng Link: https://lore.kernel.org/r/20260903012914.312305-1-qingfang.deng@linux.dev Signed-off-by: Marek Szyprowski --- drivers/iommu/dma-iommu.c | 10 ++++++---- include/linux/dma-map-ops.h | 2 +- include/linux/gfp.h | 2 +- kernel/dma/contiguous.c | 6 +++--- kernel/dma/direct.c | 8 +++++--- kernel/dma/ops_helpers.c | 11 +++++++---- mm/page_alloc.c | 2 +- 7 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 1b26c9ce4aab..65d692ec7fce 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -1628,10 +1628,12 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size, void *cpu_addr; page = dma_alloc_contiguous(dev, alloc_size, gfp); - if (!page) - page = alloc_pages_node(node, gfp, get_order(alloc_size)); - if (!page) - return NULL; + if (!page) { + cpu_addr = alloc_pages_exact_nid(node, alloc_size, gfp); + if (!cpu_addr) + return NULL; + page = virt_to_page(cpu_addr); + } if (!coherent || PageHighMem(page)) { pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs); diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index eee6de5188c7..afaf2c12189b 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -143,7 +143,7 @@ static inline struct page *dma_alloc_contiguous(struct device *dev, size_t size, static inline void dma_free_contiguous(struct device *dev, struct page *page, size_t size) { - __free_pages(page, get_order(size)); + free_pages_exact(page_address(page), size); } #endif /* CONFIG_DMA_CMA*/ diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 872bc53f32ec..aa4601805f21 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -329,7 +329,7 @@ void *alloc_pages_exact_noprof(size_t size, gfp_t gfp_mask) __alloc_size(1); void free_pages_exact(void *virt, size_t size); -__meminit void *alloc_pages_exact_nid_noprof(int nid, size_t size, gfp_t gfp_mask) __alloc_size(2); +void *alloc_pages_exact_nid_noprof(int nid, size_t size, gfp_t gfp_mask) __alloc_size(2); #define alloc_pages_exact_nid(...) \ alloc_hooks(alloc_pages_exact_nid_noprof(__VA_ARGS__)) diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index 66093460584e..90937dc7c627 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -451,8 +451,8 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp) * * This function releases memory allocated by dma_alloc_contiguous(). As the * cma_release returns false when provided pages do not belong to contiguous - * area and true otherwise, this function then does a fallback __free_pages() - * upon a false-return. + * area and true otherwise, this function then does a fallback + * free_pages_exact() upon a false-return. */ void dma_free_contiguous(struct device *dev, struct page *page, size_t size) { @@ -476,7 +476,7 @@ void dma_free_contiguous(struct device *dev, struct page *page, size_t size) } /* not in any cma, free from buddy */ - __free_pages(page, get_order(size)); + free_pages_exact(page_address(page), size); } /* diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index da665ca22d5c..ea64453d7370 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -120,6 +120,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size, int node = dev_to_node(dev); struct page *page; u64 phys_limit; + void *va; WARN_ON_ONCE(!PAGE_ALIGNED(size)); @@ -133,9 +134,9 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size, dma_free_contiguous(dev, page, size); } - while ((page = alloc_pages_node(node, gfp, get_order(size))) - && !dma_coherent_ok(dev, page_to_phys(page), size)) { - __free_pages(page, get_order(size)); + while ((va = alloc_pages_exact_nid(node, size, gfp)) && + !dma_coherent_ok(dev, virt_to_phys(va), size)) { + free_pages_exact(va, size); if (IS_ENABLED(CONFIG_ZONE_DMA32) && phys_limit < DMA_BIT_MASK(64) && @@ -146,6 +147,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size, else return NULL; } + page = va ? virt_to_page(va) : NULL; return page; } diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c index 6b5f9208d31c..5cdf3f60cf47 100644 --- a/kernel/dma/ops_helpers.c +++ b/kernel/dma/ops_helpers.c @@ -67,10 +67,13 @@ struct page *dma_common_alloc_pages(struct device *dev, size_t size, phys_addr_t phys; page = dma_alloc_contiguous(dev, size, gfp); - if (!page) - page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size)); - if (!page) - return NULL; + if (!page) { + void *va = alloc_pages_exact_nid(dev_to_node(dev), size, gfp); + + if (!va) + return NULL; + page = virt_to_page(va); + } phys = page_to_phys(page); if (use_dma_iommu(dev)) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 12fac9084c48..5e2cae2e7993 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5655,7 +5655,7 @@ EXPORT_SYMBOL(alloc_pages_exact_noprof); * * Return: pointer to the allocated area or %NULL in case of error. */ -void * __meminit alloc_pages_exact_nid_noprof(int nid, size_t size, gfp_t gfp_mask) +void *alloc_pages_exact_nid_noprof(int nid, size_t size, gfp_t gfp_mask) { unsigned int order = get_order(size); struct page *p; From 57a57ae077f0f167056fc08f1666678f78e5b147 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 9 Sep 2026 11:57:26 +0200 Subject: [PATCH 03/11] MAINTAINERS: Add files to the DMA MAPPING HELPERS entry Add DMA trace events file to the DMA MAPPING HELPERS entry to get proper notification for further changes there. Reported-by: Donggeun Yoo Closes: https://lore.kernel.org/all/20260907120156.603600-1-donggeunyoo.kernel@gmail.com/ Signed-off-by: Marek Szyprowski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..e2f922cf5996 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7728,6 +7728,7 @@ F: include/linux/dma-direct.h F: include/linux/dma-map-ops.h F: include/linux/dma-mapping.h F: include/linux/swiotlb.h +F: include/trace/events/dma.h F: kernel/dma/ DMA MAPPING & SCATTERLIST API [RUST] From 5e3bc6b44c8853bcc7782eac122c5baf2d02f399 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 30 Sep 2026 15:57:32 +0200 Subject: [PATCH 04/11] dma-direct: Restore arch_dma_alloc() for the DMA_ATTR_NO_KERNEL_MAPPING case Commit 849facea92fa ("dma-direct: simplify the DMA_ATTR_NO_KERNEL_MAPPING handling") moved the DMA_ATTR_NO_KERNEL_MAPPING shortcut in dma_direct_alloc() in front of the arch_dma_alloc() call. Architectures providing their own DMA coherent allocator (CONFIG_ARCH_HAS_DMA_ALLOC) therefore no longer see such requests. ARM 32bit is one of such. Lack of the call to arch_dma_alloc() results in the lack of proper cache management on HighMem pages. Take the dma-direct shortcut only for coherent devices or when the architecture doesn't provide arch_dma_alloc(). While touching this, move that check into separate function to avoid duplication in alloc and free paths. Fixes: 849facea92fa ("dma-direct: simplify the DMA_ATTR_NO_KERNEL_MAPPING handling") Reported-by: Aneesh Kumar K.V Closes: https://lore.kernel.org/all/yq5ams2akznl.fsf@kernel.org/ Assisted-by: Claude Opus 5.5 Link: https://lore.kernel.org/r/20260930135734.1013905-1-m.szyprowski@samsung.com Signed-off-by: Marek Szyprowski --- kernel/dma/direct.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index ea64453d7370..25e9a86835fd 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -181,6 +181,22 @@ static struct page *dma_direct_alloc_from_pool(struct device *dev, size_t size, return page; } +/* + * Check if the allocation can skip the kernel mapping and return the page + * pointer as the cookie. + */ +static bool dma_direct_use_no_mapping(struct device *dev, unsigned long attrs) +{ + if ((attrs & (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) != + DMA_ATTR_NO_KERNEL_MAPPING) + return false; + if (is_swiotlb_for_alloc(dev)) + return false; + if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_ALLOC) && !dev_is_dma_coherent(dev)) + return false; + return true; +} + static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp) { @@ -228,8 +244,7 @@ void *dma_direct_alloc(struct device *dev, size_t size, if (attrs & DMA_ATTR_NO_WARN) gfp |= __GFP_NOWARN; - if (((attrs & (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) == - DMA_ATTR_NO_KERNEL_MAPPING) && !is_swiotlb_for_alloc(dev)) + if (dma_direct_use_no_mapping(dev, attrs)) return dma_direct_alloc_no_mapping(dev, size, dma_handle, gfp); if (!dev_is_dma_coherent(dev)) { @@ -376,8 +391,7 @@ void dma_direct_free(struct device *dev, size_t size, if (attrs & __DMA_ATTR_ALLOC_CC_SHARED) mark_mem_encrypted = true; - if (((attrs & (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) == - DMA_ATTR_NO_KERNEL_MAPPING) && !is_swiotlb_for_alloc(dev)) { + if (dma_direct_use_no_mapping(dev, attrs)) { /* cpu_addr is a struct page cookie, not a kernel address */ dma_free_contiguous(dev, cpu_addr, size); return; From 0875ed8eea3b68e293f7fbc516df6bcfe33deb01 Mon Sep 17 00:00:00 2001 From: Francesco Valla Date: Thu, 8 Oct 2026 00:05:21 +0200 Subject: [PATCH 05/11] dma: swiotlb: use KiB in restricted pool allocation log Update the allocation logs for restricted memory pools to use KiBs instead of MiBs, to avoid truncation when the size of the memory area is not a multiple of a MiB. Without this modification, pools smaller than 1MiB are logged as 0MiB. The same modification has already been performed for coherent memory pools [1]. Link: https://lore.kernel.org/all/20260629223759.2637162-1-sharaienko@google.com/ [1] Signed-off-by: Francesco Valla Link: https://lore.kernel.org/r/20261008-swiotlb-size-print-v1-1-374493b5c5a6@valla.it Signed-off-by: Marek Szyprowski --- kernel/dma/swiotlb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index ded7016a46a7..9379d95095e0 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -2083,8 +2083,8 @@ static int __init rmem_swiotlb_setup(unsigned long node, of_get_flat_dt_prop(node, "no-map", NULL)) return -EINVAL; - pr_info("Reserved memory: created restricted DMA pool at %pa, size %ld MiB\n", - &rmem->base, (unsigned long)rmem->size / SZ_1M); + pr_info("Reserved memory: created restricted DMA pool at %pa, size %llu KiB\n", + &rmem->base, (unsigned long long)(rmem->size / SZ_1K)); return 0; } From 5b944c36746be496346cdb63ebb31522791d403c Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V (Arm)" Date: Thu, 24 Sep 2026 11:37:49 +0530 Subject: [PATCH 06/11] dma: swiotlb: Rename swiotlb_size_or_default() Rename swiotlb_size_or_default() to swiotlb_default_pool_size() to make clear that it returns the current size of the default SWIOTLB pool. This includes changes made by command-line options or sizing heuristics, not just the compiled-in default. No functional change. Signed-off-by: Aneesh Kumar K.V (Arm) Link: https://lore.kernel.org/r/20260924060756.1325156-2-aneesh.kumar@kernel.org Signed-off-by: Marek Szyprowski --- arch/arm/xen/mm.c | 2 +- arch/arm64/mm/init.c | 2 +- arch/riscv/mm/init.c | 2 +- arch/x86/include/asm/crash_reserve.h | 4 ++-- include/linux/swiotlb.h | 2 +- kernel/dma/swiotlb.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c index 882cd70c7a2f..a6cf55c6fd55 100644 --- a/arch/arm/xen/mm.c +++ b/arch/arm/xen/mm.c @@ -125,7 +125,7 @@ static int __init xen_mm_init(void) return 0; /* we can work with the default swiotlb */ - rc = swiotlb_init_late(swiotlb_size_or_default(), + rc = swiotlb_init_late(swiotlb_default_pool_size(), xen_swiotlb_gfp(), NULL); if (rc < 0) return rc; diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index fbf215ecc7d0..0842098a279b 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -348,7 +348,7 @@ void __init arch_mm_preinit(void) unsigned long size = DIV_ROUND_UP(memblock_phys_mem_size(), 1024); - swiotlb_adjust_size(min(swiotlb_size_or_default(), size)); + swiotlb_adjust_size(min(swiotlb_default_pool_size(), size)); } swiotlb_init(true, flags); diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index f8994caefc70..14aea8248518 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -185,7 +185,7 @@ void __init arch_mm_preinit(void) */ unsigned long size = DIV_ROUND_UP(memblock_phys_mem_size(), 1024); - swiotlb_adjust_size(min(swiotlb_size_or_default(), size)); + swiotlb_adjust_size(min(swiotlb_default_pool_size(), size)); swiotlb = true; swiotlb_flags |= SWIOTLB_ANY; } diff --git a/arch/x86/include/asm/crash_reserve.h b/arch/x86/include/asm/crash_reserve.h index 7835b2cdff04..047f638a766d 100644 --- a/arch/x86/include/asm/crash_reserve.h +++ b/arch/x86/include/asm/crash_reserve.h @@ -18,7 +18,7 @@ * no good way to detect the paging mode of the target kernel which will be * loaded for dumping. */ -extern unsigned long swiotlb_size_or_default(void); +extern unsigned long swiotlb_default_pool_size(void); #ifdef CONFIG_X86_32 # define CRASH_ADDR_LOW_MAX SZ_512M @@ -33,7 +33,7 @@ extern unsigned long swiotlb_size_or_default(void); static inline unsigned long crash_low_size_default(void) { #ifdef CONFIG_X86_64 - return max(swiotlb_size_or_default() + (8UL << 20), 256UL << 20); + return max(swiotlb_default_pool_size() + (8UL << 20), 256UL << 20); #else return 0; #endif diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h index 2a421241b980..5f24edf47b68 100644 --- a/include/linux/swiotlb.h +++ b/include/linux/swiotlb.h @@ -38,7 +38,7 @@ struct scatterlist; #define IO_TLB_DEFAULT_SIZE (64UL << 20) #endif -unsigned long swiotlb_size_or_default(void); +unsigned long swiotlb_default_pool_size(void); void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags, int (*remap)(void *tlb, unsigned long nslabs)); int swiotlb_init_late(size_t size, gfp_t gfp_mask, diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 9379d95095e0..3d9a482af4c2 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -285,7 +285,7 @@ setup_io_tlb_npages(char *str) } early_param("swiotlb", setup_io_tlb_npages); -unsigned long swiotlb_size_or_default(void) +unsigned long swiotlb_default_pool_size(void) { return default_nslabs << IO_TLB_SHIFT; } From d5093dbca79110c3180fa7119533cff86affdf0d Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V (Arm)" Date: Thu, 24 Sep 2026 11:37:50 +0530 Subject: [PATCH 07/11] dma: swiotlb: Consolidate slab rounding Pass the area count explicitly to swiotlb_calc_nslabs() and keep the calculation separate from updates to the default_nslabs . Move the default_nslabs update into swiotlb_adjust_nareas(). Round slabs to a power of two even before the area count is initialized. For early swiotlb_adjust_size() calls, this moves rounding from area initialization to size adjustment, so early queries see the rounded size. Signed-off-by: Aneesh Kumar K.V (Arm) Link: https://lore.kernel.org/r/20260924060756.1325156-3-aneesh.kumar@kernel.org Signed-off-by: Marek Szyprowski --- kernel/dma/swiotlb.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 3d9a482af4c2..81f020fe6535 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -117,27 +117,20 @@ struct io_tlb_area { spinlock_t lock; }; -/* - * Round up number of slabs to the next power of 2. The last area is going - * be smaller than the rest if default_nslabs is not power of two. - * The number of slot in an area should be a multiple of IO_TLB_SEGSIZE, - * otherwise a segment may span two or more areas. It conflicts with free - * contiguous slots tracking: free slots are treated contiguous no matter - * whether they cross an area boundary. - * - * Return true if default_nslabs is rounded up. - */ -static bool round_up_default_nslabs(void) +/* Return a power-of-two number of slabs that can be split between areas. */ +static unsigned long swiotlb_calc_nslabs(unsigned long size, + unsigned long nareas) { - if (!default_nareas) - return false; + unsigned long nslabs; - if (default_nslabs < IO_TLB_SEGSIZE * default_nareas) - default_nslabs = IO_TLB_SEGSIZE * default_nareas; - else if (is_power_of_2(default_nslabs)) - return false; - default_nslabs = roundup_pow_of_two(default_nslabs); - return true; + nslabs = ALIGN(DIV_ROUND_UP(size, IO_TLB_SIZE), IO_TLB_SEGSIZE); + if (nareas && nslabs < IO_TLB_SEGSIZE * nareas) + nslabs = IO_TLB_SEGSIZE * nareas; + + if (!is_power_of_2(nslabs)) + nslabs = roundup_pow_of_two(nslabs); + + return nslabs; } /** @@ -150,6 +143,8 @@ static bool round_up_default_nslabs(void) */ static void swiotlb_adjust_nareas(unsigned int nareas) { + unsigned long nslabs; + if (!nareas) nareas = 1; else if (!is_power_of_2(nareas)) @@ -158,9 +153,12 @@ static void swiotlb_adjust_nareas(unsigned int nareas) default_nareas = nareas; pr_info("area num %d.\n", nareas); - if (round_up_default_nslabs()) + nslabs = swiotlb_calc_nslabs(default_nslabs << IO_TLB_SHIFT, nareas); + if (nslabs != default_nslabs) { + default_nslabs = nslabs; pr_info("SWIOTLB bounce buffer size roundup to %luMB", (default_nslabs << IO_TLB_SHIFT) >> 20); + } } /** @@ -300,10 +298,8 @@ void __init swiotlb_adjust_size(unsigned long size) if (default_nslabs != IO_TLB_DEFAULT_SIZE >> IO_TLB_SHIFT) return; - size = ALIGN(size, IO_TLB_SIZE); - default_nslabs = ALIGN(size >> IO_TLB_SHIFT, IO_TLB_SEGSIZE); - if (round_up_default_nslabs()) - size = default_nslabs << IO_TLB_SHIFT; + default_nslabs = swiotlb_calc_nslabs(size, default_nareas); + size = default_nslabs << IO_TLB_SHIFT; pr_info("SWIOTLB bounce buffer size adjusted to %luMB", size >> 20); } From af3857eac74e8a957740c083ca7e381e5bf82265 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V (Arm)" Date: Thu, 24 Sep 2026 11:37:51 +0530 Subject: [PATCH 08/11] dma: swiotlb: Track whether the pool size was explicitly set swiotlb_adjust_size() must not override a pool size supplied through the swiotlb= command-line parameter. Currently, it determines whether a size was supplied by comparing default_nslabs with IO_TLB_DEFAULT_SIZE. This fails when the user explicitly requests the compiled-in default size, allowing the request to be overwritten by automatic pool sizing. Record whether swiotlb= supplied a numeric size in swiotlb_cmdline_size_set and check that flag directly before adjusting the pool size. Reviewed-by: Catalin Marinas Reviewed-by: Robin Murphy Signed-off-by: Aneesh Kumar K.V (Arm) Link: https://lore.kernel.org/r/20260924060756.1325156-4-aneesh.kumar@kernel.org Signed-off-by: Marek Szyprowski --- kernel/dma/swiotlb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 81f020fe6535..dcce241104ed 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -100,6 +100,7 @@ static struct io_tlb_mem io_tlb_default_mem; static unsigned long default_nslabs = IO_TLB_DEFAULT_SIZE >> IO_TLB_SHIFT; static unsigned long default_nareas; +static bool swiotlb_cmdline_size_set __initdata; /** * struct io_tlb_area - IO TLB memory area descriptor @@ -253,6 +254,7 @@ setup_io_tlb_npages(char *str) /* avoid tail segment of size < IO_TLB_SEGSIZE */ default_nslabs = ALIGN(simple_strtoul(str, &str, 0), IO_TLB_SEGSIZE); + swiotlb_cmdline_size_set = true; } if (*str == ',') ++str; @@ -295,7 +297,7 @@ void __init swiotlb_adjust_size(unsigned long size) * architectures such as those supporting memory encryption to * adjust/expand SWIOTLB size for their use. */ - if (default_nslabs != IO_TLB_DEFAULT_SIZE >> IO_TLB_SHIFT) + if (swiotlb_cmdline_size_set) return; default_nslabs = swiotlb_calc_nslabs(size, default_nareas); From 9441e7eca18c8004d1afe57b2f9b27717549562a Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sun, 11 Oct 2026 18:33:31 -0400 Subject: [PATCH 09/11] tools/dma: fix building with O= With O= set, "all" depends on $(OUTPUT)dma_map_benchmark, but the only rule builds dma_map_benchmark in the source directory, and built-in rules are disabled: make: *** No rule to make target '/dma_map_benchmark', needed by 'all'. Stop. Build $(OUTPUT)dma_map_benchmark. Fixes: f74ee32963f1b ("tools/dma: move dma_map_benchmark from selftests to tools/dma") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Sasha Levin Acked-by: Qinxin Xia Link: https://lore.kernel.org/r/20261011223358.2327625-3-sashal@kernel.org Signed-off-by: Marek Szyprowski --- tools/dma/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/dma/Makefile b/tools/dma/Makefile index e4abf37bf020..0c239a6b9eae 100644 --- a/tools/dma/Makefile +++ b/tools/dma/Makefile @@ -38,8 +38,8 @@ prepare: $(OUTPUT)include/linux/map_benchmark.h FORCE: DMA_MAP_BENCHMARK = dma_map_benchmark -$(DMA_MAP_BENCHMARK): prepare FORCE - $(CC) $(CFLAGS) $(DMA_MAP_BENCHMARK).c -o $(DMA_MAP_BENCHMARK) +$(OUTPUT)$(DMA_MAP_BENCHMARK): prepare FORCE + $(CC) $(CFLAGS) $(DMA_MAP_BENCHMARK).c -o $@ clean: rm -f $(ALL_PROGRAMS) From 352665c55877368854f87724698dad0b453444eb Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sun, 11 Oct 2026 18:33:32 -0400 Subject: [PATCH 10/11] tools/dma: fix building from the top level with O= "make O= tools/dma" fails: Makefile:27: /tools/build/Makefile.include: No such file or directory tools/Makefile clears srctree, and the dma Makefile only works it out again when building_out_of_srctree is not set, which the top-level Makefile sets for O= builds. Also work out srctree when it is empty, as tools/bpf/Makefile does since commit a89b2cbf71d64 ("tools, bpf: Fix build for 'make -s tools/bpf O='"). Fixes: f74ee32963f1b ("tools/dma: move dma_map_benchmark from selftests to tools/dma") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Sasha Levin Acked-by: Qinxin Xia Link: https://lore.kernel.org/r/20261011223358.2327625-4-sashal@kernel.org Signed-off-by: Marek Szyprowski --- tools/dma/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/dma/Makefile b/tools/dma/Makefile index 0c239a6b9eae..20ae7f09cd20 100644 --- a/tools/dma/Makefile +++ b/tools/dma/Makefile @@ -7,7 +7,13 @@ bindir ?= /usr/bin # isn't set and when invoked from selftests build, where srctree # is set to ".". building_out_of_srctree is undefined for in srctree # builds +ifeq ($(srctree),) +update_srctree := 1 +endif ifndef building_out_of_srctree +update_srctree := 1 +endif +ifeq ($(update_srctree),1) srctree := $(patsubst %/,%,$(dir $(CURDIR))) srctree := $(patsubst %/,%,$(dir $(srctree))) endif From 4733f2966433b7f926f70c9dc461c33cd5c10835 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 13 Oct 2026 14:44:09 +0300 Subject: [PATCH 11/11] dma-debug: Skip cacheline tracking for MMIO mappings Mapping one MMIO dma-buf for many RDMA importers overflows dma-debug's cacheline overlap counter and triggers a warning. DMA_ATTR_MMIO mappings must never be CPU-cacheable, so CPU cacheline overlap tracking does not apply to them. Skip MMIO entries in both cacheline insertion and removal. Keep them in the DMA entry hash so mapping and unmapping checks still run. The reported warning was: ------------[ cut here ]------------ DMA-API: exceeded 7 overlapping mappings of cacheline 0x000000e040000400 WARNING: kernel/dma/debug.c:465 at add_dma_entry+0x3b5/0x470, CPU#58: dtr/5264 Modules linked in: xt_conntrack xt_MASQUERADE ip6table_nat ip6table_filter ip6_tables iptable_nat nf_nat xt_addrtype rpcsec_gss_krb5 krb5 auth_rpcgss oid_registry overlay rpcrdma rdma_ucm ib_uverbs ib_iser libiscsi scsi_transport_iscsi ib_umad rdma_cm ib_ipoib iw_cm ib_cm nvidia(O) mlx5_fwctl nvidia_drm(O) mlx5_ib ib_uverbs_support ib_core zram zsmalloc fuse mlx5_core CPU: 58 UID: 0 PID: 5264 Comm: dtr Tainted: G O J 7.3.0-rc6_for_upstream_debug_2026_10_08_13_46 #1.fc40 PREEMPTLAZY Tainted: [O]=OOT_MODULE, [J]=FWCTL Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 RIP: 0010:add_dma_entry+0x3bf/0x470 Code: ac e8 f5 89 6e 00 e9 47 fe ff ff 41 f7 c6 00 18 00 00 75 9f 83 fb 07 7e 9a 48 8d 3d eb 02 5f 04 be 07 00 00 00 48 8d 54 24 30 <67> 48 0f b9 3a eb 82 0f 0b 48 c7 c7 80 d3 2a 85 4c 89 e6 e8 a9 1f RSP: 0018:ff110001bcb7f230 EFLAGS: 00010002 RAX: 0000000000000006 RBX: 0000000000000008 RCX: 0000000000000000 RDX: ff110001bcb7f260 RSI: 0000000000000007 RDI: ffffffff85cfd070 RBP: 000000e040000400 R08: 0000000000000000 R09: ffe21c0044b8abf5 R10: ff11000225c55faf R11: 0000000000000000 R12: 0000000000000246 R13: 1fe220003796fe48 R14: 0000000000000400 R15: 00000000ffffffff FS: 00007fa8f91e6b80(0000) GS:ff11000eb81cc000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa8f5ca80f0 CR3: 0000000164871004 CR4: 0000000000373eb0 Call Trace: ? check_sync+0x2070/0x2070 ? find_held_lock+0x2b/0x80 ? debug_dma_map_phys+0x588/0x840 debug_dma_map_phys+0x267/0x840 ? dma_debug_add_bus+0xd0/0xd0 ? __sg_alloc_table+0x2b7/0x4d0 dma_map_phys+0x10a/0x330 ? sg_miter_start+0x130/0x130 dma_buf_phys_vec_to_sgt+0x565/0xed0 uverbs_dmabuf_map+0x158/0x340 [ib_uverbs] ? uverbs_dmabuf_unpin+0x10/0x10 [ib_uverbs] dma_buf_map_attachment+0x138/0x690 ib_umem_dmabuf_map_pages+0x249/0xc90 [ib_core] pagefault_dmabuf_mr+0x172/0xf80 [mlx5_ib] ? do_raw_spin_unlock+0x54/0x210 mlx5_ib_init_dmabuf_mr+0x13e/0x200 [mlx5_ib] [... '?' frames and user-space registers omitted ...] reg_user_mr_dmabuf+0x3bf/0x760 [mlx5_ib] mlx5_ib_reg_user_mr_dmabuf+0x2dd/0x13b0 [mlx5_ib] ib_uverbs_handler_UVERBS_METHOD_REG_MR+0x76d/0xd90 [ib_uverbs] ib_uverbs_cmd_verbs+0x2337/0x31c0 [ib_uverbs] ib_uverbs_ioctl+0x1c7/0x350 [ib_uverbs] __x64_sys_ioctl+0x72c/0x11f0 do_syscall_64+0xa1/0x4f0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 ---[ end trace 0000000000000000 ]--- Signed-off-by: Leon Romanovsky Reviewed-by: Robin Murphy Fixes: 3aa31a8bb11e ("dma-buf: provide phys_vec to scatter-gather mapping routine") Link: https://lore.kernel.org/r/20261013-dma-debug-fixes-v1-1-1ad1f471cc61@nvidia.com Signed-off-by: Marek Szyprowski --- kernel/dma/debug.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index 026d50de340e..36c1f7cbb2ae 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -489,8 +489,10 @@ static int active_cacheline_insert(struct dma_debug_entry *entry, /* If the device is not writing memory then we don't have any * concerns about the cpu consuming stale data. This mitigates * legitimate usages of overlapping mappings. + * MMIO mappings are never CPU-cacheable and do not need tracking either. */ - if (entry->direction == DMA_TO_DEVICE) + if (entry->direction == DMA_TO_DEVICE || + entry->attrs & DMA_ATTR_MMIO) return 0; spin_lock_irqsave(&radix_lock, flags); @@ -519,7 +521,8 @@ static void active_cacheline_remove(struct dma_debug_entry *entry) unsigned long flags; /* ...mirror the insert case */ - if (entry->direction == DMA_TO_DEVICE) + if (entry->direction == DMA_TO_DEVICE || + entry->attrs & DMA_ATTR_MMIO) return; spin_lock_irqsave(&radix_lock, flags);