Re: [PATCH v10 1/5] lib: objpool added: ring-array based lockless MPMC
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Date: 2023-10-15 23:27:13
Also in:
lkml
On Mon, 16 Oct 2023 00:06:11 +0800 "wuqiang.matt" [off-list ref] wrote:
On 2023/10/15 23:43, Masami Hiramatsu (Google) wrote:quoted
On Sun, 15 Oct 2023 13:32:47 +0800 "wuqiang.matt" [off-list ref] wrote:quoted
objpool is a scalable implementation of high performance queue for object allocation and reclamation, such as kretprobe instances. With leveraging percpu ring-array to mitigate hot spots of memory contention, it delivers near-linear scalability for high parallel scenarios. The objpool is best suited for the following cases: 1) Memory allocation or reclamation are prohibited or too expensive 2) Consumers are of different priorities, such as irqs and threads Limitations: 1) Maximum objects (capacity) is fixed after objpool creation 2) All pre-allocated objects are managed in percpu ring array, which consumes more memory than linked listsThanks for updating! This looks good to me except 2 points. [...]quoted
+ +/* initialize object pool and pre-allocate objects */ +int objpool_init(struct objpool_head *pool, int nr_objs, int object_size, + gfp_t gfp, void *context, objpool_init_obj_cb objinit, + objpool_fini_cb release) +{ + int rc, capacity, slot_size; + + /* check input parameters */ + if (nr_objs <= 0 || nr_objs > OBJPOOL_NR_OBJECT_MAX || + object_size <= 0 || object_size > OBJPOOL_OBJECT_SIZE_MAX) + return -EINVAL; + + /* align up to unsigned long size */ + object_size = ALIGN(object_size, sizeof(long)); + + /* calculate capacity of percpu objpool_slot */ + capacity = roundup_pow_of_two(nr_objs);This must be 'roundup_pow_of_two(nr_objs + 1)' because if nr_objs is power of 2 and all objects are pushed on the same slot, tail == head. This means empty and full is the same.That won't happen. Would tail and head wrap only when >= 2^32. When all objects are pushed to the same slot, tail will be (head + capacity).
Ah, indeed. OK.
quoted
quoted
+ if (!capacity) + return -EINVAL; + + /* initialize objpool pool */ + memset(pool, 0, sizeof(struct objpool_head)); + pool->nr_cpus = nr_cpu_ids; + pool->obj_size = object_size; + pool->capacity = capacity; + pool->gfp = gfp & ~__GFP_ZERO; + pool->context = context; + pool->release = release; + slot_size = pool->nr_cpus * sizeof(struct objpool_slot); + pool->cpu_slots = kzalloc(slot_size, pool->gfp); + if (!pool->cpu_slots) + return -ENOMEM; + + /* initialize per-cpu slots */ + rc = objpool_init_percpu_slots(pool, nr_objs, context, objinit); + if (rc) + objpool_fini_percpu_slots(pool); + else + refcount_set(&pool->ref, pool->nr_objs + 1); + + return rc; +} +EXPORT_SYMBOL_GPL(objpool_init); +[...]quoted
+ +/* drop unused objects and defref objpool for releasing */ +void objpool_fini(struct objpool_head *pool) +{ + void *obj; + + do { + /* grab object from objpool and drop it */ + obj = objpool_pop(pool); + + /* + * drop reference of objpool anyway even if + * the obj is NULL, since one extra ref upon + * objpool was already grabbed during pool + * initialization in objpool_init() + */ + if (refcount_dec_and_test(&pool->ref)) + objpool_free(pool);Nit: you can call objpool_drop() instead of repeating the same thing here.objpool_drop won't deref objpool if given obj is NULL. But here we need drop objpool anyway even if obj is NULL.
I guess you decrement for the 'objpool' itself if obj=NULL, but I think it is a bit hacky (so you added the comment). e.g. rethook is doing something like below. --- /* extra count for this pool itself */ count = 1; /* make the pool empty */ while (objpool_pop(pool)) count++; if (refcount_sub_and_test(count, &pool->ref)) objpool_free(pool); ---
quoted
Thank you,quoted
+ } while (obj); +} +EXPORT_SYMBOL_GPL(objpool_fini); -- 2.40.1Thanks for you time
-- Masami Hiramatsu (Google) [off-list ref]