mm/thp: clear deferred split shrinker bits when queues drain
Subsystems (3)
Lance Yang
reviewerPATCH 1/1 mm/thp: clear deferred split shrinker bits when queues drain
From: Lance Yang <[email protected]>
deferred_split_count() returns the raw list_lru count. When the per-memcg, per-node list is empty, that count is 0.
That skips scanning, but it does not tell memcg reclaim that the shrinker is empty. shrink_slab_memcg() only clears the memcg shrinker bit when the count callback reports SHRINK_EMPTY.
Return SHRINK_EMPTY for an empty deferred split list, so the bit can be cleared once the queue has drained.
--- mm/huge_memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
mm/huge_memory.c +4 -1
@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)static unsigned long deferred_split_count (struct shrinker *shrink,struct shrink_control *sc) { -return list_lru_shrink_count (&deferred_split_lru, sc); +unsigned long count; + + count =list_lru_shrink_count (&deferred_split_lru, sc); +return count ?:SHRINK_EMPTY ; }static bool thp_underused (struct folio *folio)
Signature · 1 line
2.49.0
Lance Yang
reviewerSorry, I missed Johannes in Cc ...
Quoted 33 lines · Lance Yang
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>--- mm/huge_memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)mm/huge_memory.c +4
-1@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)static unsigned long deferred_split_count (struct shrinker *shrink,struct shrink_control *sc) { -return list_lru_shrink_count (&deferred_split_lru, sc); +unsigned long count; + + count =list_lru_shrink_count (&deferred_split_lru, sc); +return count ?:SHRINK_EMPTY ; }static bool thp_underused (struct folio *folio)
David Hildenbrand (Arm)
maintainerQuoted 11 lines · Lance Yang
Sorry, I missed Johannes in Cc ...
Quoted 8 lines · Lance Yang
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.
What's the effect of that? Would we consider it a fix that we'd want to backport?
Quoted 9 lines
Quoted 9 lines
Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>---
mm/huge_memory.c| 5 ++++-1 file changed, 4 insertions(+), 1 deletion(-)
Quoted 17 lines
Quoted 6 lines
mm/huge_memory.c +0
-0@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)mm/huge_memory.c void deferred_split_folio(struct folio *folio, bool · line 4406
Quoted 10 lines
mm/huge_memory.c +4
-1static unsigned long deferred_split_count(struct shrinker *shrink, struct shrink_control *sc) { - return list_lru_shrink_count(&deferred_split_lru, sc); + unsigned long count; + + count = list_lru_shrink_count(&deferred_split_lru, sc); + return count ?: SHRINK_EMPTY; } static bool thp_underused(struct folio *folio)
This is against Johannes' work, right?
If this is a fix, likely it would be fixing 87eaceb3faa5 ("mm: thp: make deferred split shrinker memcg aware"), right?
Signature · 3 lines
Cheers, David
Lance Yang
reviewerQuoted 6 lines · David Hildenbrand (Arm)
Quoted 5 lines · Lance Yang
Sorry, I missed Johannes in Cc ...
Quoted 2 lines · Lance Yang
From: Lance Yang <[email protected]>
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.What's the effect of that? Would we consider it a fix that we'd want to backport?
Just a stale memcg shrinker bit :) I'd treat this patch as a small cleanup.
Once the queue is empty, count_objects() returns 0. That skips the scan, but shrink_slab_memcg() only clears the bit on SHRINK_EMPTY, not 0.
So memcg reclaim can keep calling the shrinker even though there is nothing on that queue.
Quoted 27 lines
Quoted 9 lines
Quoted 9 lines
Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>--- mm/huge_memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)Quoted 17 lines
Quoted 6 lines
mm/huge_memory.c +0
-0@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)mm/huge_memory.c void deferred_split_folio(struct folio *folio, bool · line 4406
Quoted 10 lines
mm/huge_memory.c +4
-1static unsigned long deferred_split_count(struct shrinker *shrink, struct shrink_control *sc) { - return list_lru_shrink_count(&deferred_split_lru, sc); + unsigned long count; + + count = list_lru_shrink_count(&deferred_split_lru, sc); + return count ?: SHRINK_EMPTY; } static bool thp_underused(struct folio *folio)
Quoted 1 line
This is against Johannes' work, right?
Yep, I noticed it there, but the behavior is older.
Quoted 2 lines
If this is a fix, likely it would be fixing
87eaceb3faa5("mm: thp: make deferred split shrinker memcg aware"), right?
No missed reclaim, just some extra reclaim work :)
Cheers, Lance
Quoted 20 lines · Lance Yang
Quoted 5 lines · David Hildenbrand (Arm)
Quoted 2 lines · Lance Yang
Sorry, I missed Johannes in Cc ...
What's the effect of that? Would we consider it a fix that we'd want to backport?
Just a stale memcg shrinker bit :) I'd treat this patch as a small cleanup.
Once the queue is empty,
count_objects()returns 0. That skips the scan, butshrink_slab_memcg()only clears the bit onSHRINK_EMPTY, not 0.So memcg reclaim can keep calling the shrinker even though there is nothing on that queue.
Quoted 2 lines
Quoted 1 line
Quoted 8 lines
Quoted 1 line
This is against Johannes' work, right?
Yep, I noticed it there, but the behavior is older.
Quoted 2 lines
If this is a fix, likely it would be fixing
87eaceb3faa5("mm: thp: make deferred split shrinker memcg aware"), right?No missed reclaim, just some extra reclaim work :)
Thanks for clarifying! :)
Signature · 3 lines
Cheers, David
Quoted 5 lines · Lance Yang
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.
Quoted 8 lines
That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>
Same as slab, workingset and others.
Andrew Morton
maintainerQuoted 23 lines · On Tue, 2 Jun 2026 12:34:53 +0800 Lance Yang <[email protected]> wrote:
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>--- mm/huge_memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)mm/huge_memory.c +0
-0@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped) static unsigned long deferred_split_count(struct shrinker *shrink,
mm/huge_memory.c void deferred_split_folio(struct folio *folio, bool partially_mapped) · line 4406
Quoted 10 lines
mm/huge_memory.c +4
-1struct shrink_control *sc) { -return list_lru_shrink_count (&deferred_split_lru, sc); +unsigned long count; + + count =list_lru_shrink_count (&deferred_split_lru, sc); +return count ?:SHRINK_EMPTY ; }static bool thp_underused (struct folio *folio)
Should this be handled as a fix against hannes's "mm: switch deferred split shrinker to list_lru"?
Lance Yang
reviewerQuoted 34 lines · Andrew Morton
Quoted 32 lines · On Tue, 2 Jun 2026 12:34:53 +0800 Lance Yang <[email protected]> wrote:
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>--- mm/huge_memory.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)mm/huge_memory.c +4
-1@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)static unsigned long deferred_split_count (struct shrinker *shrink,struct shrink_control *sc) { -return list_lru_shrink_count (&deferred_split_lru, sc); +unsigned long count; + + count =list_lru_shrink_count (&deferred_split_lru, sc); +return count ?:SHRINK_EMPTY ; }
Quoted 4 lines
mm/huge_memory.c void deferred_split_folio(struct folio *folio, bool partially_mapped) · line 4406
Quoted 1 line
mm/huge_memory.c +0
-0static bool thp_underused (struct folio *folio)Should this be handled as a fix against hannes's "mm: switch deferred split shrinker to
list_lru"?
Hmm ... I noticed this while looking at Johannes' work, but the behavior is older than that ...
We also discussed the Fixes tag earlier[1] and decided to leave it out. There is no missed reclaim, only some extra reclaim work :)
[1] https://lore.kernel.org/linux-mm/[email protected]/
Cheers, Lance
David Hildenbrand (Arm)
maintainerQuoted 42 lines · Lance Yang
Quoted 30 lines · Andrew Morton
Quoted 28 lines · On Tue, 2 Jun 2026 12:34:53 +0800 Lance Yang <[email protected]> wrote:
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>---
mm/huge_memory.c| 5 ++++-1 file changed, 4 insertions(+), 1 deletion(-)mm/huge_memory.c +1
-1@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped) static unsigned long deferred_split_count(struct shrinker *shrink, struct shrink_control *sc) { - return list_lru_shrink_count(&deferred_split_lru, sc); + unsigned long count;Quoted 8 lines
mm/huge_memory.c void deferred_split_folio(struct folio *folio, bool partially_mapped) · line 4406
Quoted 5 lines
mm/huge_memory.c +3
-0+ + count = list_lru_shrink_count(&deferred_split_lru, sc); + return count ?: SHRINK_EMPTY; } static bool thp_underused(struct folio *folio)Should this be handled as a fix against hannes's "mm: switch deferred split shrinker to
list_lru"?
Quoted 5 lines
Hmm ... I noticed this while looking at Johannes' work, but the behavior is older than that ...
We also discussed the Fixes tag earlier[1] and decided to leave it out. There is no missed reclaim, only some extra reclaim work :)
Right, I conclude that this can be a separate patch on top.
Signature · 3 lines
Cheers, David
Quoted 22 lines · Lance Yang
From: Lance Yang <[email protected]>
deferred_split_count()returns the rawlist_lrucount. When the per-memcg, per-node list is empty, that count is 0.That skips scanning, but it does not tell memcg reclaim that the shrinker is empty.
shrink_slab_memcg()only clears the memcg shrinker bit when the count callback reportsSHRINK_EMPTY.Return
SHRINK_EMPTYfor an empty deferred split list, so the bit can be cleared once the queue has drained.Signed-off-by Lance Yang <[email protected]>---
mm/huge_memory.c| 5 ++++-1 file changed, 4 insertions(+), 1 deletion(-)mm/huge_memory.c +0
-0
mm/huge_memory.c void deferred_split_folio(struct folio *folio, bool partially_mapped) · line 4404
Quoted 9 lines
mm/huge_memory.c +4
-1@@ -4397,7 +4397,10 @@ void deferred_split_folio(struct folio *folio, bool partially_mapped)static unsigned long deferred_split_count (struct shrinker *shrink,struct shrink_control *sc) { -return list_lru_shrink_count (&deferred_split_lru, sc); +unsigned long count; + + count =list_lru_shrink_count (&deferred_split_lru, sc); +return count ?:SHRINK_EMPTY ; }
Looks you are right, thanks.
Quoted 5 lines
mm/huge_memory.c +0
-0static bool thp_underused (struct folio *folio)Signature · 1 line
2.49.0
Signature · 2 lines
Wei Yang Help you, Help me