dma-contiguous: fix truncation of numa_cma / cma_pernuma sizes >= 2G
Subsystems (2)
PATCH 1/1 dma-contiguous: fix truncation of numa_cma / cma_pernuma sizes >= 2G
numa_cma=0:4G reserves nothing at all. dma_numa_cma_reserve() copies the requested size into a local int before handing it to cma_declare_contiguous_nid(), so 0x100000000 truncates to zero and the loop skips the node silently. Both parameters are documented in kernel-parameters.txt as nn[MG], so that is the syntax the documentation invites.
Which bits survive decides what a request turns into: 4G, 8G and 16G reserve nothing, 2G, 3G and 6G sign-extend into a size the allocator rejects with a warning, and 5G quietly reserves 1G.
It reaches further than those parameters. On a CMA_SIZE_PERNUMA kernel with no per-node parameter, dma_numa_cma_reserve() takes the per-node size from the default area, so a plain cma=4G on a multi-node machine feeds that size through the same local and loses every per-node area.
numa_cma_size[] and pernuma_size_bytes are both phys_addr_t, so use it for the local too, and give early_numa_cma() separate variables for the node id and the size while in there.
--- kernel/dma/contiguous.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
kernel/dma/contiguous.c +9 -7
@@ -140,21 +140,22 @@ static bool numa_cma_configured __initdata;static int __init early_numa_cma (char *p) {int nid, count =0 ; -unsigned long tmp; +unsigned long node; +phys_addr_t size;char *s = p;while (*s) { -if (sscanf (s,"%lu%n" , &tmp, &count) !=1 ) +if (sscanf (s,"%lu%n" , &node, &count) !=1 )break ;if (s[count] ==':' ) { -if (tmp >=MAX_NUMNODES ) +if (node >=MAX_NUMNODES )break ; - nid =array_index_nospec (tmp,MAX_NUMNODES ); + nid =array_index_nospec (node,MAX_NUMNODES ); s += count +1 ; - tmp =memparse (s, &s); - numa_cma_size[nid] = tmp; + size =memparse (s, &s); + numa_cma_size[nid] = size;if (*s ==',' ) s++;
@@ -207,9 +208,10 @@ static void __init dma_numa_cma_reserve(void) pernuma_size_bytes =cma_get_size (dma_contiguous_default_area);for_each_node (nid) { -int size, ret; +phys_addr_t size;char name[CMA_MAX_NAME ];struct cma **cma; +int ret;if (!node_online (nid)) {if (pernuma_size_bytes || numa_cma_size[nid])
base-commit: 26260251022fbc2f248a3d747a9b2b961b18d2d8
Signature · 1 line
2.47.1
Quoted 62 lines · Alexander Graf
numa_cma=0:4G reserves nothing at all.
dma_numa_cma_reserve()copies the requested size into a local int before handing it tocma_declare_contiguous_nid(), so0x100000000truncates to zero and the loop skips the node silently. Both parameters are documented in kernel-parameters.txt as nn[MG], so that is the syntax the documentation invites.Which bits survive decides what a request turns into: 4G, 8G and 16G reserve nothing, 2G, 3G and 6G sign-extend into a size the allocator rejects with a warning, and 5G quietly reserves 1G.
It reaches further than those parameters. On a
CMA_SIZE_PERNUMAkernel with no per-node parameter,dma_numa_cma_reserve()takes the per-node size from the default area, so a plain cma=4G on a multi-node machine feeds that size through the same local and loses every per-node area.numa_cma_size[] and
pernuma_size_bytesare bothphys_addr_t, so use it for the local too, and giveearly_numa_cma()separate variables for the node id and the size while in there.Fixes d5cae2261b86 ("dma-contiguous: simplify numa cma area handling")Assisted-by Kiro:claude-opus-5Signed-off-by Alexander Graf <[email protected]>--- kernel/dma/contiguous.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)kernel/dma/contiguous.c +7
-6@@ -140,21 +140,22 @@ static bool numa_cma_configured __initdata;static int __init early_numa_cma (char *p) {int nid, count =0 ; -unsigned long tmp; +unsigned long node; +phys_addr_t size;char *s = p;while (*s) { -if (sscanf (s,"%lu%n" , &tmp, &count) !=1 ) +if (sscanf (s,"%lu%n" , &node, &count) !=1 )break ;if (s[count] ==':' ) { -if (tmp >=MAX_NUMNODES ) +if (node >=MAX_NUMNODES )break ; - nid =array_index_nospec (tmp,MAX_NUMNODES ); + nid =array_index_nospec (node,MAX_NUMNODES ); s += count +1 ; - tmp =memparse (s, &s); - numa_cma_size[nid] = tmp; + size =memparse (s, &s); + numa_cma_size[nid] = size;if (*s ==',' ) s++;
kernel/dma/contiguous.c static void __init dma_numa_cma_reserve(void) · line 214
Quoted 8 lines
kernel/dma/contiguous.c +2
-1@@ -207,9 +208,10 @@ static void __init dma_numa_cma_reserve(void) pernuma_size_bytes =cma_get_size (dma_contiguous_default_area);for_each_node (nid) { -int size, ret; +phys_addr_t size;char name[CMA_MAX_NAME ];struct cma **cma; +int ret;
Looks good to me, and thanks for the fix!
Quoted 16 lines · Alexander Graf
numa_cma=0:4G reserves nothing at all.
dma_numa_cma_reserve()copies the requested size into a local int before handing it tocma_declare_contiguous_nid(), so0x100000000truncates to zero and the loop skips the node silently. Both parameters are documented in kernel-parameters.txt as nn[MG], so that is the syntax the documentation invites.Which bits survive decides what a request turns into: 4G, 8G and 16G reserve nothing, 2G, 3G and 6G sign-extend into a size the allocator rejects with a warning, and 5G quietly reserves 1G.
It reaches further than those parameters. On a
CMA_SIZE_PERNUMAkernel with no per-node parameter,dma_numa_cma_reserve()takes the per-node size from the default area, so a plain cma=4G on a multi-node machine feeds that size through the same local and loses every per-node area.
Quoted 8 lines
numa_cma_size[] and
pernuma_size_bytesare bothphys_addr_t, so use it for the local too, and giveearly_numa_cma()separate variables for the node id and the size while in there.Fixes d5cae2261b86 ("dma-contiguous: simplify numa cma area handling")Assisted-by Kiro:claude-opus-5Signed-off-by Alexander Graf <[email protected]>
Applied to dma-mapping-for-next. Thanks!
Best regards
Signature · 2 lines
Marek Szyprowski, PhD Samsung R&D Institute Poland