[PATCH v2 13/13] mm/slab: stop allocating objcg pointers when unnecessary
From: Vlastimil Babka (SUSE)
Date: Mon Jul 20 2026 - 10:34:02 EST
Start using the slab_needs_objcg() helper to calculate slabobj_ext size.
Caches that we know to never need objcg pointers (currently
KMALLOC_NORMAL caches) will thus stop wasting memory on them when memory
allocation profiling is enabled.
For things to work properly, we need to also add slab_needs_objcg()
checks to mem_cgroup_from_obj_slab() and memcg_slab_free_hook(), because
when obj_exts array exists for a slab only due to mem_alloc profiling,
we would otherwise attempt to access a non-existing objcg pointer in
that slab.
The function __memcg_slab_post_alloc_hook() should not be possible to
call for a slab where slab_needs_objcg() is false, but add a DEBUG_VM
check there to prevent breaking this assumption accidentally.
Signed-off-by: Vlastimil Babka (SUSE) <vbabka@xxxxxxxxxx>
---
mm/memcontrol.c | 6 ++++++
mm/slab.h | 14 +++++++++++---
mm/slub.c | 3 +++
3 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index cb1e97b4edc1..aace85fb99f9 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2871,6 +2871,9 @@ struct mem_cgroup *mem_cgroup_from_obj_slab(struct slab *slab, void *p)
if (!obj_exts)
return NULL;
+ if (!slab_needs_objcg(slab))
+ return NULL;
+
get_slab_obj_exts(obj_exts);
obj_ext = slab_obj_ext(slab->slab_cache, slab, obj_exts, p);
objcg = slab_obj_ext_objcg(obj_ext);
@@ -3580,6 +3583,9 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
slab = virt_to_slab(p[i]);
+ if (IS_ENABLED(CONFIG_DEBUG_VM) && WARN_ON_ONCE(!slab_needs_objcg(slab)))
+ continue;
+
if (!slab_obj_exts(slab) &&
alloc_slab_obj_exts(slab, s, flags, slab_alloc_flags)) {
continue;
diff --git a/mm/slab.h b/mm/slab.h
index 65b44902c06e..18c2a807f023 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -615,7 +615,7 @@ static inline size_t cache_obj_ext_size(struct kmem_cache *s)
{
size_t sz = 0;
- if (IS_ENABLED(CONFIG_MEMCG))
+ if (cache_needs_objcg(s))
sz += 1;
if (slab_obj_ext_has_codetag())
@@ -626,7 +626,15 @@ static inline size_t cache_obj_ext_size(struct kmem_cache *s)
static inline size_t slab_obj_ext_size(struct slab *slab)
{
- return cache_obj_ext_size(slab->slab_cache);
+ size_t sz = 0;
+
+ if (slab_needs_objcg(slab))
+ sz += 1;
+
+ if (slab_obj_ext_has_codetag())
+ sz += 1;
+
+ return sizeof(struct slabobj_ext) * sz;
}
#ifdef CONFIG_SLAB_OBJ_EXT
@@ -753,7 +761,7 @@ static inline void slab_obj_ext_set_objcg(struct slabobj_ext *obj_ext,
static inline union codetag_ref *
slab_obj_ext_codetag_ref(struct slab *slab, struct slabobj_ext *obj_ext)
{
- if (IS_ENABLED(CONFIG_MEMCG))
+ if (slab_needs_objcg(slab))
obj_ext += 1;
return &obj_ext->_ctref;
diff --git a/mm/slub.c b/mm/slub.c
index 30435e2509ea..e6148b73b92f 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2512,6 +2512,9 @@ void memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p,
if (likely(!obj_exts))
return;
+ if (!slab_needs_objcg(slab))
+ return;
+
get_slab_obj_exts(obj_exts);
__memcg_slab_free_hook(s, slab, p, objects, obj_exts);
put_slab_obj_exts(obj_exts);
--
2.55.0