linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: Hao Li <hao.li@linux.dev>
To: "Vlastimil Babka (SUSE)" <vbabka@kernel.org>
Cc: Harry Yoo <harry@kernel.org>, Christoph Lameter <cl@gentwo.org>,
	 David Rientjes <rientjes@google.com>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	 Andrew Morton <akpm@linux-foundation.org>,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	 hu.shengming@zte.com.cn,
	Vinicius Costa Gomes <vinicius.gomes@intel.com>
Subject: Re: [PATCH RFC] mm, slab: add an optimistic __slab_try_return_freelist()
Date: Thu, 23 Apr 2026 14:47:40 +0800	[thread overview]
Message-ID: <aldjcfy4b5dnifqfnzrleyoeizz33fxscnrdxnitnf5zfdeuqs@v4n2vf6ywy2c> (raw)
In-Reply-To: <d0510c1f-4930-4424-97b1-b9c9028eddb5@kernel.org>

On Wed, Apr 22, 2026 at 01:55:09PM +0200, Vlastimil Babka (SUSE) wrote:
> On 4/22/26 09:09, Hao Li wrote:
> > On Tue, Apr 21, 2026 at 04:49:52PM +0200, Vlastimil Babka (SUSE) wrote:
> >> When we end up returning extraneous objects during refill to a slab
> >> where we just did a get_freelist_nofreeze(), it is likely no other CPU
> >> has freed objects to it meanwhile. We can then reattach the remainder of
> >> the freelist without having to walk the (potentially cache cold)
> >> freelist to to find its tail to connect slab->freelist to it.
> > 
> > this approach is clever!
> 
> Thanks!
> 
> > 
> > I was just brainstorming a bit here: what if we only try calling
> > slab_update_freelist without grabbing the lock or touching the partial list at
> > all?
> > Instead, we could just toss the slab right back into pc.slabs. That way,
> > we can let the downstream logic for handling "leftover slabs" take care of this
> > slab together. It could save us a whole lock/unlock pair.
> 
> Great suggestion, thanks! Indeed it should not be necessary to reattach the
> freelist and return the slab to the partial list at the same moment, AFAICS.
> Makes the code simpler. Here's a pre-v2:

Thanks for ack and working on this!

> 
> From 6f56844b79fcca5d1dd4203b879af7daa11d09e5 Mon Sep 17 00:00:00 2001
> From: "Vlastimil Babka (SUSE)" <vbabka@kernel.org>
> Date: Tue, 21 Apr 2026 16:28:01 +0200
> Subject: [PATCH RFC] mm, slab: add an optimistic __slab_try_return_freelist()
> 
> When we end up returning extraneous objects during refill to a slab
> where we just did a get_freelist_nofreeze(), it is likely no other CPU
> has freed objects to it meanwhile. We can then reattach the remainder of
> the freelist without having to walk the (potentially cache cold)
> freelist to to find its tail to connect slab->freelist to it.
> 
> Add a __slab_try_return_freelist() function that does that. As suggested
> by Hao Li, it doesn't need to also return the slab to the partial list,
> because there's code in __refill_objects_node() that already does that
> for any slabs where we don't detach the freelist.
> 
> Signed-off-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
>  mm/slub.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 54 insertions(+), 9 deletions(-)

Looks good to me.

Reviewed-by: Hao Li <hao.li@linux.dev>

