* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 4:34 [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain Lance Yang
@ 2026-06-02 4:38 ` Lance Yang
2026-06-02 12:11 ` David Hildenbrand (Arm)
2026-06-02 10:30 ` Usama Arif
2026-06-02 20:37 ` Andrew Morton
2 siblings, 1 reply; 9+ messages in thread
From: Lance Yang @ 2026-06-02 4:38 UTC (permalink / raw)
To: akpm, hannes
Cc: david, ljs, shakeel.butt, mhocko, david, roman.gushchin,
muchun.song, qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas,
vbabka, ryncsn, zaslonko, gor, wangkefeng.wang, baolin.wang,
baohua, dev.jain, npache, ryan.roberts, cgroups, linux-mm,
linux-kernel
Sorry, I missed Johannes in Cc ...
On 2026/6/2 12:34, Lance Yang wrote:
> From: Lance Yang <lance.yang@linux.dev>
>
> 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.
>
> Signed-off-by: Lance Yang <lance.yang@linux.dev>
> ---
> mm/huge_memory.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 72f6caf0fec6..62d598290c3b 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -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)
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 4:38 ` Lance Yang
@ 2026-06-02 12:11 ` David Hildenbrand (Arm)
2026-06-02 12:47 ` Lance Yang
0 siblings, 1 reply; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-02 12:11 UTC (permalink / raw)
To: Lance Yang, akpm, hannes
Cc: ljs, shakeel.butt, mhocko, david, roman.gushchin, muchun.song,
qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas, vbabka, ryncsn,
zaslonko, gor, wangkefeng.wang, baolin.wang, baohua, dev.jain,
npache, ryan.roberts, cgroups, linux-mm, linux-kernel
On 6/2/26 06:38, Lance Yang wrote:
> Sorry, I missed Johannes in Cc ...
>
> On 2026/6/2 12:34, Lance Yang wrote:
>> From: Lance Yang <lance.yang@linux.dev>
>>
>> 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.
What's the effect of that? Would we consider it a fix that we'd want to backport?
>>
>> Return SHRINK_EMPTY for an empty deferred split list, so the bit can be
>> cleared once the queue has drained.
>>
>> Signed-off-by: Lance Yang <lance.yang@linux.dev>
>> ---
>> mm/huge_memory.c | 5 ++++-
>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>> index 72f6caf0fec6..62d598290c3b 100644
>> --- a/mm/huge_memory.c
>> +++ b/mm/huge_memory.c
>> @@ -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)
>
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?
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 12:11 ` David Hildenbrand (Arm)
@ 2026-06-02 12:47 ` Lance Yang
2026-06-02 17:33 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 9+ messages in thread
From: Lance Yang @ 2026-06-02 12:47 UTC (permalink / raw)
To: David Hildenbrand (Arm), akpm, hannes
Cc: ljs, shakeel.butt, mhocko, david, roman.gushchin, muchun.song,
qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas, vbabka, ryncsn,
zaslonko, gor, wangkefeng.wang, baolin.wang, baohua, dev.jain,
npache, ryan.roberts, cgroups, linux-mm, linux-kernel
On 2026/6/2 20:11, David Hildenbrand (Arm) wrote:
> On 6/2/26 06:38, Lance Yang wrote:
>> Sorry, I missed Johannes in Cc ...
>>
>> On 2026/6/2 12:34, Lance Yang wrote:
>>> From: Lance Yang <lance.yang@linux.dev>
>>>
>>> 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.
>
> 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.
>>>
>>> Return SHRINK_EMPTY for an empty deferred split list, so the bit can be
>>> cleared once the queue has drained.
>>>
>>> Signed-off-by: Lance Yang <lance.yang@linux.dev>
>>> ---
>>> mm/huge_memory.c | 5 ++++-
>>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>>> index 72f6caf0fec6..62d598290c3b 100644
>>> --- a/mm/huge_memory.c
>>> +++ b/mm/huge_memory.c
>>> @@ -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)
>>
>
> This is against Johannes' work, right?
Yep, I noticed it there, but the behavior is older.
> 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
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 12:47 ` Lance Yang
@ 2026-06-02 17:33 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-02 17:33 UTC (permalink / raw)
To: Lance Yang, akpm, hannes
Cc: ljs, shakeel.butt, mhocko, david, roman.gushchin, muchun.song,
qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas, vbabka, ryncsn,
zaslonko, gor, wangkefeng.wang, baolin.wang, baohua, dev.jain,
npache, ryan.roberts, cgroups, linux-mm, linux-kernel
On 6/2/26 14:47, Lance Yang wrote:
>
>
> On 2026/6/2 20:11, David Hildenbrand (Arm) wrote:
>> On 6/2/26 06:38, Lance Yang wrote:
>>> 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,
> 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.
>
>>>
>>
>> This is against Johannes' work, right?
>
> Yep, I noticed it there, but the behavior is older.
>
>> 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! :)
Reviewed-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 4:34 [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain Lance Yang
2026-06-02 4:38 ` Lance Yang
@ 2026-06-02 10:30 ` Usama Arif
2026-06-02 20:37 ` Andrew Morton
2 siblings, 0 replies; 9+ messages in thread
From: Usama Arif @ 2026-06-02 10:30 UTC (permalink / raw)
To: Lance Yang, akpm
Cc: david, ljs, shakeel.butt, mhocko, david, roman.gushchin,
muchun.song, qi.zheng, yosry.ahmed, ziy, liam, kas, vbabka,
ryncsn, zaslonko, gor, wangkefeng.wang, baolin.wang, baohua,
dev.jain, npache, ryan.roberts, cgroups, linux-mm, linux-kernel
On 02/06/2026 05:34, Lance Yang wrote:
> From: Lance Yang <lance.yang@linux.dev>
>
> 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.
>
> Signed-off-by: Lance Yang <lance.yang@linux.dev>
Same as slab, workingset and others.
Acked-by: Usama Arif <usama.arif@linux.dev>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 4:34 [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain Lance Yang
2026-06-02 4:38 ` Lance Yang
2026-06-02 10:30 ` Usama Arif
@ 2026-06-02 20:37 ` Andrew Morton
2026-06-03 2:00 ` Lance Yang
2 siblings, 1 reply; 9+ messages in thread
From: Andrew Morton @ 2026-06-02 20:37 UTC (permalink / raw)
To: Lance Yang
Cc: david, ljs, shakeel.butt, mhocko, david, roman.gushchin,
muchun.song, qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas,
vbabka, ryncsn, zaslonko, gor, wangkefeng.wang, baolin.wang,
baohua, dev.jain, npache, ryan.roberts, cgroups, linux-mm,
linux-kernel, Johannes Weiner
On Tue, 2 Jun 2026 12:34:53 +0800 Lance Yang <lance.yang@linux.dev> wrote:
> From: Lance Yang <lance.yang@linux.dev>
>
> 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.
>
> Signed-off-by: Lance Yang <lance.yang@linux.dev>
> ---
> mm/huge_memory.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 72f6caf0fec6..62d598290c3b 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -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)
Should this be handled as a fix against hannes's "mm: switch deferred
split shrinker to list_lru"?
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-02 20:37 ` Andrew Morton
@ 2026-06-03 2:00 ` Lance Yang
2026-06-03 8:02 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 9+ messages in thread
From: Lance Yang @ 2026-06-03 2:00 UTC (permalink / raw)
To: Andrew Morton
Cc: david, ljs, shakeel.butt, mhocko, david, roman.gushchin,
muchun.song, qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas,
vbabka, ryncsn, zaslonko, gor, wangkefeng.wang, baolin.wang,
baohua, dev.jain, npache, ryan.roberts, cgroups, linux-mm,
linux-kernel, Johannes Weiner
On 2026/6/3 04:37, Andrew Morton wrote:
> On Tue, 2 Jun 2026 12:34:53 +0800 Lance Yang <lance.yang@linux.dev> wrote:
>
>> From: Lance Yang <lance.yang@linux.dev>
>>
>> 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.
>>
>> Signed-off-by: Lance Yang <lance.yang@linux.dev>
>> ---
>> mm/huge_memory.c | 5 ++++-
>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>> index 72f6caf0fec6..62d598290c3b 100644
>> --- a/mm/huge_memory.c
>> +++ b/mm/huge_memory.c
>> @@ -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)
>
> 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/63797977-1c18-4885-8099-f5c21c80da39@linux.dev/
Cheers, Lance
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/1] mm/thp: clear deferred split shrinker bits when queues drain
2026-06-03 2:00 ` Lance Yang
@ 2026-06-03 8:02 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-06-03 8:02 UTC (permalink / raw)
To: Lance Yang, Andrew Morton
Cc: ljs, shakeel.butt, mhocko, david, roman.gushchin, muchun.song,
qi.zheng, yosry.ahmed, ziy, liam, usama.arif, kas, vbabka, ryncsn,
zaslonko, gor, wangkefeng.wang, baolin.wang, baohua, dev.jain,
npache, ryan.roberts, cgroups, linux-mm, linux-kernel,
Johannes Weiner
On 6/3/26 04:00, Lance Yang wrote:
>
>
> On 2026/6/3 04:37, Andrew Morton wrote:
>> On Tue, 2 Jun 2026 12:34:53 +0800 Lance Yang <lance.yang@linux.dev> wrote:
>>
>>> From: Lance Yang <lance.yang@linux.dev>
>>>
>>> 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.
>>>
>>> Signed-off-by: Lance Yang <lance.yang@linux.dev>
>>> ---
>>> mm/huge_memory.c | 5 ++++-
>>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>>> index 72f6caf0fec6..62d598290c3b 100644
>>> --- a/mm/huge_memory.c
>>> +++ b/mm/huge_memory.c
>>> @@ -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)
>>
>> 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 :)
Right, I conclude that this can be a separate patch on top.
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread