mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
@ 2026-10-19 23:06 Joanne Koong
  2026-10-20  0:16 ` Shakeel Butt
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Joanne Koong @ 2026-10-19 23:06 UTC (permalink / raw)
  To: Andrew Morton
  Cc: linux-mm, linux-kernel, David Hildenbrand, Lorenzo Stoakes,
	Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Qi Zheng,
	Shakeel Butt, Roman Gushchin, Muchun Song, Zi Yan, Baolin Wang,
	Ryan Roberts, Barry Song, Usama Arif, Lance Yang, Hugh Dickins

move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
folio_unqueue_deferred_split() on folios where the refcount has dropped
to zero. In these paths, folio_unqueue_deferred_split() is called with
the lruvec lock often held (move_folios_to_lru() always holds it and
folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
in the batch except the first). However, it's not needed to hold the
lruvec lock.

The lruvec lock protects folio->lru and the reclaim LRU lists whereas
the deferred split queue lives on folio->_deferred_list in
deferred_split_lru. The deferred_split_lru is a list_lru with its own
per-memcg per-node lock.

There are already other existing callers that call
folio_unqueue_deferred_split() without holding the lruvec lock (eg
__folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).

Add folio_batch_unqueue_deferred_split() and use it in
move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
unqueue each batch of zero-refcount folios from the deferred split list
after the lruvec lock is released and before
mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
critical sections, which run with IRQs disabled and are highly
contended.

This came out of the discussion on v2 of the mTHP shrinker series [1],
where queueing small mTHP folios on the deferred split queue was found
to regress Barry's fault microbenchmark. With [1] applied and
khugepaged/max_ptes_none=0 so that every folio is queued, perf lock
contention showed that the extra cost came from waiting on the lruvec
lock in the free path.

Test setup: 2-socket Intel Skylake, 40 cores / 80 threads, memory bound
to node 0 so that all threads contend on one lruvec, with each of 80
threads faulting in and unmapping 16 MB of anonymous memory 200 times.
With only 16K mTHP enabled, this patch reduces the queueing cost from
15% runtime to noise, and lruvec lock wait time (summed across threads)
drops from 491 seconds to 38 seconds. Most of the remaining contention
moves to the deferred split list_lru lock, but runtime still drops back
to the unqueued baseline. This test uses a worst-case configuration ([1]
is expected to only queue folios of 2M and larger).

[1] https://lore.kernel.org/linux-mm/CAJnrk1a2PpUGTsCgS7jct2KET4K2zAL+_h+te8G1MaU9bXs99w@mail.gmail.com/

Reviewed-by: Lance Yang <lance.yang@linux.dev>
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
v2 -> v3:
- Move benchmark results into the commit message (Lance, Barry)
v2 link: https://lore.kernel.org/linux-mm/20261017004555.2890038-1-joannelkoong@gmail.com/

v1 -> v2:
- Reword comment in folio_batch_move_lru() (David)
v1 link: https://lore.kernel.org/linux-mm/20261013221307.850803-1-joannelkoong@gmail.com/

 mm/folio.c    | 24 +++++++++++++++++++-----
 mm/internal.h |  1 +
 mm/vmscan.c   |  3 ++-
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/mm/folio.c b/mm/folio.c
index 35e242b48870..45c814f3857b 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -109,6 +109,19 @@ EXPORT_SYMBOL(__folio_put);
 
 typedef void (*move_fn_t)(struct lruvec *lruvec, struct folio *folio);
 
+/*
+ * Unqueue a batch of folios from the deferred split queue. This must be
+ * called after the folios' refcounts have dropped to zero and before they
+ * are uncharged (see comment above __folio_unqueue_deferred_split() for why).
+ */
+void folio_batch_unqueue_deferred_split(struct folio_batch *fbatch)
+{
+	unsigned int i;
+
+	for (i = 0; i < folio_batch_count(fbatch); i++)
+		folio_unqueue_deferred_split(fbatch->folios[i]);
+}
+
 static void lru_add(struct lruvec *lruvec, struct folio *folio)
 {
 	int was_unevictable = folio_test_clear_unevictable(folio);
@@ -177,14 +190,10 @@ static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn)
 		 *
 		 * We're bypassing normal cleanup. Clear flags that are not
 		 * applicable to dead folios.
-		 *
-		 * Since the folio may be part of a huge page, unqueue from
-		 * deferred split list to avoid a dangling list entry.
 		 */
 		if (is_lru_add && folio_ref_freeze(folio, 1)) {
 			__folio_clear_active(folio);
 			__folio_clear_unevictable(folio);
-			folio_unqueue_deferred_split(folio);
 			fbatch->folios[i] = NULL;
 			folio_batch_add(&free_fbatch, folio);
 			continue;
@@ -201,6 +210,11 @@ static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn)
 
 	/* Cleanup filtered dead folios. */
 	if (is_lru_add) {
+		/*
+		 * Unqueue any large folios from the deferred split queue to
+		 * avoid a dangling list entry once the folios are freed.
+		 */
+		folio_batch_unqueue_deferred_split(&free_fbatch);
 		mem_cgroup_uncharge_folios(&free_fbatch);
 		free_unref_folios(&free_fbatch);
 	}
@@ -1020,7 +1034,6 @@ void folios_put_refs(struct folio_batch *folios, unsigned int *refs)
 			free_huge_folio(folio);
 			continue;
 		}
-		folio_unqueue_deferred_split(folio);
 		__page_cache_release(folio, &lruvec, &flags);
 
 		if (j != i)
@@ -1035,6 +1048,7 @@ void folios_put_refs(struct folio_batch *folios, unsigned int *refs)
 	}
 
 	folios->nr = j;
+	folio_batch_unqueue_deferred_split(folios);
 	mem_cgroup_uncharge_folios(folios);
 	free_unref_folios(folios);
 }
diff --git a/mm/internal.h b/mm/internal.h
index d497c35ee783..33ae388feb33 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -761,6 +761,7 @@ static inline bool folio_unqueue_deferred_split(struct folio *folio)
 
 	return __folio_unqueue_deferred_split(folio);
 }
+void folio_batch_unqueue_deferred_split(struct folio_batch *fbatch);
 
 static inline struct folio *page_rmappable_folio(struct page *page)
 {
diff --git a/mm/vmscan.c b/mm/vmscan.c
index daa7e37c2236..9c71a50309e8 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2007,9 +2007,9 @@ static unsigned int move_folios_to_lru(struct list_head *list, bool do_rotate)
 		if (unlikely(folio_put_testzero(folio))) {
 			__folio_clear_lru_flags(folio);
 
-			folio_unqueue_deferred_split(folio);
 			if (folio_batch_add(&free_folios, folio) == 0) {
 				lruvec_unlock_irq(lruvec);
+				folio_batch_unqueue_deferred_split(&free_folios);
 				mem_cgroup_uncharge_folios(&free_folios);
 				free_unref_folios(&free_folios);
 				lruvec = NULL;
@@ -2041,6 +2041,7 @@ static unsigned int move_folios_to_lru(struct list_head *list, bool do_rotate)
 		lruvec_unlock_irq(lruvec);
 
 	if (free_folios.nr) {
+		folio_batch_unqueue_deferred_split(&free_folios);
 		mem_cgroup_uncharge_folios(&free_folios);
 		free_unref_folios(&free_folios);
 	}
-- 
2.52.0


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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-19 23:06 [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock Joanne Koong
@ 2026-10-20  0:16 ` Shakeel Butt
  2026-10-20  2:28 ` Muchun Song
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Shakeel Butt @ 2026-10-20  0:16 UTC (permalink / raw)
  To: Joanne Koong
  Cc: Andrew Morton, linux-mm, linux-kernel, David Hildenbrand,
	Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Johannes Weiner,
	Qi Zheng, Roman Gushchin, Muchun Song, Zi Yan, Baolin Wang,
	Ryan Roberts, Barry Song, Usama Arif, Lance Yang, Hugh Dickins

On Mon, Oct 19, 2026 at 04:06:46PM -0700, Joanne Koong wrote:
> move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
> folio_unqueue_deferred_split() on folios where the refcount has dropped
> to zero. In these paths, folio_unqueue_deferred_split() is called with
> the lruvec lock often held (move_folios_to_lru() always holds it and
> folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
> in the batch except the first). However, it's not needed to hold the
> lruvec lock.
> 
> The lruvec lock protects folio->lru and the reclaim LRU lists whereas
> the deferred split queue lives on folio->_deferred_list in
> deferred_split_lru. The deferred_split_lru is a list_lru with its own
> per-memcg per-node lock.
> 
> There are already other existing callers that call
> folio_unqueue_deferred_split() without holding the lruvec lock (eg
> __folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).
> 
> Add folio_batch_unqueue_deferred_split() and use it in
> move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
> unqueue each batch of zero-refcount folios from the deferred split list
> after the lruvec lock is released and before
> mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
> critical sections, which run with IRQs disabled and are highly
> contended.
> 
> This came out of the discussion on v2 of the mTHP shrinker series [1],
> where queueing small mTHP folios on the deferred split queue was found
> to regress Barry's fault microbenchmark. With [1] applied and
> khugepaged/max_ptes_none=0 so that every folio is queued, perf lock
> contention showed that the extra cost came from waiting on the lruvec
> lock in the free path.
> 
> Test setup: 2-socket Intel Skylake, 40 cores / 80 threads, memory bound
> to node 0 so that all threads contend on one lruvec, with each of 80
> threads faulting in and unmapping 16 MB of anonymous memory 200 times.
> With only 16K mTHP enabled, this patch reduces the queueing cost from
> 15% runtime to noise, and lruvec lock wait time (summed across threads)
> drops from 491 seconds to 38 seconds. Most of the remaining contention
> moves to the deferred split list_lru lock, but runtime still drops back
> to the unqueued baseline. This test uses a worst-case configuration ([1]
> is expected to only queue folios of 2M and larger).
> 
> [1] https://lore.kernel.org/linux-mm/CAJnrk1a2PpUGTsCgS7jct2KET4K2zAL+_h+te8G1MaU9bXs99w@mail.gmail.com/
> 
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>

Acked-by: Shakeel Butt <shakeel.butt@linux.dev>

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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-19 23:06 [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock Joanne Koong
  2026-10-20  0:16 ` Shakeel Butt
