On Mon, Feb 12, 2024 at 01:39:20PM -0800, Suren Baghdasaryan wrote: > If slabobj_ext vector allocation for a slab object fails and later on it > succeeds for another object in the same slab, the slabobj_ext for the > original object will be NULL and will be flagged in case when > CONFIG_MEM_ALLOC_PROFILING_DEBUG is enabled. > Mark failed slabobj_ext vector allocations using a new objext_flags flag > stored in the lower bits of slab->obj_exts. When new allocation succeeds > it marks all tag references in the same slabobj_ext vector as empty to > avoid warnings implemented by CONFIG_MEM_ALLOC_PROFILING_DEBUG checks. > > Signed-off-by: Suren Baghdasaryan <surenb@xxxxxxxxxx> > --- > include/linux/memcontrol.h | 4 +++- > mm/slab.h | 25 +++++++++++++++++++++++++ > mm/slab_common.c | 22 +++++++++++++++------- > 3 files changed, 43 insertions(+), 8 deletions(-) > > diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h > index 2b010316016c..f95241ca9052 100644 > --- a/include/linux/memcontrol.h > +++ b/include/linux/memcontrol.h > @@ -365,8 +365,10 @@ enum page_memcg_data_flags { > #endif /* CONFIG_MEMCG */ > > enum objext_flags { > + /* slabobj_ext vector failed to allocate */ > + OBJEXTS_ALLOC_FAIL = __FIRST_OBJEXT_FLAG, > /* the next bit after the last actual flag */ > - __NR_OBJEXTS_FLAGS = __FIRST_OBJEXT_FLAG, > + __NR_OBJEXTS_FLAGS = (__FIRST_OBJEXT_FLAG << 1), > }; > > #define OBJEXTS_FLAGS_MASK (__NR_OBJEXTS_FLAGS - 1) > diff --git a/mm/slab.h b/mm/slab.h > index cf332a839bf4..7bb3900f83ef 100644 > --- a/mm/slab.h > +++ b/mm/slab.h > @@ -586,9 +586,34 @@ static inline void mark_objexts_empty(struct slabobj_ext *obj_exts) > } > } > > +static inline void mark_failed_objexts_alloc(struct slab *slab) > +{ > + slab->obj_exts = OBJEXTS_ALLOC_FAIL; Uh, does this mean slab->obj_exts is suddenly non-NULL? Is everything that accesses obj_exts expecting this? -Kees > +} > + > +static inline void handle_failed_objexts_alloc(unsigned long obj_exts, > + struct slabobj_ext *vec, unsigned int objects) > +{ > + /* > + * 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; > + > + for (i = 0; i < objects; i++) > + set_codetag_empty(&vec[i].ref); > + } > +} > + > + > #else /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */ > > static inline void mark_objexts_empty(struct slabobj_ext *obj_exts) {} > +static inline void mark_failed_objexts_alloc(struct slab *slab) {} > +static inline void handle_failed_objexts_alloc(unsigned long obj_exts, > + struct slabobj_ext *vec, unsigned int objects) {} > > #endif /* CONFIG_MEM_ALLOC_PROFILING_DEBUG */ > > diff --git a/mm/slab_common.c b/mm/slab_common.c > index d5f75d04ced2..489c7a8ba8f1 100644 > --- a/mm/slab_common.c > +++ b/mm/slab_common.c > @@ -214,29 +214,37 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, > gfp_t gfp, bool new_slab) > { > unsigned int objects = objs_per_slab(s, slab); > - unsigned long obj_exts; > - void *vec; > + unsigned long new_exts; > + unsigned long old_exts; > + struct slabobj_ext *vec; > > gfp &= ~OBJCGS_CLEAR_MASK; > /* Prevent recursive extension vector allocation */ > gfp |= __GFP_NO_OBJ_EXT; > vec = kcalloc_node(objects, sizeof(struct slabobj_ext), gfp, > slab_nid(slab)); > - if (!vec) > + if (!vec) { > + /* Mark vectors which failed to allocate */ > + if (new_slab) > + mark_failed_objexts_alloc(slab); > + > return -ENOMEM; > + } > > - obj_exts = (unsigned long)vec; > + new_exts = (unsigned long)vec; > #ifdef CONFIG_MEMCG > - obj_exts |= MEMCG_DATA_OBJEXTS; > + new_exts |= MEMCG_DATA_OBJEXTS; > #endif > + old_exts = slab->obj_exts; > + handle_failed_objexts_alloc(old_exts, vec, objects); > if (new_slab) { > /* > * If the slab is brand new and nobody can yet access its > * obj_exts, no synchronization is required and obj_exts can > * be simply assigned. > */ > - slab->obj_exts = obj_exts; > - } else if (cmpxchg(&slab->obj_exts, 0, obj_exts)) { > + slab->obj_exts = new_exts; > + } else if (cmpxchg(&slab->obj_exts, old_exts, new_exts) != old_exts) { > /* > * If the slab is already in use, somebody can allocate and > * assign slabobj_exts in parallel. In this case the existing > -- > 2.43.0.687.g38aa6559b0-goog > -- Kees Cook