[PATCH v2 7/9] mm: percpu: per-node kmem accounting for obj_exts metadata
From: Alexandre Ghiti
Date: Fri Jun 26 2026 - 06:33:02 EST
Account the percpu obj_exts metadata to the correct NUMA node. The
obj_exts array is vmalloc'd and its pages may reside on different nodes,
so walk the vmalloc pages via vmalloc_to_page() + page_to_nid() and add
each page's slice to the same per-node byte accumulation as the per-cpu
payload. The post-alloc and free hooks then charge / uncharge the
combined accumulation per node, so the metadata rides the same batched
account_kmem() and the same precharged page pool as the payload.
Folding the metadata into the shared node_bytes[] accumulation means each
node is rounded up to whole pages only once, across payload and metadata
combined.
Note that there is no need to bump the number of pages to precharge here
since we already use pcpu_obj_full_size() and the same reasoning as the
previous commit applies here: we waste strictly less than N pages.
Signed-off-by: Alexandre Ghiti <alex@xxxxxxxx>
---
mm/percpu.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/mm/percpu.c b/mm/percpu.c
index e9d2d3716b99..9224344d4b8e 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1672,6 +1672,23 @@ static void pcpu_memcg_accumulate_pages(struct pcpu_chunk *chunk, int off,
}
}
+static void pcpu_memcg_accumulate_obj_exts(struct pcpu_chunk *chunk, int off,
+ size_t size, unsigned int *node_bytes)
+{
+ size_t ext_bytes = size / PCPU_MIN_ALLOC_SIZE * sizeof(struct pcpuobj_ext);
+ unsigned long ext_start = (unsigned long)&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT];
+ unsigned long ext_end = ext_start + ext_bytes;
+ unsigned long addr;
+
+ for (addr = ext_start; addr < ext_end; addr = ALIGN(addr + 1, PAGE_SIZE)) {
+ struct page *page = vmalloc_to_page((void *)addr);
+ size_t page_sz = min_t(size_t, ext_end - addr,
+ PAGE_SIZE - offset_in_page(addr));
+
+ node_bytes[page_to_nid(page)] += page_sz;
+ }
+}
+
static void pcpu_memcg_post_alloc_hook(struct obj_cgroup *objcg,
struct pcpu_chunk *chunk, int off,
size_t size)
@@ -1689,6 +1706,7 @@ static void pcpu_memcg_post_alloc_hook(struct obj_cgroup *objcg,
chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].cgroup = objcg;
pcpu_memcg_accumulate_pages(chunk, off, size, node_bytes);
+ pcpu_memcg_accumulate_obj_exts(chunk, off, size, node_bytes);
rcu_read_lock();
mod_memcg_state(obj_cgroup_memcg(objcg), MEMCG_PERCPU_B,
@@ -1731,6 +1749,7 @@ static void pcpu_memcg_free_hook(struct pcpu_chunk *chunk, int off, size_t size)
chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].cgroup = NULL;
pcpu_memcg_accumulate_pages(chunk, off, size, node_bytes);
+ pcpu_memcg_accumulate_obj_exts(chunk, off, size, node_bytes);
rcu_read_lock();
mod_memcg_state(obj_cgroup_memcg(objcg), MEMCG_PERCPU_B,
--
2.54.0