[PATCH 04/11] mm: percpu: prepare to use dedicated percpu area

From: Yang Shi

Date: Wed Apr 29 2026 - 13:10:47 EST


The percpu variables are allocated from vmalloc area by default. The
architectures which support local percpu map will allocate percpu
variables from dedicated percpu area, for example, ARM64.

Introduce a new kernel config, CONFIG_HAVE_LOCAL_PER_CPU_MAP. The
architectures which support local percpu map will need to select it. If
it is enabled, allocate percpu variables from the dedicated percpu area.

Signed-off-by: Yang Shi <yang@xxxxxxxxxxxxxxxxxxxxxx>
---
mm/Kconfig | 3 +++
mm/percpu.c | 6 ++++++
mm/vmalloc.c | 20 +++++++++++++++++---
3 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/mm/Kconfig b/mm/Kconfig
index e8bf1e9e6ad9..ccdf58b63fb8 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1022,6 +1022,9 @@ config NEED_PER_CPU_PAGE_FIRST_CHUNK
config USE_PERCPU_NUMA_NODE_ID
bool

+config HAVE_LOCAL_PER_CPU_MAP
+ bool
+
config HAVE_SETUP_PER_CPU_AREA
bool

diff --git a/mm/percpu.c b/mm/percpu.c
index b0676b8054ed..daa2c88e6971 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -3243,9 +3243,15 @@ int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t
}

/* allocate vm area, map the pages and copy static data */
+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ vm.addr = (void *)ALIGN(PERCPU_START, PAGE_SIZE);
+ vm.size = num_possible_cpus() * ai->unit_size;
+ vm_area_add_early(&vm);
+#else
vm.flags = VM_ALLOC;
vm.size = num_possible_cpus() * ai->unit_size;
vm_area_register_early(&vm, PAGE_SIZE);
+#endif

for (unit = 0; unit < num_possible_cpus(); unit++) {
unsigned long unit_addr =
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index aa08651ec0df..068a6709062d 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4841,9 +4841,15 @@ pvm_find_va_enclose_addr(unsigned long addr)
static unsigned long
pvm_determine_end_from_reverse(struct vmap_area **va, unsigned long align)
{
- unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
+ unsigned long vmalloc_end;
unsigned long addr;

+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ vmalloc_end = PERCPU_END & ~(align - 1);
+#else
+ vmalloc_end = VMALLOC_END & ~(align - 1);
+#endif
+
if (likely(*va)) {
list_for_each_entry_from_reverse((*va),
&free_vmap_area_list, list) {
@@ -4884,14 +4890,22 @@ struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
const size_t *sizes, int nr_vms,
size_t align)
{
- const unsigned long vmalloc_start = ALIGN(VMALLOC_START, align);
- const unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
+ unsigned long vmalloc_start;
+ unsigned long vmalloc_end;
struct vmap_area **vas, *va;
struct vm_struct **vms;
int area, area2, last_area, term_area;
unsigned long base, start, size, end, last_end, orig_start, orig_end;
bool purged = false;

+#ifdef CONFIG_HAVE_LOCAL_PER_CPU_MAP
+ vmalloc_start = ALIGN(PERCPU_START, align);
+ vmalloc_end = PERCPU_END & ~(align - 1);
+#else
+ vmalloc_start = ALIGN(VMALLOC_START, align);
+ vmalloc_end = VMALLOC_END & ~(align - 1);
+#endif
+
/* verify parameters and allocate data structures */
BUG_ON(offset_in_page(align) || !is_power_of_2(align));
for (last_area = 0, area = 0; area < nr_vms; area++) {
--
2.47.0