@ 2026-10-20  2:28 ` Muchun Song
  2026-10-20  8:15 ` David Hildenbrand (Arm)
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Muchun Song @ 2026-10-20  2:28 UTC (permalink / raw)
  To: Joanne Koong
  Cc: Andrew Morton, linux-mm, linux-kernel, David Hildenbrand,
	Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Johannes Weiner,
	Qi Zheng, Shakeel Butt, Roman Gushchin, Zi Yan, Baolin Wang,
	Ryan Roberts, Barry Song, Usama Arif, Lance Yang, Hugh Dickins



> On Oct 20, 2026, at 07:06, Joanne Koong <joannelkoong@gmail.com> wrote:
> 
> move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
> folio_unqueue_deferred_split() on folios where the refcount has dropped
> to zero. In these paths, folio_unqueue_deferred_split() is called with
> the lruvec lock often held (move_folios_to_lru() always holds it and
> folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
> in the batch except the first). However, it's not needed to hold the
> lruvec lock.
> 
> The lruvec lock protects folio->lru and the reclaim LRU lists whereas
> the deferred split queue lives on folio->_deferred_list in
> deferred_split_lru. The deferred_split_lru is a list_lru with its own
> per-memcg per-node lock.
> 
> There are already other existing callers that call
> folio_unqueue_deferred_split() without holding the lruvec lock (eg
> __folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).
> 
> Add folio_batch_unqueue_deferred_split() and use it in
> move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
> unqueue each batch of zero-refcount folios from the deferred split list
> after the lruvec lock is released and before
> mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
> critical sections, which run with IRQs disabled and are highly
> contended.
> 
> This came out of the discussion on v2 of the mTHP shrinker series [1],
> where queueing small mTHP folios on the deferred split queue was found
> to regress Barry's fault microbenchmark. With [1] applied and
> khugepaged/max_ptes_none=0 so that every folio is queued, perf lock
> contention showed that the extra cost came from waiting on the lruvec
> lock in the free path.
> 
> Test setup: 2-socket Intel Skylake, 40 cores / 80 threads, memory bound
> to node 0 so that all threads contend on one lruvec, with each of 80
> threads faulting in and unmapping 16 MB of anonymous memory 200 times.
> With only 16K mTHP enabled, this patch reduces the queueing cost from
> 15% runtime to noise, and lruvec lock wait time (summed across threads)
> drops from 491 seconds to 38 seconds. Most of the remaining contention
> moves to the deferred split list_lru lock, but runtime still drops back
> to the unqueued baseline. This test uses a worst-case configuration ([1]
> is expected to only queue folios of 2M and larger).
> 
> [1] https://lore.kernel.org/linux-mm/CAJnrk1a2PpUGTsCgS7jct2KET4K2zAL+_h+te8G1MaU9bXs99w@mail.gmail.com/
> 
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>

