[PATCH v2 1/5] swiotlb: enforce pool nareas and nslabs invariants
From: Luigi Rizzo <hidden>
Date: 2026-08-24 15:29:38
Also in:
driver-core, linux-iommu, linux-mm, lkml
Subsystem:
dma mapping helpers, the rest · Maintainers:
Marek Szyprowski, Linus Torvalds
The SWIOTLB allocator relies on two runtime invariants across all
pool initialization paths:
1. pool->nareas must always be a power of two so that a slot's area
can be located efficiently via bitwise masking (index & (nareas - 1))
instead of integer division.
2. pool->nslabs must be a multiple of nareas * IO_TLB_SEGSIZE so that
each area contains an integer multiple of IO_TLB_SEGSIZE (default 128)
slots, preventing contiguous allocations from crossing area boundaries.
Enforce these invariants consistently during early boot, pool
initialization (swiotlb_init_io_tlb_pool), and restricted DMA pool setup.
Fixes: 8ac04063354a ("swiotlb: reduce the number of areas to match actual memory pool size")
Signed-off-by: Luigi Rizzo <redacted>
---
kernel/dma/swiotlb.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 1abd3e6146f45..8e4bd9d47735a 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c@@ -33,6 +33,7 @@ #include <linux/kmsan-checks.h> #include <linux/iommu-helper.h> #include <linux/init.h> +#include <linux/log2.h> #include <linux/memblock.h> #include <linux/mm.h> #include <linux/pfn.h>
@@ -176,7 +177,7 @@ static void swiotlb_adjust_nareas(unsigned int nareas) static unsigned int limit_nareas(unsigned int nareas, unsigned long nslots) { if (nslots < nareas * IO_TLB_SEGSIZE) - return nslots / IO_TLB_SEGSIZE; + return rounddown_pow_of_two(nslots / IO_TLB_SEGSIZE); return nareas; }
@@ -269,7 +270,16 @@ static void swiotlb_init_io_tlb_pool(struct io_tlb_pool *mem, phys_addr_t start, unsigned long nslabs, bool late_alloc, unsigned int nareas) { void *vaddr = phys_to_virt(start); - unsigned long bytes = nslabs << IO_TLB_SHIFT, i; + unsigned long bytes, i; + + /* + * If we have multiple areas, ensure each area's size is a multiple of + * IO_TLB_SEGSIZE slots by aligning the total pool size down. + */ + if (nareas > 1) + nslabs = ALIGN_DOWN(nslabs, nareas * IO_TLB_SEGSIZE); + + bytes = nslabs << IO_TLB_SHIFT; mem->nslabs = nslabs; mem->start = start;
@@ -1813,7 +1823,10 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem, struct device *dev) { struct io_tlb_mem *mem = rmem->priv; - unsigned long nslabs = rmem->size >> IO_TLB_SHIFT; + unsigned long nslabs = round_down(rmem->size >> IO_TLB_SHIFT, IO_TLB_SEGSIZE); + + if (!nslabs) + return -EINVAL; /* Set Per-device io tlb area to one */ unsigned int nareas = 1;
--
2.55.0.766.g2966f0265a-goog