mm/hugetlb_cma: support percentage-based hugetlb_cma reservation
Subsystems (3)
PATCH v4 1/1 mm/hugetlb_cma: support percentage-based hugetlb_cma reservation
Currently, hugetlb_cma reservation only supports absolute sizes (e.g., hugetlb_cma=2G or hugetlb_cma=0:1G,1:1G). This can be restrictive in heterogeneous environments or when deploying common kernel command lines across machines with different memory capacities.
Add support for percentage-based hugetlb_cma reservation (e.g., hugetlb_cma=20% or hugetlb_cma=0:20%,1:10%).
The percentage is calculated against the total memory (for global settings) or against the node-specific memory (for node-specific settings) using memblock APIs during early boot.
---
v4:
- Clear global hugetlb_cma_percent and hugetlb_cma_size when node-specific parameters are parsed so the last command line configuration wins (Usama Arif).
- Warn when ALIGN_DOWN rounds down a non-zero percentage-derived reservation size to 0 MiB (Usama Arif).
v3:
- Enhance pr_info logs to explicitly show percentage-derived sizes per Andrew Morton"s suggestions.
- Explicitly align percentage-derived sizes down to the gigantic page size to prevent allocation failures (Usama Arif).
.../admin-guide/kernel-parameters.txt | 7 +- mm/hugetlb_cma.c | 141 +++++++++++++++++- 2 files changed, 138 insertions(+), 10 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +5 -2
@@ -2064,8 +2064,11 @@ Kernel parameters hugetlb_cma= [HW,CMA,EARLY] The size of a CMA area used for allocation of gigantic hugepages. Or using node format, the size of a CMA area per node can be specified. - Format: nn[KMGTPE] or (node format) - <node>:nn[KMGTPE][,<node>:nn[KMGTPE]] + The size can be an absolute value (e.g., 2G) or a + percentage of the total memory or node memory (e.g., 20%). + Format: nn[KMGTPE] or nn% or (node format) + <node>:nn[KMGTPE][,<node>:nn[KMGTPE]] or + <node>:nn%[,<node>:nn%] Reserve a CMA area of given size and allocate gigantic hugepages using the CMA allocator. If enabled, the
mm/hugetlb_cma.c +133 -8
196 lines not on this page · raw message
Signature · 1 line
2.55.0.571.g244d577d93-goog
Quoted 35 lines · Sourav Panda
Currently,
hugetlb_cmareservation only supports absolute sizes (e.g., hugetlb_cma=2G or hugetlb_cma=0:1G,1:1G). This can be restrictive in heterogeneous environments or when deploying common kernel command lines across machines with different memory capacities.Add support for percentage-based
hugetlb_cmareservation (e.g., hugetlb_cma=20% or hugetlb_cma=0:20%,1:10%).The percentage is calculated against the total memory (for global settings) or against the node-specific memory (for node-specific settings) using memblock APIs during early boot.
Signed-off-by Sourav Panda <[email protected]>---
v4:
- Clear globalhugetlb_cma_percentandhugetlb_cma_sizewhen node-specific parameters are parsed so the last command line configuration wins (Usama Arif).
- Warn whenALIGN_DOWNrounds down a non-zero percentage-derived reservation size to 0 MiB (Usama Arif).v3:
- Enhancepr_infologs to explicitly show percentage-derived sizes per Andrew Morton"s suggestions.
- Explicitly align percentage-derived sizes down to the gigantic page size to prevent allocation failures (Usama Arif)..../admin-guide/kernel-parameters.txt | 7 +- mm/hugetlb_cma.c | 141 +++++++++++++++++- 2 files changed, 138 insertions(+), 10 deletions(-)Documentation/admin-guide/kernel-parameters.txt +0
-0
Documentation/admin-guide/kernel-parameters.txt Kernel parameters · line 2068
Quoted 7 lines
Documentation/admin-guide/kernel-parameters.txt +2
-2@@ -2064,8 +2064,11 @@ Kernel parameters hugetlb_cma= [HW,CMA,EARLY] The size of a CMA area used for allocation of gigantic hugepages. Or using node format, the size of a CMA area per node can be specified. - Format: nn[KMGTPE] or (node format) - <node>:nn[KMGTPE][,<node>:nn[KMGTPE]] + The size can be an absolute value (e.g., 2G) or a + percentage of the total memory or node memory (e.g., 20%).
I think it would be good to add something like below in the doc as well:
Percentage-derived sizes are rounded down to a multiple of the architecture's gigantic hugepage size and may become zero.
And thanks for the patch! This is very useful to have in a heterogeneous fleet.
Quoted 214 lines
Documentation/admin-guide/kernel-parameters.txt +3
-0+ Format: nn[KMGTPE] or nn% or (node format) + <node>:nn[KMGTPE][,<node>:nn[KMGTPE]] or + <node>:nn%[,<node>:nn%] Reserve a CMA area of given size and allocate gigantic hugepages using the CMA allocator. If enabled, themm/hugetlb_cma.c +133
-8@@ -9,6 +9,9 @@#include <asm/setup.h> #include <linux/hugetlb.h> +#include <linux/memblock.h> +#include <linux/math.h> +#include <linux/math64.h> #include "internal.h" #include "hugetlb_cma.h" @@ -18,6 +21,28 @@ static unsigned long hugetlb_cma_size_in_node[MAX_NUMNODES] __initdata;static bool hugetlb_cma_only__ro_after_init ;static unsigned long hugetlb_cma_size__ro_after_init ; +static unsigned int hugetlb_cma_percent__initdata ; +static unsigned int hugetlb_cma_percent_in_node[MAX_NUMNODES ]__initdata ; + +#ifdef CONFIG_NUMA +static phys_addr_t __init memblock_node_memory_size (int nid) +{ +struct memblock_region *reg; +phys_addr_t size =0 ; + +for_each_mem_region (reg) { +if (reg->nid == nid) + size += reg->size; + } +return size; +} +#else +static phys_addr_t __init memblock_node_memory_size (int nid) +{ +return memblock_phys_mem_size (); +} +#endif + void hugetlb_cma_free_frozen_folio(struct folio *folio) { WARN_ON_ONCE(!cma_release_frozen(hugetlb_cma[folio_nid(folio)],@@ -100,14 +125,31 @@ static int __init cmdline_parse_hugetlb_cma(char *p)break ;if (s[count] ==':' ) { +char *next; +if (tmp >=MAX_NUMNODES )break ; nid =array_index_nospec (tmp,MAX_NUMNODES ); + hugetlb_cma_size =0 ; + hugetlb_cma_percent =0 ; + s += count +1 ; - tmp =memparse (s, &s); - hugetlb_cma_size_in_node[nid] = tmp; - hugetlb_cma_size += tmp; + tmp =memparse (s, &next); +if (*next =='%' ) { +if (tmp >100 ) { +pr_warn ("hugetlb_cma: invalid percentage %lu for node %d\n" , + tmp, nid); +break ; + } + hugetlb_cma_percent_in_node[nid] = tmp; + hugetlb_cma_size_in_node[nid] =0 ; + s = next +1 ; + }else { + hugetlb_cma_size_in_node[nid] = tmp; + hugetlb_cma_percent_in_node[nid] =0 ; + s = next; + }/* * Skip the separator if have one, otherwise @@ -118,7 +160,28 @@ static int __init cmdline_parse_hugetlb_cma(char *p)else break ; }else { - hugetlb_cma_size =memparse (p, &p); +char *next; + + tmp =memparse (p, &next); +if (*next =='%' ) { +if (tmp >100 ) { +pr_warn ("hugetlb_cma: invalid percentage %lu\n" , tmp); + }else { + hugetlb_cma_percent = tmp; + hugetlb_cma_size =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { + hugetlb_cma_size_in_node[nid] =0 ; + hugetlb_cma_percent_in_node[nid] =0 ; + } + } + }else { + hugetlb_cma_size = tmp; + hugetlb_cma_percent =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { + hugetlb_cma_size_in_node[nid] =0 ; + hugetlb_cma_percent_in_node[nid] =0 ; + } + }break ; } }@@ -144,8 +207,36 @@ void __init hugetlb_cma_reserve(void) {unsigned long size, reserved, per_node, order;bool node_specific_cma_alloc = false; +bool has_node_specific_param = false;int nid; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { +if (hugetlb_cma_size_in_node[nid] || hugetlb_cma_percent_in_node[nid]) { + has_node_specific_param = true; +break ; + } + } + +if (has_node_specific_param) { + hugetlb_cma_size =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { +if (hugetlb_cma_percent_in_node[nid]) { +phys_addr_t node_gfp_mem =memblock_node_memory_size (nid); +u64 s; + + s =mul_u64_u32_div ((u64 )node_gfp_mem, + hugetlb_cma_percent_in_node[nid], +100 ); + + hugetlb_cma_size_in_node[nid] = s; + } + hugetlb_cma_size += hugetlb_cma_size_in_node[nid]; + } + }else if (hugetlb_cma_percent) { + hugetlb_cma_size =mul_u64_u32_div ((u64 )memblock_phys_mem_size (), + hugetlb_cma_percent,100 ); + } +if (!hugetlb_cma_size)return ;@@ -163,6 +254,32 @@ void __init hugetlb_cma_reserve(void)*/ VM_WARN_ON (order <=MAX_PAGE_ORDER ); +if (hugetlb_cma_percent) { +unsigned long orig_size = hugetlb_cma_size; + + hugetlb_cma_size =ALIGN_DOWN (hugetlb_cma_size,PAGE_SIZE << order); +if (orig_size && !hugetlb_cma_size) +pr_warn ("hugetlb_cma: reservation size rounded down to 0 from %lu MiB (%u%%)\n" , + orig_size /SZ_1M , hugetlb_cma_percent); + }else if (has_node_specific_param) { + hugetlb_cma_size =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { +if (hugetlb_cma_percent_in_node[nid]) { +unsigned long orig_size = hugetlb_cma_size_in_node[nid]; + + hugetlb_cma_size_in_node[nid] = +ALIGN_DOWN (hugetlb_cma_size_in_node[nid], +PAGE_SIZE << order); +if (orig_size && !hugetlb_cma_size_in_node[nid]) +pr_warn ("hugetlb_cma: reservation size rounded down to 0 from %lu MiB (%u%%) on node %d\n" , + orig_size /SZ_1M , + hugetlb_cma_percent_in_node[nid], + nid); + } + hugetlb_cma_size += hugetlb_cma_size_in_node[nid]; + } + } +hugetlb_bootmem_set_nodes ();for (nid =0 ; nid <MAX_NUMNODES ; nid++) {@@ -205,8 +322,12 @@ void __init hugetlb_cma_reserve(void) per_node =DIV_ROUND_UP (hugetlb_cma_size,nodes_weight (hugetlb_bootmem_nodes)); per_node =round_up (per_node,PAGE_SIZE << order); -pr_info ("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n" , - hugetlb_cma_size /SZ_1M , per_node /SZ_1M ); +if (hugetlb_cma_percent) +pr_info ("hugetlb_cma: reserve %lu MiB (%u%%), up to %lu MiB per node\n" , + hugetlb_cma_size /SZ_1M , hugetlb_cma_percent, per_node /SZ_1M ); +else +pr_info ("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n" , + hugetlb_cma_size /SZ_1M , per_node /SZ_1M ); } reserved =0 ;@@ -241,8 +362,12 @@ void __init hugetlb_cma_reserve(void) } reserved += size; -pr_info ("hugetlb_cma: reserved %lu MiB on node %d\n" , - size /SZ_1M , nid); +if (hugetlb_cma_percent_in_node[nid]) +pr_info ("hugetlb_cma: reserved %lu MiB (%u%%) on node %d\n" , + size /SZ_1M , hugetlb_cma_percent_in_node[nid], nid); +else +pr_info ("hugetlb_cma: reserved %lu MiB on node %d\n" , + size /SZ_1M , nid);if (reserved >= hugetlb_cma_size)break ;
Quoted 49 lines · Usama Arif
Quoted 35 lines · Sourav Panda
Currently,
hugetlb_cmareservation only supports absolute sizes (e.g., hugetlb_cma=2G or hugetlb_cma=0:1G,1:1G). This can be restrictive in heterogeneous environments or when deploying common kernel command lines across machines with different memory capacities.Add support for percentage-based
hugetlb_cmareservation (e.g., hugetlb_cma=20% or hugetlb_cma=0:20%,1:10%).The percentage is calculated against the total memory (for global settings) or against the node-specific memory (for node-specific settings) using memblock APIs during early boot.
Signed-off-by Sourav Panda <[email protected]>---
v4:
- Clear globalhugetlb_cma_percentandhugetlb_cma_sizewhen node-specific parameters are parsed so the last command line configuration wins (Usama Arif).
- Warn whenALIGN_DOWNrounds down a non-zero percentage-derived reservation size to 0 MiB (Usama Arif).v3:
- Enhancepr_infologs to explicitly show percentage-derived sizes per Andrew Morton"s suggestions.
- Explicitly align percentage-derived sizes down to the gigantic page size to prevent allocation failures (Usama Arif)..../admin-guide/kernel-parameters.txt | 7 +- mm/hugetlb_cma.c | 141 +++++++++++++++++- 2 files changed, 138 insertions(+), 10 deletions(-)Documentation/admin-guide/kernel-parameters.txt +0
-0Documentation/admin-guide/kernel-parameters.txt Kernel parameters · line 2068
Quoted 7 lines
Documentation/admin-guide/kernel-parameters.txt +2
-2@@ -2064,8 +2064,11 @@ Kernel parameters hugetlb_cma= [HW,CMA,EARLY] The size of a CMA area used for allocation of gigantic hugepages. Or using node format, the size of a CMA area per node can be specified. - Format: nn[KMGTPE] or (node format) - <node>:nn[KMGTPE][,<node>:nn[KMGTPE]] + The size can be an absolute value (e.g., 2G) or a + percentage of the total memory or node memory (e.g., 20%).I think it would be good to add something like below in the doc as well:
Quoted 7 lines
Percentage-derived sizes are rounded down to a multiple of the architecture's gigantic hugepage size and may become zero.
And thanks for the patch! This is very useful to have in a heterogeneous fleet.
Acked-by Usama Arif <[email protected]>
Thanks for the reviews Usama :)
I will send v5 today with the documentation change.
Quoted 217 lines
Quoted 216 lines
Documentation/admin-guide/kernel-parameters.txt +3
-0+ Format: nn[KMGTPE] or nn% or (node format) + <node>:nn[KMGTPE][,<node>:nn[KMGTPE]] or + <node>:nn%[,<node>:nn%] Reserve a CMA area of given size and allocate gigantic hugepages using the CMA allocator. If enabled, themm/hugetlb_cma.c +133
-8@@ -9,6 +9,9 @@#include <asm/setup.h> #include <linux/hugetlb.h> +#include <linux/memblock.h> +#include <linux/math.h> +#include <linux/math64.h> #include "internal.h" #include "hugetlb_cma.h" @@ -18,6 +21,28 @@ static unsigned long hugetlb_cma_size_in_node[MAX_NUMNODES] __initdata;static bool hugetlb_cma_only__ro_after_init ;static unsigned long hugetlb_cma_size__ro_after_init ; +static unsigned int hugetlb_cma_percent__initdata ; +static unsigned int hugetlb_cma_percent_in_node[MAX_NUMNODES ]__initdata ; + +#ifdef CONFIG_NUMA +static phys_addr_t __init memblock_node_memory_size (int nid) +{ +struct memblock_region *reg; +phys_addr_t size =0 ; + +for_each_mem_region (reg) { +if (reg->nid == nid) + size += reg->size; + } +return size; +} +#else +static phys_addr_t __init memblock_node_memory_size (int nid) +{ +return memblock_phys_mem_size (); +} +#endif + void hugetlb_cma_free_frozen_folio(struct folio *folio) { WARN_ON_ONCE(!cma_release_frozen(hugetlb_cma[folio_nid(folio)],@@ -100,14 +125,31 @@ static int __init cmdline_parse_hugetlb_cma(char *p)break ;if (s[count] ==':' ) { +char *next; +if (tmp >=MAX_NUMNODES )break ; nid =array_index_nospec (tmp,MAX_NUMNODES ); + hugetlb_cma_size =0 ; + hugetlb_cma_percent =0 ; + s += count +1 ; - tmp =memparse (s, &s); - hugetlb_cma_size_in_node[nid] = tmp; - hugetlb_cma_size += tmp; + tmp =memparse (s, &next); +if (*next =='%' ) { +if (tmp >100 ) { +pr_warn ("hugetlb_cma: invalid percentage %lu for node %d\n" , + tmp, nid); +break ; + } + hugetlb_cma_percent_in_node[nid] = tmp; + hugetlb_cma_size_in_node[nid] =0 ; + s = next +1 ; + }else { + hugetlb_cma_size_in_node[nid] = tmp; + hugetlb_cma_percent_in_node[nid] =0 ; + s = next; + }/* * Skip the separator if have one, otherwise @@ -118,7 +160,28 @@ static int __init cmdline_parse_hugetlb_cma(char *p)else break ; }else { - hugetlb_cma_size =memparse (p, &p); +char *next; + + tmp =memparse (p, &next); +if (*next =='%' ) { +if (tmp >100 ) { +pr_warn ("hugetlb_cma: invalid percentage %lu\n" , tmp); + }else { + hugetlb_cma_percent = tmp; + hugetlb_cma_size =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { + hugetlb_cma_size_in_node[nid] =0 ; + hugetlb_cma_percent_in_node[nid] =0 ; + } + } + }else { + hugetlb_cma_size = tmp; + hugetlb_cma_percent =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { + hugetlb_cma_size_in_node[nid] =0 ; + hugetlb_cma_percent_in_node[nid] =0 ; + } + }break ; } }@@ -144,8 +207,36 @@ void __init hugetlb_cma_reserve(void) {unsigned long size, reserved, per_node, order;bool node_specific_cma_alloc = false; +bool has_node_specific_param = false;int nid; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { +if (hugetlb_cma_size_in_node[nid] || hugetlb_cma_percent_in_node[nid]) { + has_node_specific_param = true; +break ; + } + } + +if (has_node_specific_param) { + hugetlb_cma_size =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { +if (hugetlb_cma_percent_in_node[nid]) { +phys_addr_t node_gfp_mem =memblock_node_memory_size (nid); +u64 s; + + s =mul_u64_u32_div ((u64 )node_gfp_mem, + hugetlb_cma_percent_in_node[nid], +100 ); + + hugetlb_cma_size_in_node[nid] = s; + } + hugetlb_cma_size += hugetlb_cma_size_in_node[nid]; + } + }else if (hugetlb_cma_percent) { + hugetlb_cma_size =mul_u64_u32_div ((u64 )memblock_phys_mem_size (), + hugetlb_cma_percent,100 ); + } +if (!hugetlb_cma_size)return ;@@ -163,6 +254,32 @@ void __init hugetlb_cma_reserve(void)*/ VM_WARN_ON (order <=MAX_PAGE_ORDER ); +if (hugetlb_cma_percent) { +unsigned long orig_size = hugetlb_cma_size; + + hugetlb_cma_size =ALIGN_DOWN (hugetlb_cma_size,PAGE_SIZE << order); +if (orig_size && !hugetlb_cma_size) +pr_warn ("hugetlb_cma: reservation size rounded down to 0 from %lu MiB (%u%%)\n" , + orig_size /SZ_1M , hugetlb_cma_percent); + }else if (has_node_specific_param) { + hugetlb_cma_size =0 ; +for (nid =0 ; nid <MAX_NUMNODES ; nid++) { +if (hugetlb_cma_percent_in_node[nid]) { +unsigned long orig_size = hugetlb_cma_size_in_node[nid]; + + hugetlb_cma_size_in_node[nid] = +ALIGN_DOWN (hugetlb_cma_size_in_node[nid], +PAGE_SIZE << order); +if (orig_size && !hugetlb_cma_size_in_node[nid]) +pr_warn ("hugetlb_cma: reservation size rounded down to 0 from %lu MiB (%u%%) on node %d\n" , + orig_size /SZ_1M , + hugetlb_cma_percent_in_node[nid], + nid); + } + hugetlb_cma_size += hugetlb_cma_size_in_node[nid]; + } + } +hugetlb_bootmem_set_nodes ();for (nid =0 ; nid <MAX_NUMNODES ; nid++) {@@ -205,8 +322,12 @@ void __init hugetlb_cma_reserve(void) per_node =DIV_ROUND_UP (hugetlb_cma_size,nodes_weight (hugetlb_bootmem_nodes)); per_node =round_up (per_node,PAGE_SIZE << order); -pr_info ("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n" , - hugetlb_cma_size /SZ_1M , per_node /SZ_1M ); +if (hugetlb_cma_percent) +pr_info ("hugetlb_cma: reserve %lu MiB (%u%%), up to %lu MiB per node\n" , + hugetlb_cma_size /SZ_1M , hugetlb_cma_percent, per_node /SZ_1M ); +else +pr_info ("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n" , + hugetlb_cma_size /SZ_1M , per_node /SZ_1M ); } reserved =0 ;@@ -241,8 +362,12 @@ void __init hugetlb_cma_reserve(void) } reserved += size; -pr_info ("hugetlb_cma: reserved %lu MiB on node %d\n" , - size /SZ_1M , nid); +if (hugetlb_cma_percent_in_node[nid]) +pr_info ("hugetlb_cma: reserved %lu MiB (%u%%) on node %d\n" , + size /SZ_1M , hugetlb_cma_percent_in_node[nid], nid); +else +pr_info ("hugetlb_cma: reserved %lu MiB on node %d\n" , + size /SZ_1M , nid);if (reserved >= hugetlb_cma_size)break ;