> 
> diff --git a/mm/slub.c b/mm/slub.c
> index 35b6cd0efc3b..95e4289671b3 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -373,6 +373,8 @@ enum stat_item {
>  	SHEAF_PREFILL_OVERSIZE,	/* Allocation of oversize sheaf for prefill */
>  	SHEAF_RETURN_FAST,	/* Sheaf return reattached spare sheaf */
>  	SHEAF_RETURN_SLOW,	/* Sheaf return could not reattach spare */
> +	REFILL_RETURN_FAST,
> +	REFILL_RETURN_SLOW,
>  	NR_SLUB_STAT_ITEMS
>  };
>  
> @@ -4323,7 +4325,8 @@ static inline bool pfmemalloc_match(struct slab *slab, gfp_t gfpflags)
>   * Assumes this is performed only for caches without debugging so we
>   * don't need to worry about adding the slab to the full list.
>   */
> -static inline void *get_freelist_nofreeze(struct kmem_cache *s, struct slab *slab)
> +static inline void *get_freelist_nofreeze(struct kmem_cache *s, struct slab *slab,
> +					  unsigned int *count)
>  {
>  	struct freelist_counters old, new;
>  
> @@ -4339,6 +4342,7 @@ static inline void *get_freelist_nofreeze(struct kmem_cache *s, struct slab *sla
>  
>  	} while (!slab_update_freelist(s, slab, &old, &new, "get_freelist_nofreeze"));
>  
> +	*count = old.objects - old.inuse;
>  	return old.freelist;
>  }
>  
> @@ -5502,6 +5506,35 @@ static noinline void free_to_partial_list(
>  	}
>  }
>  
> +/*
> + * Try returning (remainder of) the freelist that we just detached from the
> + * slab.  Optimistically assume the slab is still full, so we don't need to find
> + * the tail of the detached freelist.
> + *
> + * Fail if the slab isn't full anymore due to a cocurrent free.
> + */
> +static bool __slab_try_return_freelist(struct kmem_cache *s, struct slab *slab,
> +				       void *head, int cnt)
> +{
> +	struct freelist_counters old, new;
> +
> +	old.freelist = slab->freelist;
> +	old.counters = slab->counters;
> +
> +	if (old.freelist)
> +		return false;
> +
> +	new.freelist = head;
> +	new.counters = old.counters;
> +	new.inuse -= cnt;
> +
> +	if (!slab_update_freelist(s, slab, &old, &new, "__slab_try_return_freelist"))
> +		return false;
> +
> +	stat(s, REFILL_RETURN_FAST);
> +	return true;
> +}
> +
>  /*
>   * Slow path handling. This may still be called frequently since objects
>   * have a longer lifetime than the cpu slabs in most processing loads.
> @@ -7113,34 +7146,42 @@ __refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int mi
>  
>  	list_for_each_entry_safe(slab, slab2, &pc.slabs, slab_list) {
>  
> +		unsigned int count;
> +
>  		list_del(&slab->slab_list);
>  
> -		object = get_freelist_nofreeze(s, slab);
> +		object = get_freelist_nofreeze(s, slab, &count);
>  
> -		while (object && refilled < max) {
> +		while (count && refilled < max) {
>  			p[refilled] = object;
>  			object = get_freepointer(s, object);
>  			maybe_wipe_obj_freeptr(s, p[refilled]);
>  
>  			refilled++;
> +			count--;
>  		}
>  
>  		/*
>  		 * Freelist had more objects than we can accommodate, we need to
> -		 * free them back. We can treat it like a detached freelist, just
> -		 * need to find the tail object.
> +		 * free them back. First we try to be optimistic and assume the
> +		 * slab is stil full since we just detached its freelist.
> +		 * Otherwise we must need to find the tail object.
>  		 */
> -		if (unlikely(object)) {
> +		if (unlikely(count)) {
>  			void *head = object;
>  			void *tail;
> -			int cnt = 0;
> +
> +			if (__slab_try_return_freelist(s, slab, head, count)) {
> +				list_add(&slab->slab_list, &pc.slabs);
> +				break;
> +			}
>  
>  			do {
>  				tail = object;
> -				cnt++;
>  				object = get_freepointer(s, object);
>  			} while (object);
> -			__slab_free(s, slab, head, tail, cnt, _RET_IP_);
> +			__slab_free(s, slab, head, tail, count, _RET_IP_);
> +			stat(s, REFILL_RETURN_SLOW);
>  		}
>  
>  		if (refilled >= max)
> @@ -9366,6 +9407,8 @@ STAT_ATTR(SHEAF_PREFILL_SLOW, sheaf_prefill_slow);
>  STAT_ATTR(SHEAF_PREFILL_OVERSIZE, sheaf_prefill_oversize);
>  STAT_ATTR(SHEAF_RETURN_FAST, sheaf_return_fast);
>  STAT_ATTR(SHEAF_RETURN_SLOW, sheaf_return_slow);
> +STAT_ATTR(REFILL_RETURN_FAST, refill_return_fast);
> +STAT_ATTR(REFILL_RETURN_SLOW, refill_return_slow);
>  #endif	/* CONFIG_SLUB_STATS */
>  
>  #ifdef CONFIG_KFENCE
> @@ -9454,6 +9497,8 @@ static const struct attribute *const slab_attrs[] = {
>  	&sheaf_prefill_oversize_attr.attr,
>  	&sheaf_return_fast_attr.attr,
>  	&sheaf_return_slow_attr.attr,
> +	&refill_return_fast_attr.attr,
> +	&refill_return_slow_attr.attr,
>  #endif
>  #ifdef CONFIG_FAILSLAB
>  	&failslab_attr.attr,
> -- 
> 2.53.0
> 
> 

-- 
Thanks,
Hao


      reply	other threads:[~2026-04-23  6:48 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-21 14:49 Vlastimil Babka (SUSE)
2026-04-22  7:09 ` Hao Li
2026-04-22 11:55   ` Vlastimil Babka (SUSE)
2026-04-23  6:47     ` Hao Li [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aldjcfy4b5dnifqfnzrleyoeizz33fxscnrdxnitnf5zfdeuqs@v4n2vf6ywy2c \
    --to=hao.li@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=cl@gentwo.org \
    --cc=harry@kernel.org \
    --cc=hu.shengming@zte.com.cn \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=rientjes@google.com \
    --cc=roman.gushchin@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=vinicius.gomes@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox