On Fri, Feb 14, 2025 at 8:27 AM Vlastimil Babka <vbabka@xxxxxxx> wrote: > > From: "Liam R. Howlett" <Liam.Howlett@xxxxxxxxxx> > > Make testing work for the slab and rcu changes that have come in with > the sheaves work. > > This only works with one kmem_cache, and only the first one used. > Subsequent setting of keme_cache will not update the active kmem_cache s/keme_cache/kmem_cache > and will be silently dropped because there are other tests which happen > after the kmem_cache of interest is set. > > The saved active kmem_cache is used in the rcu callback, which passes > the object to be freed. > > The rcu call takes the rcu_head, which is passed in as the field in the > struct (in this case rcu in the maple tree node), which is calculated by > pointer math. The offset of which is saved (in a global variable) for > restoring the node pointer on the callback after the rcu grace period > expires. > > Don't use any of this outside of testing, please. > > Signed-off-by: Liam R. Howlett <Liam.Howlett@xxxxxxxxxx> > --- > tools/include/linux/slab.h | 41 ++++++++++++++++++++++++++++++++--- > tools/testing/shared/linux.c | 24 ++++++++++++++++---- > tools/testing/shared/linux/rcupdate.h | 22 +++++++++++++++++++ > 3 files changed, 80 insertions(+), 7 deletions(-) > > diff --git a/tools/include/linux/slab.h b/tools/include/linux/slab.h > index 51b25e9c4ec7b66bdf4c68cc1353c6faf1ca7bb8..a475364cfd9fcdb10db252aab18ea3a620326b6b 100644 > --- a/tools/include/linux/slab.h > +++ b/tools/include/linux/slab.h > @@ -22,6 +22,12 @@ enum slab_state { > FULL > }; > > +struct kmem_cache_args { > + unsigned int align; > + unsigned int sheaf_capacity; > + void (*ctor)(void *); > +}; > + > static inline void *kzalloc(size_t size, gfp_t gfp) > { > return kmalloc(size, gfp | __GFP_ZERO); > @@ -36,9 +42,38 @@ static inline void *kmem_cache_alloc(struct kmem_cache *cachep, int flags) > } > void kmem_cache_free(struct kmem_cache *cachep, void *objp); > > -struct kmem_cache *kmem_cache_create(const char *name, unsigned int size, > - unsigned int align, unsigned int flags, > - void (*ctor)(void *)); > + > +struct kmem_cache * > +__kmem_cache_create_args(const char *name, unsigned int size, > + struct kmem_cache_args *args, unsigned int flags); > + > +/* If NULL is passed for @args, use this variant with default arguments. */ > +static inline struct kmem_cache * > +__kmem_cache_default_args(const char *name, unsigned int size, > + struct kmem_cache_args *args, unsigned int flags) > +{ > + struct kmem_cache_args kmem_default_args = {}; > + > + return __kmem_cache_create_args(name, size, &kmem_default_args, flags); > +} > + > +static inline struct kmem_cache * > +__kmem_cache_create(const char *name, unsigned int size, unsigned int align, > + unsigned int flags, void (*ctor)(void *)) > +{ > + struct kmem_cache_args kmem_args = { > + .align = align, > + .ctor = ctor, > + }; > + > + return __kmem_cache_create_args(name, size, &kmem_args, flags); > +} > + > +#define kmem_cache_create(__name, __object_size, __args, ...) \ > + _Generic((__args), \ > + struct kmem_cache_args *: __kmem_cache_create_args, \ > + void *: __kmem_cache_default_args, \ > + default: __kmem_cache_create)(__name, __object_size, __args, __VA_ARGS__) > > void kmem_cache_free_bulk(struct kmem_cache *cachep, size_t size, void **list); > int kmem_cache_alloc_bulk(struct kmem_cache *cachep, gfp_t gfp, size_t size, > diff --git a/tools/testing/shared/linux.c b/tools/testing/shared/linux.c > index 66dbb362385f3c3d923233448cc591adfe6dc9e7..9f5fd722f27f1d3877be8927be30409cd74ab3c3 100644 > --- a/tools/testing/shared/linux.c > +++ b/tools/testing/shared/linux.c > @@ -20,6 +20,7 @@ struct kmem_cache { > pthread_mutex_t lock; > unsigned int size; > unsigned int align; > + unsigned int sheaf_capacity; > int nr_objs; > void *objs; > void (*ctor)(void *); > @@ -31,6 +32,8 @@ struct kmem_cache { > void *private; > }; > > +static struct kmem_cache *kmem_active = NULL; > + > void kmem_cache_set_callback(struct kmem_cache *cachep, void (*callback)(void *)) > { > cachep->callback = callback; > @@ -147,6 +150,14 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) > pthread_mutex_unlock(&cachep->lock); > } > > +void kmem_cache_free_active(void *objp) > +{ > + if (!kmem_active) > + printf("WARNING: No active kmem_cache\n"); > + > + kmem_cache_free(kmem_active, objp); > +} > + > void kmem_cache_free_bulk(struct kmem_cache *cachep, size_t size, void **list) > { > if (kmalloc_verbose) > @@ -234,23 +245,28 @@ int kmem_cache_alloc_bulk(struct kmem_cache *cachep, gfp_t gfp, size_t size, > } > > struct kmem_cache * > -kmem_cache_create(const char *name, unsigned int size, unsigned int align, > - unsigned int flags, void (*ctor)(void *)) > +__kmem_cache_create_args(const char *name, unsigned int size, > + struct kmem_cache_args *args, > + unsigned int flags) > { > struct kmem_cache *ret = malloc(sizeof(*ret)); > > pthread_mutex_init(&ret->lock, NULL); > ret->size = size; > - ret->align = align; > + ret->align = args->align; > + ret->sheaf_capacity = args->sheaf_capacity; > ret->nr_objs = 0; > ret->nr_allocated = 0; > ret->nr_tallocated = 0; > ret->objs = NULL; > - ret->ctor = ctor; > + ret->ctor = args->ctor; > ret->non_kernel = 0; > ret->exec_callback = false; > ret->callback = NULL; > ret->private = NULL; > + if (!kmem_active) > + kmem_active = ret; This kmem_active and kfree_cb_offset look like bad hacks... Could we maybe modify kmem_cache_alloc() to allocate a small metadata at the beginning to store a pointer to kmem_cache and kfree_cb_offset value? > + > return ret; > } > > diff --git a/tools/testing/shared/linux/rcupdate.h b/tools/testing/shared/linux/rcupdate.h > index fed468fb0c78db6f33fb1900c7110ab5f3c19c65..c95e2f0bbd93798e544d7d34e0823ed68414f924 100644 > --- a/tools/testing/shared/linux/rcupdate.h > +++ b/tools/testing/shared/linux/rcupdate.h > @@ -9,4 +9,26 @@ > #define rcu_dereference_check(p, cond) rcu_dereference(p) > #define RCU_INIT_POINTER(p, v) do { (p) = (v); } while (0) > > +void kmem_cache_free_active(void *objp); > +static unsigned long kfree_cb_offset = 0; > + > +static inline void kfree_rcu_cb(struct rcu_head *head) > +{ > + void *objp = (void *) ((unsigned long)head - kfree_cb_offset); > + > + kmem_cache_free_active(objp); > +} > + > +#ifndef offsetof > +#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) > +#endif > + > +#define kfree_rcu(ptr, rhv) \ > +do { \ > + if (!kfree_cb_offset) \ > + kfree_cb_offset = offsetof(typeof(*(ptr)), rhv); \ > + \ > + call_rcu(&ptr->rhv, kfree_rcu_cb); \ > +} while (0) > + > #endif > > -- > 2.48.1 >