Acked-by: Muchun Song <muchun.song@linux.dev>

Thanks.


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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-19 23:06 [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock Joanne Koong
  2026-10-20  0:16 ` Shakeel Butt
  2026-10-20  2:28 ` Muchun Song
@ 2026-10-20  8:15 ` David Hildenbrand (Arm)
  2026-10-20  9:26 ` Barry Song
  2026-10-20 22:36 ` Andrew Morton
  4 siblings, 0 replies; 8+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-20  8:15 UTC (permalink / raw)
  To: Joanne Koong, Andrew Morton
  Cc: linux-mm, linux-kernel, Lorenzo Stoakes, Liam R . Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Johannes Weiner, Qi Zheng, Shakeel Butt, Roman Gushchin,
	Muchun Song, Zi Yan, Baolin Wang, Ryan Roberts, Barry Song,
	Usama Arif, Lance Yang, Hugh Dickins

On 10/20/26 01:06, Joanne Koong wrote:
> move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
> folio_unqueue_deferred_split() on folios where the refcount has dropped
> to zero. In these paths, folio_unqueue_deferred_split() is called with
> the lruvec lock often held (move_folios_to_lru() always holds it and
> folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
> in the batch except the first). However, it's not needed to hold the
> lruvec lock.
> 
> The lruvec lock protects folio->lru and the reclaim LRU lists whereas
> the deferred split queue lives on folio->_deferred_list in
> deferred_split_lru. The deferred_split_lru is a list_lru with its own
> per-memcg per-node lock.
> 
> There are already other existing callers that call
> folio_unqueue_deferred_split() without holding the lruvec lock (eg
> __folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).
> 
> Add folio_batch_unqueue_deferred_split() and use it in
> move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
> unqueue each batch of zero-refcount folios from the deferred split list
> after the lruvec lock is released and before
> mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
> critical sections, which run with IRQs disabled and are highly
> contended.
> 
> This came out of the discussion on v2 of the mTHP shrinker series [1],
> where queueing small mTHP folios on the deferred split queue was found
> to regress Barry's fault microbenchmark. With [1] applied and
> khugepaged/max_ptes_none=0 so that every folio is queued, perf lock
> contention showed that the extra cost came from waiting on the lruvec
> lock in the free path.
> 
> Test setup: 2-socket Intel Skylake, 40 cores / 80 threads, memory bound
> to node 0 so that all threads contend on one lruvec, with each of 80
> threads faulting in and unmapping 16 MB of anonymous memory 200 times.
> With only 16K mTHP enabled, this patch reduces the queueing cost from
> 15% runtime to noise, and lruvec lock wait time (summed across threads)
> drops from 491 seconds to 38 seconds. Most of the remaining contention
> moves to the deferred split list_lru lock, but runtime still drops back
> to the unqueued baseline. This test uses a worst-case configuration ([1]
> is expected to only queue folios of 2M and larger).
> 
> [1] https://lore.kernel.org/linux-mm/CAJnrk1a2PpUGTsCgS7jct2KET4K2zAL+_h+te8G1MaU9bXs99w@mail.gmail.com/
> 
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> ---

Thanks Joanne!

Acked-by: David Hildenbrand (Arm) <david@kernel.org>

-- 
Cheers,

David

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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-19 23:06 [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock Joanne Koong
                   ` (2 preceding siblings ...)
  2026-10-20  8:15 ` David Hildenbrand (Arm)
@ 2026-10-20  9:26 ` Barry Song
  2026-10-20 22:36 ` Andrew Morton
  4 siblings, 0 replies; 8+ messages in thread
From: Barry Song @ 2026-10-20  9:26 UTC (permalink / raw)
  To: Joanne Koong
  Cc: Andrew Morton, linux-mm, linux-kernel, David Hildenbrand,
	Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Johannes Weiner,
	Qi Zheng, Shakeel Butt, Roman Gushchin, Muchun Song, Zi Yan,
	Baolin Wang, Ryan Roberts, Usama Arif, Lance Yang, Hugh Dickins

On Tue, Oct 20, 2026 at 7:07 AM Joanne Koong <joannelkoong@gmail.com> wrote:
>
> move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
> folio_unqueue_deferred_split() on folios where the refcount has dropped
> to zero. In these paths, folio_unqueue_deferred_split() is called with
> the lruvec lock often held (move_folios_to_lru() always holds it and
> folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
> in the batch except the first). However, it's not needed to hold the
> lruvec lock.
>
> The lruvec lock protects folio->lru and the reclaim LRU lists whereas
> the deferred split queue lives on folio->_deferred_list in
> deferred_split_lru. The deferred_split_lru is a list_lru with its own
> per-memcg per-node lock.
>
> There are already other existing callers that call
> folio_unqueue_deferred_split() without holding the lruvec lock (eg
> __folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).
>
> Add folio_batch_unqueue_deferred_split() and use it in
> move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
> unqueue each batch of zero-refcount folios from the deferred split list
> after the lruvec lock is released and before
> mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
> critical sections, which run with IRQs disabled and are highly
> contended.
>
> This came out of the discussion on v2 of the mTHP shrinker series [1],
> where queueing small mTHP folios on the deferred split queue was found
> to regress Barry's fault microbenchmark. With [1] applied and
> khugepaged/max_ptes_none=0 so that every folio is queued, perf lock
> contention showed that the extra cost came from waiting on the lruvec
> lock in the free path.
>
> Test setup: 2-socket Intel Skylake, 40 cores / 80 threads, memory bound
> to node 0 so that all threads contend on one lruvec, with each of 80
> threads faulting in and unmapping 16 MB of anonymous memory 200 times.
> With only 16K mTHP enabled, this patch reduces the queueing cost from
> 15% runtime to noise, and lruvec lock wait time (summed across threads)
> drops from 491 seconds to 38 seconds. Most of the remaining contention
> moves to the deferred split list_lru lock, but runtime still drops back
> to the unqueued baseline. This test uses a worst-case configuration ([1]
> is expected to only queue folios of 2M and larger).
>
> [1] https://lore.kernel.org/linux-mm/CAJnrk1a2PpUGTsCgS7jct2KET4K2zAL+_h+te8G1MaU9bXs99w@mail.gmail.com/
>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> ---

Reviewed-by: Barry Song <baohua@kernel.org>

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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-19 23:06 [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock Joanne Koong
                   ` (3 preceding siblings ...)
  2026-10-20  9:26 ` Barry Song
@ 2026-10-20 22:36 ` Andrew Morton
  2026-10-20 22:40   ` Barry Song
  4 siblings, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2026-10-20 22:36 UTC (permalink / raw)
  To: Joanne Koong
  Cc: linux-mm, linux-kernel, David Hildenbrand, Lorenzo Stoakes,
	Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Johannes Weiner, Qi Zheng,
	Shakeel Butt, Roman Gushchin, Muchun Song, Zi Yan, Baolin Wang,
	Ryan Roberts, Barry Song, Usama Arif, Lance Yang, Hugh Dickins

On Mon, 19 Oct 2026 16:06:46 -0700 Joanne Koong <joannelkoong@gmail.com> wrote:

> move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
> folio_unqueue_deferred_split() on folios where the refcount has dropped
> to zero. In these paths, folio_unqueue_deferred_split() is called with
> the lruvec lock often held (move_folios_to_lru() always holds it and
> folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
> in the batch except the first). However, it's not needed to hold the
> lruvec lock.
> 
> The lruvec lock protects folio->lru and the reclaim LRU lists whereas
> the deferred split queue lives on folio->_deferred_list in
> deferred_split_lru. The deferred_split_lru is a list_lru with its own
> per-memcg per-node lock.
> 
> There are already other existing callers that call
> folio_unqueue_deferred_split() without holding the lruvec lock (eg
> __folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).
> 
> Add folio_batch_unqueue_deferred_split() and use it in
> move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
> unqueue each batch of zero-refcount folios from the deferred split list
> after the lruvec lock is released and before
> mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
> critical sections, which run with IRQs disabled and are highly
> contended.

Thanks.

This is a speedup and not a correctness thing, yes?  Let's make it a
7.4-rcX thing, for the 7.5 merge window.

I'll park it in the mm-new branch for now.

If people think the speedup is sufficiently worthwhile then I can add a
cc:stable to it, if clear justification is included in the changelog.

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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-20 22:36 ` Andrew Morton
@ 2026-10-20 22:40   ` Barry Song
  2026-10-20 23:47     ` Andrew Morton
  0 siblings, 1 reply; 8+ messages in thread
From: Barry Song @ 2026-10-20 22:40 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Joanne Koong, linux-mm, linux-kernel, David Hildenbrand,
	Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Johannes Weiner,
	Qi Zheng, Shakeel Butt, Roman Gushchin, Muchun Song, Zi Yan,
	Baolin Wang, Ryan Roberts, Usama Arif, Lance Yang, Hugh Dickins

On Wed, Oct 21, 2026 at 6:36 AM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Mon, 19 Oct 2026 16:06:46 -0700 Joanne Koong <joannelkoong@gmail.com> wrote:
>
> > move_folios_to_lru(), folio_batch_move_lru() and folios_put_refs() call
> > folio_unqueue_deferred_split() on folios where the refcount has dropped
> > to zero. In these paths, folio_unqueue_deferred_split() is called with
> > the lruvec lock often held (move_folios_to_lru() always holds it and
> > folio_batch_move_lru()/folios_put_refs() usually hold it for every folio
> > in the batch except the first). However, it's not needed to hold the
> > lruvec lock.
> >
> > The lruvec lock protects folio->lru and the reclaim LRU lists whereas
> > the deferred split queue lives on folio->_deferred_list in
> > deferred_split_lru. The deferred_split_lru is a list_lru with its own
> > per-memcg per-node lock.
> >
> > There are already other existing callers that call
> > folio_unqueue_deferred_split() without holding the lruvec lock (eg
> > __folio_put(), shrink_folio_list(), __folio_migrate_mapping(), ...).
> >
> > Add folio_batch_unqueue_deferred_split() and use it in
> > move_folios_to_lru(), folio_batch_move_lru(), and folios_put_refs() to
> > unqueue each batch of zero-refcount folios from the deferred split list
> > after the lruvec lock is released and before
> > mem_cgroup_uncharge_folios(). This helps shorten the lruvec lock's
> > critical sections, which run with IRQs disabled and are highly
> > contended.
>
> Thanks.
>
> This is a speedup and not a correctness thing, yes?  Let's make it a
> 7.4-rcX thing, for the 7.5 merge window.
>
> I'll park it in the mm-new branch for now.
>
> If people think the speedup is sufficiently worthwhile then I can add a
> cc:stable to it, if clear justification is included in the changelog.

This isn't even a speedup at the moment, as no users have
adopted it yet.

mTHP folios aren't currently added to the deferred split
queue. Joanne is about to add >=2MB mTHP folios to the
deferred split queue again, but even then, contention on
the queue shouldn't be an issue.

It may become a problem someday when we add smaller large
folios, such as 16KB folios, to the deferred split queue.
So this patch is more about future-proofing.

Best Regards
Barry

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

* Re: [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock
  2026-10-20 22:40   ` Barry Song
@ 2026-10-20 23:47     ` Andrew Morton
  0 siblings, 0 replies; 8+ messages in thread
From: Andrew Morton @ 2026-10-20 23:47 UTC (permalink / raw)
  To: Barry Song
  Cc: Joanne Koong, linux-mm, linux-kernel, David Hildenbrand,
	Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
	Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Johannes Weiner,
	Qi Zheng, Shakeel Butt, Roman Gushchin, Muchun Song, Zi Yan,
	Baolin Wang, Ryan Roberts, Usama Arif, Lance Yang, Hugh Dickins

On Wed, 21 Oct 2026 06:40:23 +0800 Barry Song <baohua@kernel.org> wrote:

> > This is a speedup and not a correctness thing, yes?  Let's make it a
> > 7.4-rcX thing, for the 7.5 merge window.
> >
> > I'll park it in the mm-new branch for now.
> >
> > If people think the speedup is sufficiently worthwhile then I can add a
> > cc:stable to it, if clear justification is included in the changelog.
> 
> This isn't even a speedup at the moment, as no users have
> adopted it yet.
> 
> mTHP folios aren't currently added to the deferred split
> queue. Joanne is about to add >=2MB mTHP folios to the
> deferred split queue again, but even then, contention on
> the queue shouldn't be an issue.
> 
> It may become a problem someday when we add smaller large
> folios, such as 16KB folios, to the deferred split queue.
> So this patch is more about future-proofing.

Oh.  This:

: This came out of the discussion on v2 of the mTHP shrinker series [1],
: where queueing small mTHP folios on the deferred split queue was found
: to regress Barry's fault microbenchmark. With [1] applied and
: khugepaged/max_ptes_none=0 so that every folio is queued, perf lock
: contention showed that the extra cost came from waiting on the lruvec
: lock in the free path.

made it sound like an unquantified speedup.

Joanne, please clarify the expected impact (always) in the
changelogging if there are reasons for a v4.

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

end of thread, other threads:[~2026-10-20 23:47 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-19 23:06 [PATCH v3] mm: unqueue deferred split folios outside the lruvec lock Joanne Koong
2026-10-20  0:16 ` Shakeel Butt
2026-10-20  2:28 ` Muchun Song
2026-10-20  8:15 ` David Hildenbrand (Arm)
2026-10-20  9:26 ` Barry Song
2026-10-20 22:36 ` Andrew Morton
2026-10-20 22:40   ` Barry Song
2026-10-20 23:47     ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®