On Thu, Aug 13, 2026 at 2:34 AM Hao Ge <[email protected]> wrote:
>
> After shutdown_mem_profiling() clears mem_profiling_support,
> needs_section_mem() returns false, so later modules have their codetag
> section placed as regular data and never enter the alloc_tag maple tree.
> codetag_load_module() still called load_module(), which allocated a percpu
> counter for every tag; release_module_tags() could not find these modules
> on unload, so the counters leaked.
>
> Return CODETAG_MODULE_EXCLUDED from load_module() when profiling is off:
> codetag_module_init() drops the module's cmod and no counters are
> allocated. codetag_unload_module() now always calls free_section_mem(),
> since an excluded module may still hold a reserved section.
>
> Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
> Signed-off-by: Hao Ge <[email protected]>
Thanks for the fix. I think it could be done simpler, see below.
> ---
> include/linux/codetag.h | 4 ++++
> lib/codetag.c | 8 +++++---
> mm/alloc_tag.c | 8 ++++++--
> 3 files changed, 15 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/codetag.h b/include/linux/codetag.h
> index a25a085c2df1..88081c618673 100644
> --- a/include/linux/codetag.h
> +++ b/include/linux/codetag.h
> @@ -52,6 +52,10 @@ struct codetag_type_desc {
> #endif
> };
>
> +/* module_load() return values */
> +#define CODETAG_MODULE_LOAD 0 /* module loads with its tags */
> +#define CODETAG_MODULE_EXCLUDED 1 /* module loads without its
> tags */
I see no reason for adding these special values. You could simply
return -ENOTSUP when profiling is disabled.
> +
> struct codetag_iterator {
> struct codetag_type *cttype;
> struct codetag_module *cmod;
> diff --git a/lib/codetag.c b/lib/codetag.c
> index a9cda4c962a3..8506ecab9ea7 100644
> --- a/lib/codetag.c
> +++ b/lib/codetag.c
> @@ -238,9 +238,10 @@ static int codetag_module_init(struct codetag_type
> *cttype, struct module *mod)
> }
> up_write(&cttype->mod_lock);
>
> - if (err < 0) {
> + if (err) {
> + /* Error or excluded: cmod is dropped, free it. */
IIUC here you want to call kfree() if profiling got disabled. If you
return -ENOTSUP instead of CODETAG_MODULE_EXCLUDED then this condition
does not need to change.
> kfree(cmod);
> - return err;
> + return err < 0 ? err : 0;
Here you can do:
if (err && err != -ENOTSUP)
return err;
return 0;
> }
>
> return 0;
> @@ -388,7 +389,8 @@ void codetag_unload_module(struct module *mod)
> ++cttype->content_id;
> }
> up_write(&cttype->mod_lock);
> - if (found && cttype->desc.free_section_mem)
> + /* an excluded module may still hold section memory */
> + if (cttype->desc.free_section_mem)
> cttype->desc.free_section_mem(mod, true);
> }
> mutex_unlock(&codetag_lock);
> diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
> index 0a7b657fe2de..461fa87fbb0b 100644
> --- a/mm/alloc_tag.c
> +++ b/mm/alloc_tag.c
> @@ -977,9 +977,13 @@ static int load_module(struct module *mod, struct
> codetag *start, struct codetag
> struct alloc_tag *stop_tag;
> struct alloc_tag *tag;
>
> + /* Profiling disabled: load the module but exclude its tags. */
> + if (!mem_profiling_support)
> + return CODETAG_MODULE_EXCLUDED;
Return -ENOTSUP here.
> +
> /* percpu counters for core allocations are already statically
> allocated */
> if (!mod)
> - return 0;
> + return CODETAG_MODULE_LOAD;
>
> start_tag = ct_to_alloc_tag(start);
> stop_tag = ct_to_alloc_tag(stop);
> @@ -1002,7 +1006,7 @@ static int load_module(struct module *mod, struct
> codetag *start, struct codetag
> */
> kmemleak_ignore_percpu(tag->counters);
> }
> - return 0;
> + return CODETAG_MODULE_LOAD;
> }
>
> static void replace_module(struct module *mod, struct module *new_mod)
> --
> 2.25.1
>