Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH RFC 00/12] mm/slab, alloc_tag: reduce obj_ext memory waste
       [not found] <20260715-b4-objext_split-v1-0-9a49c4ccf4c3@kernel.org>
@ 2026-07-15 15:32 ` Suren Baghdasaryan
       [not found] ` <20260715-b4-objext_split-v1-1-9a49c4ccf4c3@kernel.org>
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-15 15:32 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:10 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> The recent fixes for objext array handling inspired me to look into this
> finally. It's been bothering me that the memory usage of struct
> slabobj_ext depend only on config options and not whether the fields are
> actually used. So with both CONFIG_MEMCG=y and
> CONFIG_MEM_ALLOC_PROFILING=y there is always objcg field and codetag_ref
> field. And thus:
>
> 1) Having memory allocation profiling config-enabled but not
>    boot-enabled means wasted memory on unused codetag_refs. This makes
>    it less suitable for a general distro config and the page allocator
>    side doesn't suffer from this, only slab and percpu.
>
> 2) Complementary, with memory allocation profiling enabled, there are
>    caches/slabs that don't need the objcg field, so memory is wasted on
>    those.

It's funny because yesterday I started working on a prototype for the
same optimization. But your patchset is much more mature, so I'll
focus instead on reviewing it.

>
> This series should solve the point 1) fully for slab, pcpuobj_ext
> handling can be perhaps improved similarly, haven't looked into that.
>
> For 2) it avoids allocating objcg fields for KMALLOC_NORMAL caches where
> we know they are not necessary because kmalloc() with __GFP_ACCOUNT will
> pick a KMALLOC_CGROUP type.
>
> The named kmem_caches are tricky. They can be created with SLAB_ACCOUNT
> and then we know objcg fields are always needed. But also they can be
> created without SLAB_ACCOUNT and then some allocations have
> __GFP_ACCOUNT and some not and we don't know that in advance.

Do you know how often this happens that a named cache with no
SLAB_ACCOUNT is used for __GFP_ACCOUNT allocation?

>
> A possible future solution is to introduce e.g. SLAB_MAYBE_ACCOUNT, add
> it to caches where we know __GFP_ACCOUNT is used, and only honour
> __GFP_ACCOUNT for those, while warning for an unexpected usage
> elsewhere.

I wonder if for such caches we could create two separate caches, one
serving __GFP_ACCOUNT and using extentions containing objcg and
another one for non-__GFP_ACCOUNT with optimized extentions?

>
> Only lightly tested, need to run at least some microbenchmarks to see if
> the now somewhat more complicated access to objcg is visible or not.
>
> Based on slab/for-next-fixes
>
> Git branch: https://git.kernel.org/pub/scm/linux/kernel/git/vbabka/linux.git/log/?h=b4/objext_split
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
> Vlastimil Babka (SUSE) (12):
>       mm/slab: skip kfence objects in allocation profiling
>       mm/slab: remove objs_per_slab()
>       mm: move struct slabobj_ext to mm/slab.h
>       mm/slab: make slab_obj_ext() determine object index
>       mm/slab: abstract slabobj_ext.objcg access
>       mm/slab: abstract slabobj_ext.ref access
>       mm/slab: replace slab.stride with obj_exts_in_object
>       mm/slab: change struct slabobj_ext to a union
>       mm/slab: introduce slab_obj_ext_has_codetag()
>       mm/slab: reduce slabobj_ext memory with allocation profiling disabled
>       mm/slab: add slab_needs_objcg() helper
>       mm/slab: stop allocating objcg pointers when unnecessary
>
>  include/linux/memcontrol.h |  13 ----
>  mm/kfence/core.c           |   5 +-
>  mm/kfence/kfence_test.c    |   2 +-
>  mm/memcontrol.c            |  41 ++++++-----
>  mm/slab.h                  | 169 ++++++++++++++++++++++++++++++++++++---------
>  mm/slub.c                  | 162 +++++++++++++++++++++++++++----------------
>  6 files changed, 267 insertions(+), 125 deletions(-)
> ---
> base-commit: d9e6a7623938968e3752b67e37eaff097e559a54
> change-id: 20260714-b4-objext_split-da82426257d5
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 01/12] mm/slab: skip kfence objects in allocation profiling
       [not found] ` <20260715-b4-objext_split-v1-1-9a49c4ccf4c3@kernel.org>
@ 2026-07-15 16:02   ` Suren Baghdasaryan
       [not found]     ` <ab9782bf-d3dd-4cb5-ae66-127b3a16330b@kernel.org>
  0 siblings, 1 reply; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-15 16:02 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:10 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> struct kfence_metadata only has obj_exts with CONFIG_MEMCG.

I don't quite understand this statement. obj_exts are allocated when
either CONFIG_MEMCG or CONFIG_MEM_ALLOC_PROFILING is enabled.

> If it's
> enabled, it does also work for allocation profiling, but there's little
> value recording tags for KFENCE objects.

Unless we are leaking them, right?

> Furthermore it would complicate
> the upcoming changes, so just skip them in the slab hooks.

Ok, I can understand that. If we do this, we should document that
kfence objects are no longer tracked.

>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
>  mm/slub.c | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/mm/slub.c b/mm/slub.c
> index 0337e60db5ac..a4be70d080fb 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -2352,6 +2352,9 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags,
>         if (alloc_flags & SLAB_ALLOC_NO_RECURSE)
>                 return;
>
> +       if (is_kfence_address(object))
> +               return;
> +
>         slab = virt_to_slab(object);
>         obj_exts = prepare_slab_obj_exts_hook(s, slab, flags, alloc_flags, object);
>         /*
> @@ -2399,6 +2402,9 @@ __alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p
>         for (i = 0; i < objects; i++) {
>                 unsigned int off = obj_to_index(s, slab, p[i]);
>
> +               if (is_kfence_address(p[i]))
> +                       continue;
> +
>                 alloc_tag_sub(&slab_obj_ext(slab, obj_exts, off)->ref, s->size);
>         }
>         put_slab_obj_exts(obj_exts);
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 02/12] mm/slab: remove objs_per_slab()
       [not found] ` <20260715-b4-objext_split-v1-2-9a49c4ccf4c3@kernel.org>
@ 2026-07-15 16:13   ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-15 16:13 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:10 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> The function has an unused kmem_cache argument and almost nothing uses
> it anyway; doing slab->objects is simpler. Remove it with the last two
> users. KUNIT_EXPECT_EQ() needs a cast to avoid "error: ‘typeof’ applied
> to a bit-field" but we don't need to keep a wrapper just for that.
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Suren Baghdasayan <surenb@google.com>

