[PATCH] dma-contiguous: simplify numa cma area handling

From: Feng Tang

Date: Sun May 24 2026 - 21:51:29 EST


Currently, there are 2 kernel cmdline ways to setup numa cma area:
"cma_pernuma=" and "numa_cma=", and there are 2 cma arrays as well,
while they have no difference technically. Robin suggested to cleanup
the code and only use one array [1], as "the apparent intent that
users only want one _or_ the other".

Simplify the code by only using one array to save the numa cma area.
And in rare case that a user really setup the 2 cmdline parameters
at the same time, let the per-node specific size setting 'numa_cma='
take priority over the global numa cma setting.

Link[1]: https://lore.kernel.org/lkml/43c5301c-fe6a-41e4-9482-ccfc7b62f2a7@xxxxxxx/

Suggested-by: Robin Murphy <robin.murphy@xxxxxxx>
Signed-off-by: Feng Tang <feng.tang@xxxxxxxxxxxxxxxxx>
---
.../admin-guide/kernel-parameters.txt | 4 ++
kernel/dma/Kconfig | 4 +-
kernel/dma/contiguous.c | 46 +++++--------------
3 files changed, 19 insertions(+), 35 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 4d0f545fb3ec..8e717a587870 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -895,6 +895,10 @@ Kernel parameters
contiguous memory allocations. It will reserve CMA
area for the specified node.

+ If it is setup together with upper 'cmd_pernuma='
+ (unlikely), its size setting takes priority for the
+ specified numa nodes.
+
With numa CMA enabled, DMA users on node nid will
first try to allocate buffer from the numa area
which is located in node nid, if the allocation fails,
diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig
index c9fa0a922cba..0a4ba21a57a7 100644
--- a/kernel/dma/Kconfig
+++ b/kernel/dma/Kconfig
@@ -179,7 +179,9 @@ config DMA_NUMA_CMA

You can set the size of pernuma CMA by specifying "cma_pernuma=size"
or set the node id and its size of CMA by specifying "numa_cma=
- <node>:size[,<node>:size]" on the kernel's command line.
+ <node>:size[,<node>:size]" on the kernel's command line. And in
+ rare case that the above 2 are both setup, then the "numa_cma="
+ takes priority for the specified numa nodes.

config CMA_SIZE_PERNUMA
bool "Default CMA area per NUMA node"
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 799f6e9c88bd..f754079a287d 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -134,7 +134,6 @@ EXPORT_SYMBOL_GPL(dev_get_cma_area);

static struct cma *dma_contiguous_numa_area[MAX_NUMNODES];
static phys_addr_t numa_cma_size[MAX_NUMNODES] __initdata;
-static struct cma *dma_contiguous_pernuma_area[MAX_NUMNODES];
static phys_addr_t pernuma_size_bytes __initdata;
static bool numa_cma_configured __initdata;

@@ -208,7 +207,7 @@ static void __init dma_numa_cma_reserve(void)
pernuma_size_bytes = cma_get_size(dma_contiguous_default_area);

for_each_node(nid) {
- int ret;
+ int size, ret;
char name[CMA_MAX_NAME];
struct cma **cma;

@@ -218,27 +217,17 @@ static void __init dma_numa_cma_reserve(void)
continue;
}

- if (pernuma_size_bytes) {
-
- cma = &dma_contiguous_pernuma_area[nid];
- snprintf(name, sizeof(name), "pernuma%d", nid);
- ret = cma_declare_contiguous_nid(0, pernuma_size_bytes, 0, 0,
- 0, false, name, cma, nid);
- if (ret)
- pr_warn("%s: reservation failed: err %d, node %d", __func__,
- ret, nid);
- }
-
- if (numa_cma_size[nid]) {
+ /* per-node numa setting has the priority */
+ size = numa_cma_size[nid] ?: pernuma_size_bytes;
+ if (!size)
+ continue;

- cma = &dma_contiguous_numa_area[nid];
- snprintf(name, sizeof(name), "numa%d", nid);
- ret = cma_declare_contiguous_nid(0, numa_cma_size[nid], 0, 0, 0, false,
- name, cma, nid);
- if (ret)
- pr_warn("%s: reservation failed: err %d, node %d", __func__,
- ret, nid);
- }
+ cma = &dma_contiguous_numa_area[nid];
+ snprintf(name, sizeof(name), "numa%d", nid);
+ ret = cma_declare_contiguous_nid(0, size, 0, 0, 0, false, name, cma, nid);
+ if (ret)
+ pr_warn("%s: reservation failed: err %d, node %d", __func__,
+ ret, nid);
}
}
#else
@@ -437,16 +426,8 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)

#ifdef CONFIG_DMA_NUMA_CMA
if (nid != NUMA_NO_NODE && !(gfp & (GFP_DMA | GFP_DMA32))) {
- struct cma *cma = dma_contiguous_pernuma_area[nid];
+ struct cma *cma = dma_contiguous_numa_area[nid];
struct page *page;
-
- if (cma) {
- page = cma_alloc_aligned(cma, size, gfp);
- if (page)
- return page;
- }
-
- cma = dma_contiguous_numa_area[nid];
if (cma) {
page = cma_alloc_aligned(cma, size, gfp);
if (page)
@@ -484,9 +465,6 @@ void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
* otherwise, page is from either per-numa cma or default cma
*/
#ifdef CONFIG_DMA_NUMA_CMA
- if (cma_release(dma_contiguous_pernuma_area[page_to_nid(page)],
- page, count))
- return;
if (cma_release(dma_contiguous_numa_area[page_to_nid(page)],
page, count))
return;

base-commit: 7e6ace2535d032c908e4d8747d9a7952617c001a
--
2.43.5