On 7/20/26 14:44, Harry Yoo (Oracle) wrote:
> rcu_head is overkill for kvfree_rcu() because the callback
> function is always either kfree(), vfree(), or free_large_kmalloc(),
> and thus there is no need for a function pointer.
> 
> kvfree_rcu batching reuses the field to store the start address
> of an object, however, this is not strictly needed because we can
> calculate the start address in the slowpath. For the purpose of
> kvfree_rcu batching, it is sufficient to implement a linked list using
> a single pointer.
> 
> Introduce a new struct called kvfree_rcu_head (the name was suggested
> by Vlastimil Babka), which is similar to rcu_head but is only a single
> pointer to build a linked list, without a function pointer, when
> CONFIG_KVFREE_RCU_BATCHED=y.
> 
> When kvfree_rcu is not batched, kvfree_rcu_head is the same size
> as rcu_head. Note that shrinking struct kvfree_rcu_head on
> CONFIG_KVFREE_RCU_BATCHED=n kernels would inevitably require additional
> complexity and also some sort of batching (which defeats the purpose of
> the config option) because it cannot fall back to call_rcu().
> 
> For now there are no user-visible changes to the API. k[v]free_rcu()
> simply casts rcu_head to kvfree_rcu_head. While this does not affect
> the API, it allows kfree_rcu_nolock() to reuse kvfree_rcu batching
> as a fallback when trylock or sheaf allocation fails.
> 
> Stop storing the object pointer in rcu_head.func and instead calculate
> the object's start address in kvfree_rcu_list(). Factor out the existing
> logic to calculate the start address from kvfree_rcu_cb() to
> kvmalloc_obj_start_addr().
> 
> Signed-off-by: Harry Yoo (Oracle) <[email protected]>

Nice.

Reviewed-by: Vlastimil Babka (SUSE) <[email protected]>

Nit:

> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -351,6 +351,33 @@ static inline int objs_per_slab(const struct kmem_cache 
> *cache,
>       return slab->objects;
>  }
>  
> +/* kvfree_rcu_head offset can be only less than page size */
> +static inline void *kvmalloc_obj_start_addr(void *head)
> +{
> +     void *obj = head;
> +
> +     if (unlikely(is_vmalloc_addr(obj))) {
> +             obj = (void *) PAGE_ALIGN_DOWN((unsigned long)obj);
> +     } else {
> +             struct page *page = virt_to_page(obj);
> +             struct slab *slab = page_slab(page);

Can use virt_to_slab().

> +
> +             if (!slab) {
> +                     obj = (void *) PAGE_ALIGN_DOWN((unsigned long)obj);
> +             } else if (is_kfence_address(obj)) {
> +                     obj = kfence_object_start(obj);
> +             } else {
> +                     struct kmem_cache *s = slab->slab_cache;
> +                     unsigned int idx = __obj_to_index(s, 
> slab_address(slab), obj);
> +
> +                     obj = slab_address(slab) + s->size * idx;
> +                     obj = fixup_red_left(s, obj);
> +             }
> +     }
> +
> +     return obj;
> +}
> +
>  /*
>   * State of the slab allocator.
>   *

Reply via email to