> ---
>  mm/kfence/kfence_test.c | 2 +-
>  mm/slab.h               | 6 ------
>  mm/slub.c               | 3 +--
>  3 files changed, 2 insertions(+), 9 deletions(-)
>
> diff --git a/mm/kfence/kfence_test.c b/mm/kfence/kfence_test.c
> index de2d0f7d62b1..9867c03ef0ae 100644
> --- a/mm/kfence/kfence_test.c
> +++ b/mm/kfence/kfence_test.c
> @@ -295,7 +295,7 @@ static void *test_alloc(struct kunit *test, size_t size, gfp_t gfp, enum allocat
>                          * memcg accounting works correctly.
>                          */
>                         KUNIT_EXPECT_EQ(test, obj_to_index(s, slab, alloc), 0U);
> -                       KUNIT_EXPECT_EQ(test, objs_per_slab(s, slab), 1);
> +                       KUNIT_EXPECT_EQ(test, ((unsigned int)slab->objects), 1);
>
>                         if (policy == ALLOCATE_ANY)
>                                 return alloc;
> diff --git a/mm/slab.h b/mm/slab.h
> index f5e336b6b6b0..01535e1e2d3c 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -330,12 +330,6 @@ static inline unsigned int obj_to_index(const struct kmem_cache *cache,
>         return __obj_to_index(cache, slab_address(slab), obj);
>  }
>
> -static inline int objs_per_slab(const struct kmem_cache *cache,
> -                               const struct slab *slab)
> -{
> -       return slab->objects;
> -}
> -
>  /*
>   * State of the slab allocator.
>   *
> diff --git a/mm/slub.c b/mm/slub.c
> index a4be70d080fb..9e25f2dce7a6 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -2127,7 +2127,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
>                         gfp_t gfp, unsigned int alloc_flags)
>  {
>         const bool allow_spin = alloc_flags_allow_spinning(alloc_flags);
> -       unsigned int objects = objs_per_slab(s, slab);
>         bool new_slab = alloc_flags & SLAB_ALLOC_NEW_SLAB;
>         unsigned long new_exts;
>         unsigned long old_exts;
> @@ -2183,7 +2182,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
>  #endif
>  retry:
>         old_exts = READ_ONCE(slab->obj_exts);
> -       handle_failed_objexts_alloc(old_exts, vec, objects);
> +       handle_failed_objexts_alloc(old_exts, vec, slab->objects);
>
>         if (new_slab) {
>                 /*
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 03/12] mm: move struct slabobj_ext to mm/slab.h
       [not found] ` <20260715-b4-objext_split-v1-3-9a49c4ccf4c3@kernel.org>
@ 2026-07-16  0:56   ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16  0:56 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:10 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> Users of include/linux/memcontrol.h don't need to see this internal
> structure. Further changes to the struct will reduce recompiling.
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Suren Baghdasaryan <surenb@google.com>

> ---
>  include/linux/memcontrol.h | 13 -------------
>  mm/slab.h                  | 13 +++++++++++++
>  2 files changed, 13 insertions(+), 13 deletions(-)
>
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index e1f46a0016fc..93869cc35c25 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -1440,19 +1440,6 @@ static inline void mem_cgroup_flush_workqueue(void) { }
>  static inline int mem_cgroup_init(void) { return 0; }
>  #endif /* CONFIG_MEMCG */
>
> -/*
> - * Extended information for slab objects stored as an array in page->memcg_data
> - * if MEMCG_DATA_OBJEXTS is set.
> - */
> -struct slabobj_ext {
> -#ifdef CONFIG_MEMCG
> -       struct obj_cgroup *objcg;
> -#endif
> -#ifdef CONFIG_MEM_ALLOC_PROFILING
> -       union codetag_ref ref;
> -#endif
> -} __aligned(8);
> -
>  static inline struct lruvec *parent_lruvec(struct lruvec *lruvec)
>  {
>         struct mem_cgroup *memcg;
> diff --git a/mm/slab.h b/mm/slab.h
> index 01535e1e2d3c..7bd361447c54 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -549,6 +549,19 @@ static inline bool need_kmalloc_no_objext(void)
>         return false;
>  }
>
> +/*
> + * Extended information for slab objects stored as an array in page->memcg_data
> + * if MEMCG_DATA_OBJEXTS is set.
> + */
> +struct slabobj_ext {
> +#ifdef CONFIG_MEMCG
> +       struct obj_cgroup *objcg;
> +#endif
> +#ifdef CONFIG_MEM_ALLOC_PROFILING
> +       union codetag_ref ref;
> +#endif
> +} __aligned(8);
> +
>  #ifdef CONFIG_SLAB_OBJ_EXT
>
>  /*
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 04/12] mm/slab: make slab_obj_ext() determine object index
       [not found] ` <20260715-b4-objext_split-v1-4-9a49c4ccf4c3@kernel.org>
@ 2026-07-16  1:02   ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16  1:02 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> All callers perform the same obj_to_index() calculation to pass the
> index. Simplify by passing object pointer instead and determining the
> index by slab_obj_ext().
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Suren Baghdasaryan <surenb@google.com>

> ---
>  mm/memcontrol.c | 12 +++---------
>  mm/slab.h       | 14 ++++++++------
>  mm/slub.c       | 22 +++++++---------------
>  3 files changed, 18 insertions(+), 30 deletions(-)
>
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 6dc4888a90f3..4e427286a88a 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -2865,15 +2865,13 @@ struct mem_cgroup *mem_cgroup_from_obj_slab(struct slab *slab, void *p)
>          */
>         unsigned long obj_exts;
>         struct slabobj_ext *obj_ext;
> -       unsigned int off;
>
>         obj_exts = slab_obj_exts(slab);
>         if (!obj_exts)
>                 return NULL;
>
>         get_slab_obj_exts(obj_exts);
> -       off = obj_to_index(slab->slab_cache, slab, p);
> -       obj_ext = slab_obj_ext(slab, obj_exts, off);
> +       obj_ext = slab_obj_ext(slab->slab_cache, slab, obj_exts, p);
>         if (obj_ext->objcg) {
>                 struct obj_cgroup *objcg = obj_ext->objcg;
>
> @@ -3541,7 +3539,6 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
>         size_t obj_size = obj_full_size(s);
>         struct obj_cgroup *objcg;
>         struct slab *slab;
> -       unsigned long off;
>         size_t i;
>
>         /*
> @@ -3616,8 +3613,7 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
>
>                 obj_exts = slab_obj_exts(slab);
>                 get_slab_obj_exts(obj_exts);
> -               off = obj_to_index(s, slab, p[i]);
> -               obj_ext = slab_obj_ext(slab, obj_exts, off);
> +               obj_ext = slab_obj_ext(s, slab, obj_exts, p[i]);
>                 obj_cgroup_get(objcg);
>                 obj_ext->objcg = objcg;
>                 put_slab_obj_exts(obj_exts);
> @@ -3635,10 +3631,8 @@ void __memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
>                 struct obj_cgroup *objcg;
>                 struct slabobj_ext *obj_ext;
>                 struct obj_stock_pcp *stock;
> -               unsigned int off;
>
> -               off = obj_to_index(s, slab, p[i]);
> -               obj_ext = slab_obj_ext(slab, obj_exts, off);
> +               obj_ext = slab_obj_ext(s, slab, obj_exts, p[i]);
>                 objcg = obj_ext->objcg;
>                 if (!objcg)
>                         continue;
> diff --git a/mm/slab.h b/mm/slab.h
> index 7bd361447c54..36d067d6e7c0 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -646,14 +646,16 @@ static inline unsigned int slab_get_stride(struct slab *slab)
>   * Returns a pointer to the object extension associated with the object.
>   * Must be called within a section covered by get/put_slab_obj_exts().
>   */
> -static inline struct slabobj_ext *slab_obj_ext(struct slab *slab,
> -                                              unsigned long obj_exts,
> -                                              unsigned int index)
> +static inline struct slabobj_ext *
> +slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
> +            const void *obj)
>  {
>         struct slabobj_ext *obj_ext;
> +       unsigned int index;
>
>         VM_WARN_ON_ONCE(obj_exts != slab_obj_exts(slab));
>
> +       index = obj_to_index(s, slab, obj);
>         obj_ext = (struct slabobj_ext *)(obj_exts +
>                                          slab_get_stride(slab) * index);
>         return kasan_reset_tag(obj_ext);
> @@ -669,9 +671,9 @@ static inline unsigned long slab_obj_exts(struct slab *slab)
>         return 0;
>  }
>
> -static inline struct slabobj_ext *slab_obj_ext(struct slab *slab,
> -                                              unsigned long obj_exts,
> -                                              unsigned int index)
> +static inline struct slabobj_ext *
> +slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
> +            const void *obj)
>  {
>         return NULL;
>  }
> diff --git a/mm/slub.c b/mm/slub.c
> index 9e25f2dce7a6..5e3f53bcd0d3 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -2070,11 +2070,10 @@ static inline void mark_obj_codetag_empty(const void *obj)
>         obj_slab = virt_to_slab(obj);
>         slab_exts = slab_obj_exts(obj_slab);
>         if (slab_exts) {
> +               struct slabobj_ext *ext;
> +
>                 get_slab_obj_exts(slab_exts);
> -               unsigned int offs = obj_to_index(obj_slab->slab_cache,
> -                                                obj_slab, obj);
> -               struct slabobj_ext *ext = slab_obj_ext(obj_slab,
> -                                                      slab_exts, offs);
> +               ext = slab_obj_ext(obj_slab->slab_cache, obj_slab, slab_exts, obj);
>
>                 if (unlikely(is_codetag_empty(&ext->ref))) {
>                         put_slab_obj_exts(slab_exts);
> @@ -2362,10 +2361,8 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags,
>          * check should be added before alloc_tag_add().
>          */
>         if (obj_exts) {
> -               unsigned int obj_idx = obj_to_index(s, slab, object);
> -
>                 get_slab_obj_exts(obj_exts);
> -               obj_ext = slab_obj_ext(slab, obj_exts, obj_idx);
> +               obj_ext = slab_obj_ext(s, slab, obj_exts, object);
>                 alloc_tag_add(&obj_ext->ref, current->alloc_tag, s->size);
>                 put_slab_obj_exts(obj_exts);
>         } else {
> @@ -2386,7 +2383,6 @@ static noinline void
>  __alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p,
>                                int objects)
>  {
> -       int i;
>         unsigned long obj_exts;
>
>         /* slab->obj_exts might not be NULL if it was created for MEMCG accounting. */
> @@ -2398,13 +2394,11 @@ __alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p
>                 return;
>
>         get_slab_obj_exts(obj_exts);
> -       for (i = 0; i < objects; i++) {
> -               unsigned int off = obj_to_index(s, slab, p[i]);
> -
> +       for (int i = 0; i < objects; i++) {
>                 if (is_kfence_address(p[i]))
>                         continue;
>
> -               alloc_tag_sub(&slab_obj_ext(slab, obj_exts, off)->ref, s->size);
> +               alloc_tag_sub(&slab_obj_ext(s, slab, obj_exts, p[i])->ref, s->size);
>         }
>         put_slab_obj_exts(obj_exts);
>  }
> @@ -2489,7 +2483,6 @@ bool memcg_slab_post_charge(void *p, gfp_t flags)
>         struct kmem_cache *s;
>         struct page *page;
>         struct slab *slab;
> -       unsigned long off;
>
>         page = virt_to_page(p);
>         if (PageLargeKmalloc(page)) {
> @@ -2529,8 +2522,7 @@ bool memcg_slab_post_charge(void *p, gfp_t flags)
>         obj_exts = slab_obj_exts(slab);
>         if (obj_exts) {
>                 get_slab_obj_exts(obj_exts);
> -               off = obj_to_index(s, slab, p);
> -               obj_ext = slab_obj_ext(slab, obj_exts, off);
> +               obj_ext = slab_obj_ext(s, slab, obj_exts, p);
>                 if (unlikely(obj_ext->objcg)) {
>                         put_slab_obj_exts(obj_exts);
>                         return true;
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 05/12] mm/slab: abstract slabobj_ext.objcg access
       [not found] ` <20260715-b4-objext_split-v1-5-9a49c4ccf4c3@kernel.org>
@ 2026-07-16  1:27   ` Suren Baghdasaryan
  2026-07-16 13:58     ` Vlastimil Babka (SUSE)
  0 siblings, 1 reply; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16  1:27 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> In preparation for changes to the structure, abstract access to the
> objcg field with a slab_obj_ext_objcgp() function.
> Rename the field to _objcg to make an unexpected direct access a compile
> error.
>
> No functional change intended.
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
>  mm/kfence/core.c |  2 +-
>  mm/memcontrol.c  | 23 +++++++++++++++--------
>  mm/slab.h        |  9 ++++++++-
>  mm/slub.c        |  2 +-
>  4 files changed, 25 insertions(+), 11 deletions(-)
>
> diff --git a/mm/kfence/core.c b/mm/kfence/core.c
> index 6577bd76954e..717e8baf7e5d 100644
> --- a/mm/kfence/core.c
> +++ b/mm/kfence/core.c
> @@ -1249,7 +1249,7 @@ void __kfence_free(void *addr)
>         struct kfence_metadata *meta = addr_to_metadata((unsigned long)addr);
>
>  #ifdef CONFIG_MEMCG
> -       KFENCE_WARN_ON(meta->obj_exts.objcg);
> +       KFENCE_WARN_ON(*slab_obj_ext_objcgp(&meta->obj_exts));
>  #endif
>         /*
>          * If the objects of the cache are SLAB_TYPESAFE_BY_RCU, defer freeing
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 4e427286a88a..6303a2b1a9d0 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -2865,6 +2865,7 @@ struct mem_cgroup *mem_cgroup_from_obj_slab(struct slab *slab, void *p)
>          */
>         unsigned long obj_exts;
>         struct slabobj_ext *obj_ext;
> +       struct obj_cgroup *objcg;
>
>         obj_exts = slab_obj_exts(slab);
>         if (!obj_exts)
> @@ -2872,9 +2873,8 @@ struct mem_cgroup *mem_cgroup_from_obj_slab(struct slab *slab, void *p)
>
>         get_slab_obj_exts(obj_exts);
>         obj_ext = slab_obj_ext(slab->slab_cache, slab, obj_exts, p);
> -       if (obj_ext->objcg) {
> -               struct obj_cgroup *objcg = obj_ext->objcg;
> -
> +       objcg = *slab_obj_ext_objcgp(obj_ext);
> +       if (objcg) {
>                 put_slab_obj_exts(obj_exts);
>                 return obj_cgroup_memcg(objcg);
>         }
> @@ -3577,6 +3577,7 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
>                 unsigned long obj_exts;
>                 struct slabobj_ext *obj_ext;
>                 struct obj_stock_pcp *stock;
> +               struct obj_cgroup **objcgp;
>
>                 slab = virt_to_slab(p[i]);
>
> @@ -3612,10 +3613,15 @@ bool __memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru,
>                 unlock_stock(stock);
>
>                 obj_exts = slab_obj_exts(slab);
> +
>                 get_slab_obj_exts(obj_exts);
> +
>                 obj_ext = slab_obj_ext(s, slab, obj_exts, p[i]);
> +               objcgp = slab_obj_ext_objcgp(obj_ext);
> +
>                 obj_cgroup_get(objcg);
> -               obj_ext->objcg = objcg;
> +               *objcgp = objcg;
> +
>                 put_slab_obj_exts(obj_exts);
>         }
>
> @@ -3628,16 +3634,17 @@ void __memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
>         size_t obj_size = obj_full_size(s);
>
>         for (int i = 0; i < objects; i++) {
> -               struct obj_cgroup *objcg;
> +               struct obj_cgroup **objcgp, *objcg;
>                 struct slabobj_ext *obj_ext;
>                 struct obj_stock_pcp *stock;
>
>                 obj_ext = slab_obj_ext(s, slab, obj_exts, p[i]);
> -               objcg = obj_ext->objcg;
> -               if (!objcg)
> +               objcgp = slab_obj_ext_objcgp(obj_ext);
> +               if (!*objcgp)
>                         continue;
>
> -               obj_ext->objcg = NULL;
> +               objcg = *objcgp;
> +               *objcgp = NULL;
>
>                 stock = trylock_stock();
>                 __refill_obj_stock(objcg, stock, obj_size, true);
> diff --git a/mm/slab.h b/mm/slab.h
> index 36d067d6e7c0..789bd292075f 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -555,7 +555,7 @@ static inline bool need_kmalloc_no_objext(void)
>   */
>  struct slabobj_ext {
>  #ifdef CONFIG_MEMCG
> -       struct obj_cgroup *objcg;
> +       struct obj_cgroup *_objcg;
>  #endif
>  #ifdef CONFIG_MEM_ALLOC_PROFILING
>         union codetag_ref ref;
> @@ -661,6 +661,13 @@ slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
>         return kasan_reset_tag(obj_ext);
>  }
>
> +#ifdef CONFIG_MEMCG
> +static inline struct obj_cgroup **slab_obj_ext_objcgp(struct slabobj_ext *obj_ext)

I understand why you need to return obj_cgroup** but maybe instead we
can have a separate setter function and just return a pointer here?
That would also help catch cases when someone wants to set
obj_ext._objcg that was optimized away (for that the setter would need
an additional parameter to identify whether we allocated optimized
objext vector).

> +{
> +       return &obj_ext->_objcg;
> +}
> +#endif
> +
>  int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
>                         gfp_t gfp, unsigned int alloc_flags);
>
> diff --git a/mm/slub.c b/mm/slub.c
> index 5e3f53bcd0d3..48e10198a3ce 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -2523,7 +2523,7 @@ bool memcg_slab_post_charge(void *p, gfp_t flags)
>         if (obj_exts) {
>                 get_slab_obj_exts(obj_exts);
>                 obj_ext = slab_obj_ext(s, slab, obj_exts, p);
> -               if (unlikely(obj_ext->objcg)) {
> +               if (unlikely(*slab_obj_ext_objcgp(obj_ext))) {
>                         put_slab_obj_exts(obj_exts);
>                         return true;
>                 }
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 06/12] mm/slab: abstract slabobj_ext.ref access
       [not found] ` <20260715-b4-objext_split-v1-6-9a49c4ccf4c3@kernel.org>
@ 2026-07-16  1:28   ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16  1:28 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> In preparation for changes to the structure, abstract access to the ref
> field with a slab_obj_ext_codetag_ref() function. Rename the field to
> _ctref to make an unexpected direct access a compile error.
>
> No functional change intended.
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Suren Baghdasaryan <surenb@google.com>

> ---
>  mm/slab.h | 10 +++++++++-
>  mm/slub.c | 42 ++++++++++++++++++++++++++++--------------
>  2 files changed, 37 insertions(+), 15 deletions(-)
>
> diff --git a/mm/slab.h b/mm/slab.h
> index 789bd292075f..e3f8e42070f1 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -558,7 +558,7 @@ struct slabobj_ext {
>         struct obj_cgroup *_objcg;
>  #endif
>  #ifdef CONFIG_MEM_ALLOC_PROFILING
> -       union codetag_ref ref;
> +       union codetag_ref _ctref;
>  #endif
>  } __aligned(8);
>
> @@ -668,6 +668,14 @@ static inline struct obj_cgroup **slab_obj_ext_objcgp(struct slabobj_ext *obj_ex
>  }
>  #endif
>
> +#ifdef CONFIG_MEM_ALLOC_PROFILING
> +static inline union codetag_ref *
> +slab_obj_ext_codetag_ref(struct slab *slab, struct slabobj_ext *obj_ext)
> +{
> +       return &obj_ext->_ctref;
> +}
> +#endif
> +
>  int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
>                         gfp_t gfp, unsigned int alloc_flags);
>
> diff --git a/mm/slub.c b/mm/slub.c
> index 48e10198a3ce..2bfcabc4c51a 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -2071,18 +2071,20 @@ static inline void mark_obj_codetag_empty(const void *obj)
>         slab_exts = slab_obj_exts(obj_slab);
>         if (slab_exts) {
>                 struct slabobj_ext *ext;
> +               union codetag_ref *ref;
>
>                 get_slab_obj_exts(slab_exts);
>                 ext = slab_obj_ext(obj_slab->slab_cache, obj_slab, slab_exts, obj);
> +               ref = slab_obj_ext_codetag_ref(obj_slab, ext);
>
> -               if (unlikely(is_codetag_empty(&ext->ref))) {
> +               if (unlikely(is_codetag_empty(ref))) {
>                         put_slab_obj_exts(slab_exts);
>                         return;
>                 }
>
>                 /* codetag should be NULL here */
> -               WARN_ON(ext->ref.ct);
> -               set_codetag_empty(&ext->ref);
> +               WARN_ON(ref->ct);
> +               set_codetag_empty(ref);
>                 put_slab_obj_exts(slab_exts);
>         }
>  }
> @@ -2092,19 +2094,22 @@ static inline bool mark_failed_objexts_alloc(struct slab *slab)
>         return cmpxchg(&slab->obj_exts, 0, OBJEXTS_ALLOC_FAIL) == 0;
>  }
>
> -static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
> -                       struct slabobj_ext *vec, unsigned int objects)
> +static inline void handle_failed_objexts_alloc(struct slab *slab,
> +               unsigned long obj_exts, struct slabobj_ext *vec)
>  {
>         /*
>          * If vector previously failed to allocate then we have live
>          * objects with no tag reference. Mark all references in this
>          * vector as empty to avoid warnings later on.
>          */
> -       if (obj_exts == OBJEXTS_ALLOC_FAIL) {
> -               unsigned int i;
> +       if (obj_exts != OBJEXTS_ALLOC_FAIL)
> +               return;
> +
> +       for (unsigned int i = 0; i < slab->objects; i++) {
> +               union codetag_ref *ref = slab_obj_ext_codetag_ref(slab, vec);
>
> -               for (i = 0; i < objects; i++)
> -                       set_codetag_empty(&vec[i].ref);
> +               set_codetag_empty(ref);
> +               vec++;
>         }
>  }
>
> @@ -2112,8 +2117,8 @@ static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
>
>  static inline void mark_obj_codetag_empty(const void *obj) {}
>  static inline bool mark_failed_objexts_alloc(struct slab *slab) { return false; }
> -static inline void handle_failed_objexts_alloc(unsigned long obj_exts,
> -                       struct slabobj_ext *vec, unsigned int objects) {}
> +static inline void handle_failed_objexts_alloc(struct slab *slab,
> +               unsigned long obj_exts, struct slabobj_ext *vec) {}
>
>  #endif /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */
>
> @@ -2181,7 +2186,7 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
>  #endif
>  retry:
>         old_exts = READ_ONCE(slab->obj_exts);
> -       handle_failed_objexts_alloc(old_exts, vec, slab->objects);
> +       handle_failed_objexts_alloc(slab, old_exts, vec);
>
>         if (new_slab) {
>                 /*
> @@ -2361,9 +2366,15 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags,
>          * check should be added before alloc_tag_add().
>          */
>         if (obj_exts) {
> +               union codetag_ref *ref;
> +
>                 get_slab_obj_exts(obj_exts);
> +
>                 obj_ext = slab_obj_ext(s, slab, obj_exts, object);
> -               alloc_tag_add(&obj_ext->ref, current->alloc_tag, s->size);
> +               ref = slab_obj_ext_codetag_ref(slab, obj_ext);
> +
> +               alloc_tag_add(ref, current->alloc_tag, s->size);
> +
>                 put_slab_obj_exts(obj_exts);
>         } else {
>                 alloc_tag_set_inaccurate(current->alloc_tag);
> @@ -2395,10 +2406,13 @@ __alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p
>
>         get_slab_obj_exts(obj_exts);
>         for (int i = 0; i < objects; i++) {
> +               struct slabobj_ext *ext;
> +
>                 if (is_kfence_address(p[i]))
>                         continue;
>
> -               alloc_tag_sub(&slab_obj_ext(s, slab, obj_exts, p[i])->ref, s->size);
> +               ext = slab_obj_ext(s, slab, obj_exts, p[i]);
> +               alloc_tag_sub(slab_obj_ext_codetag_ref(slab, ext), s->size);
>         }
>         put_slab_obj_exts(obj_exts);
>  }
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 07/12] mm/slab: replace slab.stride with obj_exts_in_object
       [not found] ` <20260715-b4-objext_split-v1-7-9a49c4ccf4c3@kernel.org>
@ 2026-07-16  1:44   ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16  1:44 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> The stride field is used to convert object index to an slabobj_ext so
> both compact arrays (kmalloc() or in-slab-leftover) and spread
> in-object-padding obj_ext layouts are supported.
>
> In practice thus the stride is always sizeof(slabobj_ext) or s->size.
>
> This simplifies the calculations, but with the upcoming slabobj_ext
> handling changes, it will be easier to stop storing the stride and
> instead just have a flag whether obj_ext is in the object padding.
> obj_exts_in_object() can then rely on this flag and slab_obj_ext()
> can use that to determine the stride.
>
> No functional change intended. Performance impact TBD, hopefully
> in the noise.
>
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>

Reviewed-by: Suren Baghdasaryan <surenb@google.com>

> ---
>  mm/slab.h | 43 ++++++++++++++++++++++++-------------------
>  mm/slub.c | 42 ++++++++++++++++--------------------------
>  2 files changed, 40 insertions(+), 45 deletions(-)
>
> diff --git a/mm/slab.h b/mm/slab.h
> index e3f8e42070f1..3ad9777ad600 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -81,10 +81,10 @@ struct freelist_counters {
>  #ifdef CONFIG_64BIT
>                                         /*
>                                          * Some optimizations use free bits in 'counters' field
> -                                        * to save memory. In case ->stride field is not available,
> +                                        * to save memory. If these free bits are not available,
>                                          * such optimizations are disabled.
>                                          */
> -                                       unsigned int stride;
> +                                       unsigned obj_exts_in_object:1;
>  #endif
>                                 };
>                         };
> @@ -617,22 +617,20 @@ static inline void put_slab_obj_exts(unsigned long obj_exts)
>  }
>
>  #ifdef CONFIG_64BIT
> -static inline void slab_set_stride(struct slab *slab, unsigned int stride)
> +static inline bool obj_exts_in_object(struct slab *slab)
>  {
> -       slab->stride = stride;
> -}
> -static inline unsigned int slab_get_stride(struct slab *slab)
> -{
> -       return slab->stride;
> +       /*
> +        * Note we cannot rely on the SLAB_OBJ_EXT_IN_OBJ flag here and need to
> +        * check the per-slab bit. A cache can have SLAB_OBJ_EXT_IN_OBJ set, but
> +        * allocations within_slab_leftover are preferred. And those may be
> +        * possible or not depending on the particular slab's size.
> +        */
> +       return slab->obj_exts_in_object;
>  }
>  #else
> -static inline void slab_set_stride(struct slab *slab, unsigned int stride)
> +static inline bool obj_exts_in_object(struct slab *slab)
>  {
> -       VM_WARN_ON_ONCE(stride != sizeof(struct slabobj_ext));
> -}
> -static inline unsigned int slab_get_stride(struct slab *slab)
> -{
> -       return sizeof(struct slabobj_ext);
> +       return false;
>  }
>  #endif
>
> @@ -656,8 +654,14 @@ slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
>         VM_WARN_ON_ONCE(obj_exts != slab_obj_exts(slab));
>
>         index = obj_to_index(s, slab, obj);
> -       obj_ext = (struct slabobj_ext *)(obj_exts +
> -                                        slab_get_stride(slab) * index);
> +
> +       if (!obj_exts_in_object(slab)) {
> +               obj_ext = ((struct slabobj_ext *)obj_exts) + index;
> +       } else {
> +               unsigned int stride = s->size;
> +               obj_ext = (struct slabobj_ext *)(obj_exts + index * stride);
> +       }
> +
>         return kasan_reset_tag(obj_ext);
>  }
>
> @@ -693,9 +697,10 @@ slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
>         return NULL;
>  }
>
> -static inline void slab_set_stride(struct slab *slab, unsigned int stride) { }
> -static inline unsigned int slab_get_stride(struct slab *slab) { return 0; }
> -
> +static inline bool obj_exts_in_object(struct slab *slab)
> +{
> +       return false;
> +}
>
>  #endif /* CONFIG_SLAB_OBJ_EXT */
>
> diff --git a/mm/slub.c b/mm/slub.c
> index 2bfcabc4c51a..98a14e5842a2 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -870,18 +870,6 @@ static inline bool obj_exts_in_slab(struct kmem_cache *s, struct slab *slab)
>  #endif
>
>  #if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT)
> -static bool obj_exts_in_object(struct kmem_cache *s, struct slab *slab)
> -{
> -       /*
> -        * Note we cannot rely on the SLAB_OBJ_EXT_IN_OBJ flag here and need to
> -        * check the stride. A cache can have SLAB_OBJ_EXT_IN_OBJ set, but
> -        * allocations within_slab_leftover are preferred. And those may be
> -        * possible or not depending on the particular slab's size.
> -        */
> -       return obj_exts_in_slab(s, slab) &&
> -              (slab_get_stride(slab) == s->size);
> -}
> -
>  static unsigned int obj_exts_offset_in_object(struct kmem_cache *s)
>  {
>         unsigned int offset = get_info_end(s);
> @@ -896,16 +884,20 @@ static unsigned int obj_exts_offset_in_object(struct kmem_cache *s)
>
>         return offset;
>  }
> -#else
> -static inline bool obj_exts_in_object(struct kmem_cache *s, struct slab *slab)
> +
> +static inline void slab_set_obj_exts_in_object(struct slab *slab)
>  {
> -       return false;
> +       slab->obj_exts_in_object = 1;
>  }
> -
> +#else
>  static inline unsigned int obj_exts_offset_in_object(struct kmem_cache *s)
>  {
>         return 0;
>  }
> +
> +static inline void slab_set_obj_exts_in_object(struct slab *slab)
> +{
> +}
>  #endif
>
>  #ifdef CONFIG_SLUB_DEBUG
> @@ -1206,7 +1198,7 @@ static void print_trailer(struct kmem_cache *s, struct slab *slab, u8 *p)
>
>         off += kasan_metadata_size(s, false);
>
> -       if (obj_exts_in_object(s, slab))
> +       if (obj_exts_in_object(slab))
>                 off += sizeof(struct slabobj_ext);
>
>         if (off != size_from_object(s))
> @@ -1411,7 +1403,7 @@ static int check_pad_bytes(struct kmem_cache *s, struct slab *slab, u8 *p)
>
>         off += kasan_metadata_size(s, false);
>
> -       if (obj_exts_in_object(s, slab))
> +       if (obj_exts_in_object(slab))
>                 off += sizeof(struct slabobj_ext);
>
>         if (size_from_object(s) == off)
> @@ -1439,7 +1431,7 @@ slab_pad_check(struct kmem_cache *s, struct slab *slab)
>         length = slab_size(slab);
>         end = start + length;
>
> -       if (obj_exts_in_slab(s, slab) && !obj_exts_in_object(s, slab)) {
> +       if (obj_exts_in_slab(s, slab) && !obj_exts_in_object(slab)) {
>                 remainder = length;
>                 remainder -= obj_exts_offset_in_slab(s, slab);
>                 remainder -= obj_exts_size_in_slab(slab);
> @@ -2253,9 +2245,6 @@ static void alloc_slab_obj_exts_early(struct kmem_cache *s, struct slab *slab)
>         void *addr;
>         unsigned long obj_exts;
>
> -       /* Initialize stride early to avoid memory ordering issues */
> -       slab_set_stride(slab, sizeof(struct slabobj_ext));
> -
>         if (!need_slab_obj_exts(s))
>                 return;
>
> @@ -2289,7 +2278,7 @@ static void alloc_slab_obj_exts_early(struct kmem_cache *s, struct slab *slab)
>                 obj_exts |= MEMCG_DATA_OBJEXTS;
>  #endif
>                 slab->obj_exts = obj_exts;
> -               slab_set_stride(slab, s->size);
> +               slab_set_obj_exts_in_object(slab);
>         }
>  }
>
> @@ -3402,9 +3391,10 @@ static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags,
>                 stat(s, ORDER_FALLBACK);
>         }
>
> +       /* Initializes frozen, inuse, and any extra 64bit-only flags */
> +       slab->counters = 0;
> +
>         slab->objects = oo_objects(oo);
> -       slab->inuse = 0;
> -       slab->frozen = 0;
>
>         slab->slab_cache = s;
>
> @@ -6537,7 +6527,7 @@ static inline size_t slab_ksize(struct slab *slab)
>          */
>         if (s->flags & (SLAB_TYPESAFE_BY_RCU | SLAB_STORE_USER))
>                 return s->inuse;
> -       else if (obj_exts_in_object(s, slab))
> +       else if (obj_exts_in_object(slab))
>                 return s->inuse;
>         /*
>          * Else we can use all the padding etc for the allocation
>
> --
> 2.55.0
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 05/12] mm/slab: abstract slabobj_ext.objcg access
  2026-07-16  1:27   ` [PATCH RFC 05/12] mm/slab: abstract slabobj_ext.objcg access Suren Baghdasaryan
@ 2026-07-16 13:58     ` Vlastimil Babka (SUSE)
  2026-07-16 15:27       ` Suren Baghdasaryan
  0 siblings, 1 reply; 16+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-07-16 13:58 UTC (permalink / raw)
  To: Suren Baghdasaryan
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On 7/16/26 03:27, Suren Baghdasaryan wrote:
> On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
> <vbabka@kernel.org> wrote:
>> --- a/mm/slab.h
>> +++ b/mm/slab.h
>> @@ -555,7 +555,7 @@ static inline bool need_kmalloc_no_objext(void)
>>   */
>>  struct slabobj_ext {
>>  #ifdef CONFIG_MEMCG
>> -       struct obj_cgroup *objcg;
>> +       struct obj_cgroup *_objcg;
>>  #endif
>>  #ifdef CONFIG_MEM_ALLOC_PROFILING
>>         union codetag_ref ref;
>> @@ -661,6 +661,13 @@ slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
>>         return kasan_reset_tag(obj_ext);
>>  }
>>
>> +#ifdef CONFIG_MEMCG
>> +static inline struct obj_cgroup **slab_obj_ext_objcgp(struct slabobj_ext *obj_ext)
> 
> I understand why you need to return obj_cgroup** but maybe instead we
> can have a separate setter function and just return a pointer here?

Yeah I rewrote it now and it looks better. Thanks! It ended up like this due
to some previous attempt I made.

> That would also help catch cases when someone wants to set
> obj_ext._objcg that was optimized away (for that the setter would need
> an additional parameter to identify whether we allocated optimized
> objext vector).

It would need passing the slab parameter to assert
slab_obj_ext_has_codetag() in debug mode. I don't think it's worth it given
the get/set is only called from few specialized places.

> 
>> +{
>> +       return &obj_ext->_objcg;
>> +}
>> +#endif
>> +
>>  int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
>>                         gfp_t gfp, unsigned int alloc_flags);
>>
>> diff --git a/mm/slub.c b/mm/slub.c
>> index 5e3f53bcd0d3..48e10198a3ce 100644
>> --- a/mm/slub.c
>> +++ b/mm/slub.c
>> @@ -2523,7 +2523,7 @@ bool memcg_slab_post_charge(void *p, gfp_t flags)
>>         if (obj_exts) {
>>                 get_slab_obj_exts(obj_exts);
>>                 obj_ext = slab_obj_ext(s, slab, obj_exts, p);
>> -               if (unlikely(obj_ext->objcg)) {
>> +               if (unlikely(*slab_obj_ext_objcgp(obj_ext))) {
>>                         put_slab_obj_exts(obj_exts);
>>                         return true;
>>                 }
>>
>> --
>> 2.55.0
>>



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 08/12] mm/slab: change struct slabobj_ext to a union
       [not found]   ` <CAJuCfpGq3Yv8Dq=2mPgR5LGC=40VSNTA6mg4BjTRE0ADXMZkbg@mail.gmail.com>
@ 2026-07-16 14:43     ` Vlastimil Babka (SUSE)
  2026-07-16 15:28       ` Suren Baghdasaryan
  0 siblings, 1 reply; 16+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-07-16 14:43 UTC (permalink / raw)
  To: Suren Baghdasaryan
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On 7/16/26 06:11, Suren Baghdasaryan wrote:
> On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
> <vbabka@kernel.org> wrote:
>> No functional change intended, the layout is still effectively static.
>>
>> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> 
> This is much cleaner than what I was preparing. Nicely done!
> 
> Reviewed-by: Suren Baghdasaryan <surenb@google.com>

Thanks!

>> ---
>>  mm/slab.h | 41 +++++++++++++++++++++++++++++++++--------
>>  mm/slub.c | 17 +++++++++--------
>>  2 files changed, 42 insertions(+), 16 deletions(-)
>>
>> diff --git a/mm/slab.h b/mm/slab.h
>> index 3ad9777ad600..359ab8caf61e 100644
>> --- a/mm/slab.h
>> +++ b/mm/slab.h
>> @@ -554,14 +554,34 @@ static inline bool need_kmalloc_no_objext(void)
>>   * if MEMCG_DATA_OBJEXTS is set.
>>   */
>>  struct slabobj_ext {
> 
> Perhaps we should add a comment here stating that every element of
> this union should be pointer-sized?
> 

OK, how about this?

--- a/mm/slab.h
+++ b/mm/slab.h
@@ -550,10 +550,14 @@ static inline bool need_kmalloc_no_objext(void)
 }
 
 /*
- * Extended information for slab objects stored as an array in page->memcg_data
- * if MEMCG_DATA_OBJEXTS is set.
+ * Extended information for slab objects stored as a pointer to an array in
+ * slab->obj_exts (aliasing page->memcg_data) if MEMCG_DATA_OBJEXTS is set.
  */
 struct slabobj_ext {
+       /*
+        * All elements of the union should be pointer-sized to avoid memory
+        * waste
+        */
        union {
 #ifdef CONFIG_MEMCG
                struct obj_cgroup *_objcg;



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 09/12] mm/slab: introduce slab_obj_ext_has_codetag()
       [not found]   ` <CAJuCfpGkpe=5O_vW9VKqKFxrBO+QquCD+mXth_qeUSqcWcW6hA@mail.gmail.com>
@ 2026-07-16 14:47     ` Vlastimil Babka (SUSE)
  0 siblings, 0 replies; 16+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-07-16 14:47 UTC (permalink / raw)
  To: Suren Baghdasaryan
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On 7/16/26 06:25, Suren Baghdasaryan wrote:
> On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
> <vbabka@kernel.org> wrote:
>>
>> mem_alloc_profiling_enabled() allows evaluating (with a static key) if
>> memory profiling is currently enabled. mem_profiling_support is a
>> variable where false means it's not possible to enable it anymore,
>> because the system was booted with "never" or it was later shut down.
>> This is possible to query by mem_alloc_profiling_permanently_disabled().
>>
>> To make slabobj_ext array size handling dynamic, we need a snapshot of
>> mem_alloc_profiling_permanently_disabled() early in boot, so that's not
>> affected by a later shutdown. We also need it to be static key based for
>> performance. Neither mem_alloc_profiling_enabled() nor
>> mem_alloc_profiling_permanently_disabled() satisfy this.
>>
>> Therefore introduce slab_obj_ext_has_codetag() with an underlying static
>> key for that use case. Its state is made to reflect the result of
>> mem_alloc_profiling_permanently_disabled() during kmem_cache_init(),
>> which does happen after setup_early_mem_profiling().
>>
>> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> 
> Reviewed-by: Suren Baghdasaryan <surenb@google.com>

Thanks!

>> +/*
>> + * Make sure the static key used by slab_obj_ext_has_codetag() reflects the
>> + * value of !mem_alloc_profiling_permanently_disabled()
>> + *
>> + * Any later mem alloc profiling shutdown won't be reflected in the static key
>> + * because obj_exts with codetags might already exist.
>> + */
>> +static void __init slab_obj_ext_has_codetag_init(void)
>> +{
>> +       bool key_enabled = IS_ENABLED(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT);
> 
> nit: This seems a bit indirect. Reader needs to remember that
> slab_obj_ext_has_codetag_key is initialized based on
> CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT. Wouldn't below code be
> simpler?:
> 
> bool need_codetag = !mem_alloc_profiling_permanently_disabled();
> if (need_codetag != static_key_enabled(&slab_obj_ext_has_codetag_key)) {
>         if (need_codetag)
>                 static_branch_enable(&slab_obj_ext_has_codetag_key);
>         else
>                 static_branch_disable(&slab_obj_ext_has_codetag_key);
> }
> 
> It might be less performant but who cares, it's __init function used
> only one time.

Good idea, will do!




^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 12/12] mm/slab: stop allocating objcg pointers when unnecessary
       [not found]   ` <CAJuCfpHuXu3BDf93zeBQWSW_k5wApegp44rHoZwk5Ym6xxbqEA@mail.gmail.com>
@ 2026-07-16 15:08     ` Vlastimil Babka (SUSE)
  2026-07-16 15:32       ` Suren Baghdasaryan
  0 siblings, 1 reply; 16+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-07-16 15:08 UTC (permalink / raw)
  To: Suren Baghdasaryan
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On 7/16/26 06:46, Suren Baghdasaryan wrote:
> Maybe now we should rename static_obj_ext_size() to
> static_obj_ext_max_size() as it reflects the max possible size of
> slabobj_ext?

Hm with _max_size() it doesn't have to be called static_ anymore?

But maybe we can get rid of it completely.

The only caller is calculate_sizes():

#if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT)
        if (slab_args_unmergeable(args, s->flags) &&
                        (aligned_size - size >= static_obj_ext_size()))
                s->flags |= SLAB_OBJ_EXT_IN_OBJ;
#endif

We don't have slab pointer to pass to static_obj_ext_max_size().
But we should be able to figure it out from the cache via
cache_needs_objcg().

So we'd need cache_obj_ext_size()?

The static max size here works, but may prevent SLAB_OBJ_EXT_IN_OBJ
needlessly if padding can only fit codetag_ref and not objcg, but
we don't need objcg.
Which is probably only theoretical at this point as kmalloc_normal
caches (that have no objcg) have no padding, at least without
slab_debug. But still.

>> +       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
>> @@ -741,7 +749,7 @@ static inline struct obj_cgroup **slab_obj_ext_objcgp(struct slabobj_ext *obj_ex
>>  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 771d73abacb6..09c4931e5435 100644
>> --- a/mm/slub.c
>> +++ b/mm/slub.c
>> @@ -2503,6 +2503,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
>>



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 01/12] mm/slab: skip kfence objects in allocation profiling
       [not found]     ` <ab9782bf-d3dd-4cb5-ae66-127b3a16330b@kernel.org>
@ 2026-07-16 15:24       ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16 15:24 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Thu, Jul 16, 2026 at 2:11 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> On 7/15/26 18:02, Suren Baghdasaryan wrote:
> > On Wed, Jul 15, 2026 at 3:10 AM Vlastimil Babka (SUSE)
> > <vbabka@kernel.org> wrote:
> >>
> >> struct kfence_metadata only has obj_exts with CONFIG_MEMCG.
> >
> > I don't quite understand this statement. obj_exts are allocated when
> > either CONFIG_MEMCG or CONFIG_MEM_ALLOC_PROFILING is enabled.
>
> See in mm/kfence/kfence.h
>
> struct kfence_metadata {
> ...
> #ifdef CONFIG_MEMCG
>         struct slabobj_ext obj_exts;
> #endif
> ...
>
> then in mm/kfence/core.c kfence_init_pool()
>
> #ifdef CONFIG_MEMCG
>                 struct slab *slab = page_slab(page);
>                 slab->obj_exts = (unsigned long)&kfence_metadata_init[i / 2 - 1].obj_exts |
>                                  MEMCG_DATA_OBJEXTS;
> #endif
>
> So the slab kfence fakes only has this pre-inited obj_exts
> with CONFIG_MEMCG.
>
> What happens if we run memalloc profiling without MEMCG and this
> fake slab doesn't have a pre-assigned obj_exts? I guess
> __alloc_tagging_slab_alloc_hook() will allocate it via
> prepare_slab_obj_exts_hook().
>
> But it's something that was done consciously and probably
> just accidentally works.

Ok, I see. Maybe you can expand this description to explain the
details more thoroughly? This whole kfence.obj_exts deal is not very
intuitive.

>
> >> If it's
> >> enabled, it does also work for allocation profiling, but there's little
> >> value recording tags for KFENCE objects.
> >
> > Unless we are leaking them, right?
>
> Well if there are leaks in a particular callsite, we should see that from all
> the allocations that don't end up in kfence (as kfence allocations are rare).
> So we are very unlikely to miss a leak due to this.

Ok, makes sense. Thanks for the explanation!

>
> >> Furthermore it would complicate
> >> the upcoming changes, so just skip them in the slab hooks.
> >
> > Ok, I can understand that. If we do this, we should document that
> > kfence objects are no longer tracked.
> >
> >>
> >> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> >> ---
> >>  mm/slub.c | 6 ++++++
> >>  1 file changed, 6 insertions(+)
> >>
> >> diff --git a/mm/slub.c b/mm/slub.c
> >> index 0337e60db5ac..a4be70d080fb 100644
> >> --- a/mm/slub.c
> >> +++ b/mm/slub.c
> >> @@ -2352,6 +2352,9 @@ __alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags,
> >>         if (alloc_flags & SLAB_ALLOC_NO_RECURSE)
> >>                 return;
> >>
> >> +       if (is_kfence_address(object))
> >> +               return;
> >> +
> >>         slab = virt_to_slab(object);
> >>         obj_exts = prepare_slab_obj_exts_hook(s, slab, flags, alloc_flags, object);
> >>         /*
> >> @@ -2399,6 +2402,9 @@ __alloc_tagging_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p
> >>         for (i = 0; i < objects; i++) {
> >>                 unsigned int off = obj_to_index(s, slab, p[i]);
> >>
> >> +               if (is_kfence_address(p[i]))
> >> +                       continue;
> >> +
> >>                 alloc_tag_sub(&slab_obj_ext(slab, obj_exts, off)->ref, s->size);
> >>         }
> >>         put_slab_obj_exts(obj_exts);
> >>
> >> --
> >> 2.55.0
> >>
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 05/12] mm/slab: abstract slabobj_ext.objcg access
  2026-07-16 13:58     ` Vlastimil Babka (SUSE)
@ 2026-07-16 15:27       ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16 15:27 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Thu, Jul 16, 2026 at 6:58 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> On 7/16/26 03:27, Suren Baghdasaryan wrote:
> > On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
> > <vbabka@kernel.org> wrote:
> >> --- a/mm/slab.h
> >> +++ b/mm/slab.h
> >> @@ -555,7 +555,7 @@ static inline bool need_kmalloc_no_objext(void)
> >>   */
> >>  struct slabobj_ext {
> >>  #ifdef CONFIG_MEMCG
> >> -       struct obj_cgroup *objcg;
> >> +       struct obj_cgroup *_objcg;
> >>  #endif
> >>  #ifdef CONFIG_MEM_ALLOC_PROFILING
> >>         union codetag_ref ref;
> >> @@ -661,6 +661,13 @@ slab_obj_ext(struct kmem_cache *s, struct slab *slab, unsigned long obj_exts,
> >>         return kasan_reset_tag(obj_ext);
> >>  }
> >>
> >> +#ifdef CONFIG_MEMCG
> >> +static inline struct obj_cgroup **slab_obj_ext_objcgp(struct slabobj_ext *obj_ext)
> >
> > I understand why you need to return obj_cgroup** but maybe instead we
> > can have a separate setter function and just return a pointer here?
>
> Yeah I rewrote it now and it looks better. Thanks! It ended up like this due
> to some previous attempt I made.
>
> > That would also help catch cases when someone wants to set
> > obj_ext._objcg that was optimized away (for that the setter would need
> > an additional parameter to identify whether we allocated optimized
> > objext vector).
>
> It would need passing the slab parameter to assert
> slab_obj_ext_has_codetag() in debug mode. I don't think it's worth it given
> the get/set is only called from few specialized places.

Sounds good.

>
> >
> >> +{
> >> +       return &obj_ext->_objcg;
> >> +}
> >> +#endif
> >> +
> >>  int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s,
> >>                         gfp_t gfp, unsigned int alloc_flags);
> >>
> >> diff --git a/mm/slub.c b/mm/slub.c
> >> index 5e3f53bcd0d3..48e10198a3ce 100644
> >> --- a/mm/slub.c
> >> +++ b/mm/slub.c
> >> @@ -2523,7 +2523,7 @@ bool memcg_slab_post_charge(void *p, gfp_t flags)
> >>         if (obj_exts) {
> >>                 get_slab_obj_exts(obj_exts);
> >>                 obj_ext = slab_obj_ext(s, slab, obj_exts, p);
> >> -               if (unlikely(obj_ext->objcg)) {
> >> +               if (unlikely(*slab_obj_ext_objcgp(obj_ext))) {
> >>                         put_slab_obj_exts(obj_exts);
> >>                         return true;
> >>                 }
> >>
> >> --
> >> 2.55.0
> >>
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 08/12] mm/slab: change struct slabobj_ext to a union
  2026-07-16 14:43     ` [PATCH RFC 08/12] mm/slab: change struct slabobj_ext to a union Vlastimil Babka (SUSE)
@ 2026-07-16 15:28       ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16 15:28 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Thu, Jul 16, 2026 at 7:43 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> On 7/16/26 06:11, Suren Baghdasaryan wrote:
> > On Wed, Jul 15, 2026 at 3:11 AM Vlastimil Babka (SUSE)
> > <vbabka@kernel.org> wrote:
> >> No functional change intended, the layout is still effectively static.
> >>
> >> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> >
> > This is much cleaner than what I was preparing. Nicely done!
> >
> > Reviewed-by: Suren Baghdasaryan <surenb@google.com>
>
> Thanks!
>
> >> ---
> >>  mm/slab.h | 41 +++++++++++++++++++++++++++++++++--------
> >>  mm/slub.c | 17 +++++++++--------
> >>  2 files changed, 42 insertions(+), 16 deletions(-)
> >>
> >> diff --git a/mm/slab.h b/mm/slab.h
> >> index 3ad9777ad600..359ab8caf61e 100644
> >> --- a/mm/slab.h
> >> +++ b/mm/slab.h
> >> @@ -554,14 +554,34 @@ static inline bool need_kmalloc_no_objext(void)
> >>   * if MEMCG_DATA_OBJEXTS is set.
> >>   */
> >>  struct slabobj_ext {
> >
> > Perhaps we should add a comment here stating that every element of
> > this union should be pointer-sized?
> >
>
> OK, how about this?
>
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -550,10 +550,14 @@ static inline bool need_kmalloc_no_objext(void)
>  }
>
>  /*
> - * Extended information for slab objects stored as an array in page->memcg_data
> - * if MEMCG_DATA_OBJEXTS is set.
> + * Extended information for slab objects stored as a pointer to an array in
> + * slab->obj_exts (aliasing page->memcg_data) if MEMCG_DATA_OBJEXTS is set.
>   */
>  struct slabobj_ext {
> +       /*
> +        * All elements of the union should be pointer-sized to avoid memory
> +        * waste
> +        */

Perfect. Thank you!

>         union {
>  #ifdef CONFIG_MEMCG
>                 struct obj_cgroup *_objcg;
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH RFC 12/12] mm/slab: stop allocating objcg pointers when unnecessary
  2026-07-16 15:08     ` [PATCH RFC 12/12] mm/slab: stop allocating objcg pointers when unnecessary Vlastimil Babka (SUSE)
@ 2026-07-16 15:32       ` Suren Baghdasaryan
  0 siblings, 0 replies; 16+ messages in thread
From: Suren Baghdasaryan @ 2026-07-16 15:32 UTC (permalink / raw)
  To: Vlastimil Babka (SUSE)
  Cc: Harry Yoo, Hao Li, Shakeel Butt, Alexander Potapenko, Marco Elver,
	Andrew Morton, Christoph Lameter, David Rientjes, Roman Gushchin,
	linux-mm, linux-kernel, cgroups

On Thu, Jul 16, 2026 at 8:08 AM Vlastimil Babka (SUSE)
<vbabka@kernel.org> wrote:
>
> On 7/16/26 06:46, Suren Baghdasaryan wrote:
> > Maybe now we should rename static_obj_ext_size() to
> > static_obj_ext_max_size() as it reflects the max possible size of
> > slabobj_ext?
>
> Hm with _max_size() it doesn't have to be called static_ anymore?

Yeah, static_ is an implementation detail anyway.

>
> But maybe we can get rid of it completely.
>
> The only caller is calculate_sizes():

I was thinking the same but either way sounds fine to me. Keeping
obj_ext_max_size() would be a bit more self-documenting I think.

>
> #if defined(CONFIG_SLAB_OBJ_EXT) && defined(CONFIG_64BIT)
>         if (slab_args_unmergeable(args, s->flags) &&
>                         (aligned_size - size >= static_obj_ext_size()))
>                 s->flags |= SLAB_OBJ_EXT_IN_OBJ;
> #endif
>
> We don't have slab pointer to pass to static_obj_ext_max_size().
> But we should be able to figure it out from the cache via
> cache_needs_objcg().
>
> So we'd need cache_obj_ext_size()?
>
> The static max size here works, but may prevent SLAB_OBJ_EXT_IN_OBJ
> needlessly if padding can only fit codetag_ref and not objcg, but
> we don't need objcg.
> Which is probably only theoretical at this point as kmalloc_normal
> caches (that have no objcg) have no padding, at least without
> slab_debug. But still.

Maybe keep it simple as is for now and then we can optimize it further?

>
> >> +       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
> >> @@ -741,7 +749,7 @@ static inline struct obj_cgroup **slab_obj_ext_objcgp(struct slabobj_ext *obj_ex
> >>  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 771d73abacb6..09c4931e5435 100644
> >> --- a/mm/slub.c
> >> +++ b/mm/slub.c
> >> @@ -2503,6 +2503,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
> >>
>


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-07-16 15:33 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260715-b4-objext_split-v1-0-9a49c4ccf4c3@kernel.org>
2026-07-15 15:32 ` [PATCH RFC 00/12] mm/slab, alloc_tag: reduce obj_ext memory waste Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-1-9a49c4ccf4c3@kernel.org>
2026-07-15 16:02   ` [PATCH RFC 01/12] mm/slab: skip kfence objects in allocation profiling Suren Baghdasaryan
     [not found]     ` <ab9782bf-d3dd-4cb5-ae66-127b3a16330b@kernel.org>
2026-07-16 15:24       ` Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-2-9a49c4ccf4c3@kernel.org>
2026-07-15 16:13   ` [PATCH RFC 02/12] mm/slab: remove objs_per_slab() Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-3-9a49c4ccf4c3@kernel.org>
2026-07-16  0:56   ` [PATCH RFC 03/12] mm: move struct slabobj_ext to mm/slab.h Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-4-9a49c4ccf4c3@kernel.org>
2026-07-16  1:02   ` [PATCH RFC 04/12] mm/slab: make slab_obj_ext() determine object index Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-5-9a49c4ccf4c3@kernel.org>
2026-07-16  1:27   ` [PATCH RFC 05/12] mm/slab: abstract slabobj_ext.objcg access Suren Baghdasaryan
2026-07-16 13:58     ` Vlastimil Babka (SUSE)
2026-07-16 15:27       ` Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-6-9a49c4ccf4c3@kernel.org>
2026-07-16  1:28   ` [PATCH RFC 06/12] mm/slab: abstract slabobj_ext.ref access Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-7-9a49c4ccf4c3@kernel.org>
2026-07-16  1:44   ` [PATCH RFC 07/12] mm/slab: replace slab.stride with obj_exts_in_object Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-8-9a49c4ccf4c3@kernel.org>
     [not found]   ` <CAJuCfpGq3Yv8Dq=2mPgR5LGC=40VSNTA6mg4BjTRE0ADXMZkbg@mail.gmail.com>
2026-07-16 14:43     ` [PATCH RFC 08/12] mm/slab: change struct slabobj_ext to a union Vlastimil Babka (SUSE)
2026-07-16 15:28       ` Suren Baghdasaryan
     [not found] ` <20260715-b4-objext_split-v1-9-9a49c4ccf4c3@kernel.org>
     [not found]   ` <CAJuCfpGkpe=5O_vW9VKqKFxrBO+QquCD+mXth_qeUSqcWcW6hA@mail.gmail.com>
2026-07-16 14:47     ` [PATCH RFC 09/12] mm/slab: introduce slab_obj_ext_has_codetag() Vlastimil Babka (SUSE)
     [not found] ` <20260715-b4-objext_split-v1-12-9a49c4ccf4c3@kernel.org>
     [not found]   ` <CAJuCfpHuXu3BDf93zeBQWSW_k5wApegp44rHoZwk5Ym6xxbqEA@mail.gmail.com>
2026-07-16 15:08     ` [PATCH RFC 12/12] mm/slab: stop allocating objcg pointers when unnecessary Vlastimil Babka (SUSE)
2026-07-16 15:32       ` Suren Baghdasaryan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox