Re: [PATCH] mm/percpu: account full object size for percpu memory allocations

From: Joshua Hahn

Date: Wed Oct 14 2026 - 12:52:48 EST


On Tue, 13 Oct 2026 20:34:39 +0100 "Harry Yoo (Meta)" <harry@xxxxxxxxxx> wrote:

> When memory allocation profiling is enabled, the pages backing percpu
> memory chunks are attributed to func:pcpu_alloc_pages. However, the
> accounting of individual percpu memory allocations via alloc_percpu()
> and friends record only the size of a single object rather than its
> full size across all CPUs.
>
> As a result, it is often not straightforward to identify which kernel
> code is actually responsible for percpu memory, because the size is
> under-accounted by a factor of num_possible_cpus().
>
> Account full object size with pcpu_obj_full_size(size) instead, which
> multiplies the object size by num_possible_cpus() and adds the objcg
> pointer overhead. This is consistent with what memcg already does for
> charging percpu allocations.

Hi Harry,

This looks quite good to me. Thanks for finding this!
Reviewed-by: Joshua Hahn <joshua.hahnjy@xxxxxxxxx>

Some comments below that shouldn't be blocking:

> Verified with 64-CPU VM by spawning 400 processes in a memory cgroup and
> measuring mm_init()'s rss_stat counters.
>
> Delta of kernel/fork.c:1142 func:mm_init in /proc/allocinfo:
>
> bytes calls per allocation
> unpatched 6320 395 16
> patched 418176 396 1056
>
> 'percpu' field in memory.stat: 422400 (1056 bytes per process)
>
> Memory allocation profiling now accounts the same amount of memory as
> what memcg charges.
>
> Cc: stable@xxxxxxxxxxxxxxx
> Fixes: 60fa4a9e2323 ("mm: percpu: add codetag reference into pcpuobj_ext")
> Assisted-by: LLM # Verification
> Signed-off-by: Harry Yoo (Meta) <harry@xxxxxxxxxx>
> ---
> mm/percpu.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/mm/percpu.c b/mm/percpu.c
> index 3eff382e565a..d7883507dc15 100644
> --- a/mm/percpu.c
> +++ b/mm/percpu.c
> @@ -1696,14 +1696,15 @@ static void pcpu_alloc_tag_alloc_hook(struct pcpu_chunk *chunk, int off,
> {
> if (mem_alloc_profiling_enabled() && likely(chunk->obj_exts)) {
> alloc_tag_add(&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].tag,
> - current->alloc_tag, size);
> + current->alloc_tag, pcpu_obj_full_size(size));
> }
> }

So @size by itself isn't used, so I was looking to see if we could just
pass pcpu_obj_full_size(size) to the function instead. Looking at the
call site, pcpu_memcg_post_alloc_hook() also has the same oddity where
pcpu_obj_full_size(size) is used for all calcualtions.

So I'm wondering if we can send a follow up to just unify all of these
and pass pcpu_obj_full_size(size) where the caller never uses @size
by itself, I think it would also help in demonstrating the intent to
account the full size. Same idea for the free hook below as well,
paired with pcpu_memcg_free_hook().

Happy to follow up on that myself too : -)

Anyways, the change looks good. Just wanted to bring this up as a
follow-up cleanup patch.

Have a great day Harry!

> static void pcpu_alloc_tag_free_hook(struct pcpu_chunk *chunk, int off, size_t size)
> {
> if (mem_alloc_profiling_enabled() && likely(chunk->obj_exts))
> - alloc_tag_sub(&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].tag, size);
> + alloc_tag_sub(&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].tag,
> + pcpu_obj_full_size(size));
> }
> #else
> static void pcpu_alloc_tag_alloc_hook(struct pcpu_chunk *chunk, int off,
>
> ---
> base-commit: 0e199ae6e803e76ebf128e009d336e5917221e63
> change-id: 20261013-fix-alloc-percpu-accounting-48bf24919caf
>
> Best regards,
> --
> Cheers,
> Harry / Hyeonggon