All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] mm/slub: batch partial slab list operations
@ 2026-05-29  3:50 Hao Li
  2026-05-29  3:50 ` [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state Hao Li
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Hao Li @ 2026-05-29  3:50 UTC (permalink / raw)
  To: vbabka, harry, akpm
  Cc: cl, rientjes, roman.gushchin, linux-mm, linux-kernel, Hao Li

The loops in get_partial_node_bulk() and __refill_objects_node() perform
individual list operations for each slab. This patchset optimize this.

Patch 1 is a cleanup patch that wraps duplicated code into helper
functions.

Patch 2 reduces the number of list operations by processing slabs in
batches, thereby minimizing the time spent inside the critical section.

Changes in v3:
- Reorder the two patches for better logical flow. (Thanks Vlastimil)
- Introduce set_node_partial_state for symmetry. (Thanks Vlastimil)

Changes in v2:
- Patch 1 applies the same optimization to __refill_objects_node. (Thanks
  Harry and Vlastimil)
- Patch 2 introduces a helper to wraps duplicated code. (Thanks Harry)

Hao Li (2):
  mm/slub: introduce helpers for node partial slab state
  mm/slub: detach and reattach partial slabs in batch

 mm/slub.c | 51 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 37 insertions(+), 14 deletions(-)

base-commit: 6b41dc6749232c14f3e069f8115084a7bbeee402
-- 
2.54.0



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state
  2026-05-29  3:50 [PATCH v3 0/2] mm/slub: batch partial slab list operations Hao Li
@ 2026-05-29  3:50 ` Hao Li
  2026-06-01  3:38   ` Harry Yoo
  2026-05-29  3:50 ` [PATCH v3 2/2] mm/slub: detach and reattach partial slabs in batch Hao Li
  2026-05-29  8:01 ` [PATCH v3 0/2] mm/slub: batch partial slab list operations Vlastimil Babka (SUSE)
  2 siblings, 1 reply; 8+ messages in thread
From: Hao Li @ 2026-05-29  3:50 UTC (permalink / raw)
  To: vbabka, harry, akpm
  Cc: cl, rientjes, roman.gushchin, linux-mm, linux-kernel, Hao Li

Wrap partial slab count inc/dec and flag set/clear into
helper functions to reduce code duplication.

Note that __add_partial() is called locklessly in
early_kmem_cache_node_alloc(), but since there is no such use case for
removal, __remove_partial() does not exist.

Suggested-by: Harry Yoo <harry@kernel.org>
Signed-off-by: Hao Li <hao.li@linux.dev>
---
 mm/slub.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index b9f10b869914..45aa0a834f97 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3521,15 +3521,21 @@ static inline void slab_clear_node_partial(struct slab *slab)
 /*
  * Management of partially allocated slabs.
  */
+static inline void set_node_partial_state(struct kmem_cache_node *n,
+					struct slab *slab)
+{
+	slab_set_node_partial(slab);
+	n->nr_partial++;
+}
+
 static inline void
 __add_partial(struct kmem_cache_node *n, struct slab *slab, enum add_mode mode)
 {
-	n->nr_partial++;
 	if (mode == ADD_TO_TAIL)
 		list_add_tail(&slab->slab_list, &n->partial);
 	else
 		list_add(&slab->slab_list, &n->partial);
-	slab_set_node_partial(slab);
+	set_node_partial_state(n, slab);
 }
 
 static inline void add_partial(struct kmem_cache_node *n,
@@ -3539,13 +3545,19 @@ static inline void add_partial(struct kmem_cache_node *n,
 	__add_partial(n, slab, mode);
 }
 
+static inline void clear_node_partial_state(struct kmem_cache_node *n,
+					struct slab *slab)
+{
+	slab_clear_node_partial(slab);
+	n->nr_partial--;
+}
+
 static inline void remove_partial(struct kmem_cache_node *n,
 					struct slab *slab)
 {
 	lockdep_assert_held(&n->list_lock);
 	list_del(&slab->slab_list);
-	slab_clear_node_partial(slab);
-	n->nr_partial--;
+	clear_node_partial_state(n, slab);
 }
 
 /*
@@ -8271,8 +8283,7 @@ static int __kmem_cache_do_shrink(struct kmem_cache *s)
 
 			if (free == slab->objects) {
 				list_move(&slab->slab_list, &discard);
-				slab_clear_node_partial(slab);
-				n->nr_partial--;
+				clear_node_partial_state(n, slab);
 				dec_slabs_node(s, node, slab->objects);
 			} else if (free <= SHRINK_PROMOTE_MAX)
 				list_move(&slab->slab_list, promote + free - 1);
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v3 2/2] mm/slub: detach and reattach partial slabs in batch
  2026-05-29  3:50 [PATCH v3 0/2] mm/slub: batch partial slab list operations Hao Li
  2026-05-29  3:50 ` [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state Hao Li
@ 2026-05-29  3:50 ` Hao Li
  2026-06-01  3:51   ` Harry Yoo
  2026-05-29  8:01 ` [PATCH v3 0/2] mm/slub: batch partial slab list operations Vlastimil Babka (SUSE)
  2 siblings, 1 reply; 8+ messages in thread
From: Hao Li @ 2026-05-29  3:50 UTC (permalink / raw)
  To: vbabka, harry, akpm
  Cc: cl, rientjes, roman.gushchin, linux-mm, linux-kernel, Hao Li

get_partial_node_bulk() moves each selected slab from the node's
partial list to the local pc->slabs list using a remove_partial() and
list_add() pair. In practice, the loop often detaches several adjacent
slabs. Doing this individually repeatedly manipulates list pointers
while holding n->list_lock, which causes unnecessary churn.

To demonstrate this, the counts below show how often single vs. multiple
consecutive slabs are retrieved during a will-it-scale mmap stress test:

consecutive_slabs_count        frequency
= 1                            277345324
= 2                            335238023
= 3                            175717884
>= 4                           88862337

The data confirms that retrieving multiple contiguous slabs is highly
frequent.

To optimize this, track contiguous runs of matching slabs and move each
run in a single operation using list_bulk_move_tail(). This reduces list
pointer churn inside the lock critical section.

Apply the same optimization to __refill_objects_node() when reattaching
leftover partial slabs back to the node's partial list.

The will-it-scale mmap benchmark shows a 2% ~ 5% performance improvement
after applying this patch.

Signed-off-by: Hao Li <hao.li@linux.dev>
---
 mm/slub.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index 45aa0a834f97..4fdf5c36a69b 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3751,6 +3751,7 @@ static bool get_partial_node_bulk(struct kmem_cache *s,
 				  bool allow_spin)
 {
 	struct slab *slab, *slab2;
+	struct slab *first = NULL, *last = NULL;
 	unsigned int total_free = 0;
 	unsigned long flags;
 
@@ -3769,8 +3770,15 @@ static bool get_partial_node_bulk(struct kmem_cache *s,
 		struct freelist_counters flc;
 		unsigned int slab_free;
 
-		if (!pfmemalloc_match(slab, pc->flags))
+		if (!pfmemalloc_match(slab, pc->flags)) {
+			if (first) {
+				list_bulk_move_tail(&pc->slabs,
+						    &first->slab_list,
+						    &last->slab_list);
+				first = NULL;
+			}
 			continue;
+		}
 
 		/*
 		 * determine the number of free objects in the slab racily
@@ -3787,15 +3795,20 @@ static bool get_partial_node_bulk(struct kmem_cache *s,
 		    && total_free + slab_free > pc->max_objects)
 			break;
 
-		remove_partial(n, slab);
-
-		list_add(&slab->slab_list, &pc->slabs);
+		if (!first)
+			first = slab;
+		last = slab;
+		clear_node_partial_state(n, slab);
 
 		total_free += slab_free;
 		if (total_free >= pc->max_objects)
 			break;
 	}
 
+	if (first)
+		list_bulk_move_tail(&pc->slabs, &first->slab_list,
+				    &last->slab_list);
+
 	spin_unlock_irqrestore(&n->list_lock, flags);
 	return total_free > 0;
 }
@@ -7206,11 +7219,10 @@ __refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int mi
 	if (!list_empty(&pc.slabs)) {
 		spin_lock_irqsave(&n->list_lock, flags);
 
-		list_for_each_entry_safe(slab, slab2, &pc.slabs, slab_list) {
+		list_for_each_entry(slab, &pc.slabs, slab_list)
+			set_node_partial_state(n, slab);
 
-			list_del(&slab->slab_list);
-			add_partial(n, slab, ADD_TO_TAIL);
-		}
+		list_splice_tail(&pc.slabs, &n->partial);
 
 		spin_unlock_irqrestore(&n->list_lock, flags);
 	}
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 0/2] mm/slub: batch partial slab list operations
  2026-05-29  3:50 [PATCH v3 0/2] mm/slub: batch partial slab list operations Hao Li
  2026-05-29  3:50 ` [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state Hao Li
  2026-05-29  3:50 ` [PATCH v3 2/2] mm/slub: detach and reattach partial slabs in batch Hao Li
@ 2026-05-29  8:01 ` Vlastimil Babka (SUSE)
  2 siblings, 0 replies; 8+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-05-29  8:01 UTC (permalink / raw)
  To: Hao Li, harry, akpm; +Cc: cl, rientjes, roman.gushchin, linux-mm, linux-kernel

On 5/29/26 05:50, Hao Li wrote:
> The loops in get_partial_node_bulk() and __refill_objects_node() perform
> individual list operations for each slab. This patchset optimize this.
> 
> Patch 1 is a cleanup patch that wraps duplicated code into helper
> functions.
> 
> Patch 2 reduces the number of list operations by processing slabs in
> batches, thereby minimizing the time spent inside the critical section.
> 
> Changes in v3:
> - Reorder the two patches for better logical flow. (Thanks Vlastimil)
> - Introduce set_node_partial_state for symmetry. (Thanks Vlastimil)
> 
> Changes in v2:
> - Patch 1 applies the same optimization to __refill_objects_node. (Thanks
>   Harry and Vlastimil)
> - Patch 2 introduces a helper to wraps duplicated code. (Thanks Harry)
> 
> Hao Li (2):
>   mm/slub: introduce helpers for node partial slab state
>   mm/slub: detach and reattach partial slabs in batch
> 
>  mm/slub.c | 51 +++++++++++++++++++++++++++++++++++++--------------
>  1 file changed, 37 insertions(+), 14 deletions(-)

Applied to slab/for-next, thanks!

> base-commit: 6b41dc6749232c14f3e069f8115084a7bbeee402



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state
  2026-05-29  3:50 ` [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state Hao Li
@ 2026-06-01  3:38   ` Harry Yoo
  2026-06-01  5:26     ` Hao Li
  0 siblings, 1 reply; 8+ messages in thread
From: Harry Yoo @ 2026-06-01  3:38 UTC (permalink / raw)
  To: Hao Li, vbabka, akpm; +Cc: cl, rientjes, roman.gushchin, linux-mm, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 634 bytes --]



On 5/29/26 12:50 PM, Hao Li wrote:
> Wrap partial slab count inc/dec and flag set/clear into
> helper functions to reduce code duplication.
> 
> Note that __add_partial() is called locklessly in
> early_kmem_cache_node_alloc(), but since there is no such use case for
> removal, __remove_partial() does not exist.

nit: __remove_partial() -> clear_partial_node_state()
oh wait, it does exist. Let's drop this paragraph?

Otherwise LGTM!
Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>

> Suggested-by: Harry Yoo <harry@kernel.org>
> Signed-off-by: Hao Li <hao.li@linux.dev>

-- 
Cheers,
Harry / Hyeonggon


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 2/2] mm/slub: detach and reattach partial slabs in batch
  2026-05-29  3:50 ` [PATCH v3 2/2] mm/slub: detach and reattach partial slabs in batch Hao Li
@ 2026-06-01  3:51   ` Harry Yoo
  0 siblings, 0 replies; 8+ messages in thread
From: Harry Yoo @ 2026-06-01  3:51 UTC (permalink / raw)
  To: Hao Li, vbabka, akpm; +Cc: cl, rientjes, roman.gushchin, linux-mm, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1456 bytes --]



On 5/29/26 12:50 PM, Hao Li wrote:
> get_partial_node_bulk() moves each selected slab from the node's
> partial list to the local pc->slabs list using a remove_partial() and
> list_add() pair. In practice, the loop often detaches several adjacent
> slabs. Doing this individually repeatedly manipulates list pointers
> while holding n->list_lock, which causes unnecessary churn.
> 
> To demonstrate this, the counts below show how often single vs. multiple
> consecutive slabs are retrieved during a will-it-scale mmap stress test:
> 
> consecutive_slabs_count        frequency
> = 1                            277345324
> = 2                            335238023
> = 3                            175717884
>> = 4                           88862337
> 
> The data confirms that retrieving multiple contiguous slabs is highly
> frequent.
> 
> To optimize this, track contiguous runs of matching slabs and move each
> run in a single operation using list_bulk_move_tail(). This reduces list
> pointer churn inside the lock critical section.
> 
> Apply the same optimization to __refill_objects_node() when reattaching
> leftover partial slabs back to the node's partial list.
> 
> The will-it-scale mmap benchmark shows a 2% ~ 5% performance improvement
> after applying this patch.
> 
> Signed-off-by: Hao Li <hao.li@linux.dev>
> ---

Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>

-- 
Cheers,
Harry / Hyeonggon

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state
  2026-06-01  3:38   ` Harry Yoo
@ 2026-06-01  5:26     ` Hao Li
  2026-06-01  6:17       ` Harry Yoo
  0 siblings, 1 reply; 8+ messages in thread
From: Hao Li @ 2026-06-01  5:26 UTC (permalink / raw)
  To: Harry Yoo
  Cc: vbabka, akpm, cl, rientjes, roman.gushchin, linux-mm,
	linux-kernel

On Mon, Jun 01, 2026 at 12:38:48PM +0900, Harry Yoo wrote:
> 
> 
> On 5/29/26 12:50 PM, Hao Li wrote:
> > Wrap partial slab count inc/dec and flag set/clear into
> > helper functions to reduce code duplication.
> > 
> > Note that __add_partial() is called locklessly in
> > early_kmem_cache_node_alloc(), but since there is no such use case for
> > removal, __remove_partial() does not exist.
> 
> nit: __remove_partial() -> clear_partial_node_state()

Ah, in this sentence, I just wanted to clarify that __remove_partial doesn't
exist. it's not a typo :)

> oh wait, it does exist. Let's drop this paragraph?

This paragraph is just a quick side note to explain why __remove_partial()
doesn't exist. I noticed that __add_partial lacks a symmetric counterpart,
which felt a bit abrupt/inconsistent, so I wanted to share some context on why
that is.

Historically, the __remove_partial function did exist. Back then, we had a use
case where free_partial called __remove_partial without holding the lock.
Later, free_partial was changed to call remove_partial under the lock, which is
why __remove_partial was eventually removed in commit
52b4b950b50740bff507a62907e86710743c22e7.

> 
> Otherwise LGTM!
> Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>


Thanks!

> 
> > Suggested-by: Harry Yoo <harry@kernel.org>
> > Signed-off-by: Hao Li <hao.li@linux.dev>
> 

-- 
Thanks,
Hao


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state
  2026-06-01  5:26     ` Hao Li
@ 2026-06-01  6:17       ` Harry Yoo
  0 siblings, 0 replies; 8+ messages in thread
From: Harry Yoo @ 2026-06-01  6:17 UTC (permalink / raw)
  To: Hao Li; +Cc: vbabka, akpm, cl, rientjes, roman.gushchin, linux-mm,
	linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1049 bytes --]



On 6/1/26 2:26 PM, Hao Li wrote:
> On Mon, Jun 01, 2026 at 12:38:48PM +0900, Harry Yoo wrote:
>>
>>
>> On 5/29/26 12:50 PM, Hao Li wrote:
>>> Wrap partial slab count inc/dec and flag set/clear into
>>> helper functions to reduce code duplication.
>>>
>>> Note that __add_partial() is called locklessly in
>>> early_kmem_cache_node_alloc(), but since there is no such use case for
>>> removal, __remove_partial() does not exist.
>>
>> nit: __remove_partial() -> clear_partial_node_state()
> 
> Ah, in this sentence, I just wanted to clarify that __remove_partial doesn't
> exist. it's not a typo :)
> 
>> oh wait, it does exist. Let's drop this paragraph?
> 
> This paragraph is just a quick side note to explain why __remove_partial()
> doesn't exist. I noticed that __add_partial lacks a symmetric counterpart,
> which felt a bit abrupt/inconsistent, so I wanted to share some context on why
> that is.

Ah, I see. I was confused because it didn't touch those functions :)
Thanks!

-- 
Cheers,
Harry / Hyeonggon


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-06-01  6:17 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-29  3:50 [PATCH v3 0/2] mm/slub: batch partial slab list operations Hao Li
2026-05-29  3:50 ` [PATCH v3 1/2] mm/slub: introduce helpers for node partial slab state Hao Li
2026-06-01  3:38   ` Harry Yoo
2026-06-01  5:26     ` Hao Li
2026-06-01  6:17       ` Harry Yoo
2026-05-29  3:50 ` [PATCH v3 2/2] mm/slub: detach and reattach partial slabs in batch Hao Li
2026-06-01  3:51   ` Harry Yoo
2026-05-29  8:01 ` [PATCH v3 0/2] mm/slub: batch partial slab list operations Vlastimil Babka (SUSE)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.