[PATCH v2 1/5] swiotlb: enforce pool nareas and nslabs invariants
Luigi Rizzo <[email protected]>
| Newsgroups | dev.linux.lists.iommu,dev.linux.lists.driver-core,org.kernel.vger.linux-kernel,org.kernel.vger.netdev,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]>
---
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