* [PATCH v1 1/2] mm/huge_memory: extend thp_underused() to mTHP folios
2026-07-07 20:17 [PATCH v1 0/2] mm: split underused anonymous mTHP folios Joanne Koong
@ 2026-07-07 20:17 ` Joanne Koong
2026-07-08 4:03 ` Joanne Koong
2026-07-07 20:17 ` [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list Joanne Koong
2026-07-08 7:46 ` [PATCH v1 0/2] mm: split underused anonymous mTHP folios David Hildenbrand (Arm)
2 siblings, 1 reply; 9+ messages in thread
From: Joanne Koong @ 2026-07-07 20:17 UTC (permalink / raw)
To: akpm, david, ljs
Cc: usama.arif, alex, ziy, baolin.wang, liam, npache, ryan.roberts,
dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy,
linux-mm
thp_underused() decides whether a large folio on the deferred split
list is underused and should be split so that its zero subpages can be
reclaimed. However, the logic in it only accounts for PMD-sized folios.
As preparatory work for splitting underused mTHP folios, make the logic
in thp_underused() compatible with mTHP-sized folios. This uses the
existing khugepaged_max_ptes_none sysctl value and scales it
proportionally to the size of the folio as the threshold for how many
zero-filled pages a folio may contain before it's considered underused.
This introduces no functional changes for PMD-size folios.
Suggested-by: Usama Arif <usama.arif@linux.dev>
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
mm/huge_memory.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c0892cc533a9..ea5c350b4818 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4413,24 +4413,28 @@ static unsigned long deferred_split_count(struct shrinker *shrink,
static bool thp_underused(struct folio *folio)
{
int num_zero_pages = 0, num_filled_pages = 0;
+ int nr_pages = folio_nr_pages(folio);
+ unsigned int max_ptes_none;
int i;
- if (khugepaged_max_ptes_none == HPAGE_PMD_NR - 1)
+ max_ptes_none = khugepaged_max_ptes_none * nr_pages / HPAGE_PMD_NR;
+
+ if (max_ptes_none >= nr_pages - 1)
return false;
if (folio_contain_hwpoisoned_page(folio))
return false;
- for (i = 0; i < folio_nr_pages(folio); i++) {
+ for (i = 0; i < nr_pages; i++) {
if (pages_identical(folio_page(folio, i), ZERO_PAGE(0))) {
- if (++num_zero_pages > khugepaged_max_ptes_none)
+ if (++num_zero_pages > max_ptes_none)
return true;
} else {
/*
* Another path for early exit once the number
* of non-zero filled pages exceeds threshold.
*/
- if (++num_filled_pages >= HPAGE_PMD_NR - khugepaged_max_ptes_none)
+ if (++num_filled_pages >= nr_pages - max_ptes_none)
return false;
}
}
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v1 1/2] mm/huge_memory: extend thp_underused() to mTHP folios
2026-07-07 20:17 ` [PATCH v1 1/2] mm/huge_memory: extend thp_underused() to " Joanne Koong
@ 2026-07-08 4:03 ` Joanne Koong
0 siblings, 0 replies; 9+ messages in thread
From: Joanne Koong @ 2026-07-08 4:03 UTC (permalink / raw)
To: akpm, david, ljs
Cc: usama.arif, alex, ziy, baolin.wang, liam, npache, ryan.roberts,
dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy,
linux-mm
On Tue, Jul 7, 2026 at 1:18 PM Joanne Koong <joannelkoong@gmail.com> wrote:
>
> thp_underused() decides whether a large folio on the deferred split
> list is underused and should be split so that its zero subpages can be
> reclaimed. However, the logic in it only accounts for PMD-sized folios.
>
> As preparatory work for splitting underused mTHP folios, make the logic
> in thp_underused() compatible with mTHP-sized folios. This uses the
> existing khugepaged_max_ptes_none sysctl value and scales it
> proportionally to the size of the folio as the threshold for how many
> zero-filled pages a folio may contain before it's considered underused.
>
> This introduces no functional changes for PMD-size folios.
>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> ---
> mm/huge_memory.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index c0892cc533a9..ea5c350b4818 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -4413,24 +4413,28 @@ static unsigned long deferred_split_count(struct shrinker *shrink,
> static bool thp_underused(struct folio *folio)
> {
> int num_zero_pages = 0, num_filled_pages = 0;
> + int nr_pages = folio_nr_pages(folio);
> + unsigned int max_ptes_none;
> int i;
>
> - if (khugepaged_max_ptes_none == HPAGE_PMD_NR - 1)
> + max_ptes_none = khugepaged_max_ptes_none * nr_pages / HPAGE_PMD_NR;
> +
> + if (max_ptes_none >= nr_pages - 1)
> return false;
Andrew kindly directed me to a comment by Sashiko [1]:
"Does this scale calculation unintentionally disable underused splitting for
small mTHPs with valid sysctl settings?
Due to integer division truncation, max_ptes_none >= nr_pages - 1 can match
configurations that aren't the sysctl sentinel (HPAGE_PMD_NR - 1).
...
Should the sentinel check remain against the raw
khugepaged_max_ptes_none value before scaling?"
Sashiko is right. I'll fix this up in v2 and keep the original
khugepaged_max_ptes_none == HPAGE_PMD_NR - 1 check on the raw
non-scaled value.
Thanks,
Joanne
[1] https://sashiko.dev/#/patchset/20260707201735.4113107-1-joannelkoong@gmail.com
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list
2026-07-07 20:17 [PATCH v1 0/2] mm: split underused anonymous mTHP folios Joanne Koong
2026-07-07 20:17 ` [PATCH v1 1/2] mm/huge_memory: extend thp_underused() to " Joanne Koong
@ 2026-07-07 20:17 ` Joanne Koong
2026-07-08 7:56 ` David Hildenbrand (Arm)
2026-07-08 7:46 ` [PATCH v1 0/2] mm: split underused anonymous mTHP folios David Hildenbrand (Arm)
2 siblings, 1 reply; 9+ messages in thread
From: Joanne Koong @ 2026-07-07 20:17 UTC (permalink / raw)
To: akpm, david, ljs
Cc: usama.arif, alex, ziy, baolin.wang, liam, npache, ryan.roberts,
dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy,
linux-mm
Unlike for PMD-sized folios, an anonymous mTHP folio doesn't get added
to the deferred split list at fault or collapse time. As a result, a
fully mapped mTHP folio that is mostly zero-filled doesn't get split by
the deferred split shrinker when the system is under memory pressure.
Add anonymous mTHP folios to the deferred split list so that if there's
memory pressure, a zero-filled mTHP can be split with its zero pages
remapped to the shared zero page and then reclaimed.
To minimize overhead on the common order-0 fault path, the
deferred_split_folio() call is guarded by an inline folio_test_large()
check.
Suggested-by: Usama Arif <usama.arif@linux.dev>
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
mm/memory.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/mm/memory.c b/mm/memory.c
index 6637c5b13c9b..441d918e3dc0 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -5259,6 +5259,8 @@ void map_anon_folio_pte_nopf(struct folio *folio, pte_t *pte,
folio_add_lru_vma(folio, vma);
set_ptes(vma->vm_mm, addr, pte, entry, nr_pages);
update_mmu_cache_range(NULL, vma, addr, pte, nr_pages);
+ if (folio_test_large(folio))
+ deferred_split_folio(folio, false);
}
static void map_anon_folio_pte_pf(struct folio *folio, pte_t *pte,
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list
2026-07-07 20:17 ` [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list Joanne Koong
@ 2026-07-08 7:56 ` David Hildenbrand (Arm)
2026-07-08 9:58 ` Usama Arif
2026-07-08 17:52 ` Joanne Koong
0 siblings, 2 replies; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-08 7:56 UTC (permalink / raw)
To: Joanne Koong, akpm, ljs
Cc: usama.arif, alex, ziy, baolin.wang, liam, npache, ryan.roberts,
dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy,
linux-mm
On 7/7/26 22:17, Joanne Koong wrote:
> Unlike for PMD-sized folios, an anonymous mTHP folio doesn't get added
> to the deferred split list at fault or collapse time. As a result, a
> fully mapped mTHP folio that is mostly zero-filled doesn't get split by
> the deferred split shrinker when the system is under memory pressure.
>
> Add anonymous mTHP folios to the deferred split list so that if there's
> memory pressure, a zero-filled mTHP can be split with its zero pages
> remapped to the shared zero page and then reclaimed.
>
> To minimize overhead on the common order-0 fault path, the
> deferred_split_folio() call is guarded by an inline folio_test_large()
> check.
>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> ---
> mm/memory.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/mm/memory.c b/mm/memory.c
> index 6637c5b13c9b..441d918e3dc0 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -5259,6 +5259,8 @@ void map_anon_folio_pte_nopf(struct folio *folio, pte_t *pte,
> folio_add_lru_vma(folio, vma);
> set_ptes(vma->vm_mm, addr, pte, entry, nr_pages);
> update_mmu_cache_range(NULL, vma, addr, pte, nr_pages);
> + if (folio_test_large(folio))
> + deferred_split_folio(folio, false);
> }
>
> static void map_anon_folio_pte_pf(struct folio *folio, pte_t *pte,
I had a session [1] at LSF/MM about having essentially all large anon folios
part of the the deferred split queue.
(1) I don't think this scales.
(2) I suspect the shrinker should make smarter decisions of what to scan/reclaim
first.
I think this needs more proper thought.
[1]
https://docs.google.com/presentation/d/1RfKWCY1AMVns-WLn-QdAWbI2a-rA7fbFyh7XD1Wn5BY/edit?usp=sharing
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list
2026-07-08 7:56 ` David Hildenbrand (Arm)
@ 2026-07-08 9:58 ` Usama Arif
2026-07-08 17:52 ` Joanne Koong
1 sibling, 0 replies; 9+ messages in thread
From: Usama Arif @ 2026-07-08 9:58 UTC (permalink / raw)
To: David Hildenbrand (Arm), Joanne Koong, akpm, ljs
Cc: alex, ziy, baolin.wang, liam, npache, ryan.roberts, dev.jain,
baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy, linux-mm
On 08/07/2026 08:56, David Hildenbrand (Arm) wrote:
> On 7/7/26 22:17, Joanne Koong wrote:
>> Unlike for PMD-sized folios, an anonymous mTHP folio doesn't get added
>> to the deferred split list at fault or collapse time. As a result, a
>> fully mapped mTHP folio that is mostly zero-filled doesn't get split by
>> the deferred split shrinker when the system is under memory pressure.
>>
>> Add anonymous mTHP folios to the deferred split list so that if there's
>> memory pressure, a zero-filled mTHP can be split with its zero pages
>> remapped to the shared zero page and then reclaimed.
>>
>> To minimize overhead on the common order-0 fault path, the
>> deferred_split_folio() call is guarded by an inline folio_test_large()
>> check.
>>
>> Suggested-by: Usama Arif <usama.arif@linux.dev>
>> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
>> ---
>> mm/memory.c | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>> diff --git a/mm/memory.c b/mm/memory.c
>> index 6637c5b13c9b..441d918e3dc0 100644
>> --- a/mm/memory.c
>> +++ b/mm/memory.c
>> @@ -5259,6 +5259,8 @@ void map_anon_folio_pte_nopf(struct folio *folio, pte_t *pte,
>> folio_add_lru_vma(folio, vma);
>> set_ptes(vma->vm_mm, addr, pte, entry, nr_pages);
>> update_mmu_cache_range(NULL, vma, addr, pte, nr_pages);
>> + if (folio_test_large(folio))
>> + deferred_split_folio(folio, false);
>> }
>>
>> static void map_anon_folio_pte_pf(struct folio *folio, pte_t *pte,
>
> I had a session [1] at LSF/MM about having essentially all large anon folios
> part of the the deferred split queue.
>
> (1) I don't think this scales.
>
What if we only add the mTHPs for which the policy is set to always.
I think it doesn't scale if someone sets all orders to always, but if you only
set the 2M mTHP sysfs to always, it should be the same as what we have today
for PMD order?
The main motivation for this series is to try and bring the performance for
ARM on par with x86. One of the differences is TLB misses. I imagine the lower
churn in kernel by using larger page sizes will help as well. But we will
run into OOMs without the shrinker.
> (2) I suspect the shrinker should make smarter decisions of what to scan/reclaim
> first.
Yes definitely agree!
Alexandre is looking into this as well.
>
> I think this needs more proper thought.
>
> [1]
> https://docs.google.com/presentation/d/1RfKWCY1AMVns-WLn-QdAWbI2a-rA7fbFyh7XD1Wn5BY/edit?usp=sharing
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list
2026-07-08 7:56 ` David Hildenbrand (Arm)
2026-07-08 9:58 ` Usama Arif
@ 2026-07-08 17:52 ` Joanne Koong
1 sibling, 0 replies; 9+ messages in thread
From: Joanne Koong @ 2026-07-08 17:52 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: akpm, ljs, usama.arif, alex, ziy, baolin.wang, liam, npache,
ryan.roberts, dev.jain, baohua, lance.yang, vbabka, rppt, surenb,
mhocko, willy, linux-mm
On Wed, Jul 8, 2026 at 12:56 AM David Hildenbrand (Arm)
<david@kernel.org> wrote:
>
> On 7/7/26 22:17, Joanne Koong wrote:
> > Unlike for PMD-sized folios, an anonymous mTHP folio doesn't get added
> > to the deferred split list at fault or collapse time. As a result, a
> > fully mapped mTHP folio that is mostly zero-filled doesn't get split by
> > the deferred split shrinker when the system is under memory pressure.
> >
> > Add anonymous mTHP folios to the deferred split list so that if there's
> > memory pressure, a zero-filled mTHP can be split with its zero pages
> > remapped to the shared zero page and then reclaimed.
> >
> > To minimize overhead on the common order-0 fault path, the
> > deferred_split_folio() call is guarded by an inline folio_test_large()
> > check.
> >
> > Suggested-by: Usama Arif <usama.arif@linux.dev>
> > Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> > ---
> > mm/memory.c | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/mm/memory.c b/mm/memory.c
> > index 6637c5b13c9b..441d918e3dc0 100644
> > --- a/mm/memory.c
> > +++ b/mm/memory.c
> > @@ -5259,6 +5259,8 @@ void map_anon_folio_pte_nopf(struct folio *folio, pte_t *pte,
> > folio_add_lru_vma(folio, vma);
> > set_ptes(vma->vm_mm, addr, pte, entry, nr_pages);
> > update_mmu_cache_range(NULL, vma, addr, pte, nr_pages);
> > + if (folio_test_large(folio))
> > + deferred_split_folio(folio, false);
> > }
> >
> > static void map_anon_folio_pte_pf(struct folio *folio, pte_t *pte,
>
> I had a session [1] at LSF/MM about having essentially all large anon folios
> part of the the deferred split queue.
>
> (1) I don't think this scales.
>
> (2) I suspect the shrinker should make smarter decisions of what to scan/reclaim
> first.
>
> I think this needs more proper thought.
>
> [1]
> https://docs.google.com/presentation/d/1RfKWCY1AMVns-WLn-QdAWbI2a-rA7fbFyh7XD1Wn5BY/edit?usp=sharing
Thanks for the link to the slides! Was there a conclusion from the
LSF/MM discussion about the future path forward for deferred splitting
or is that still being determined?
Thanks,
Joanne
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 0/2] mm: split underused anonymous mTHP folios
2026-07-07 20:17 [PATCH v1 0/2] mm: split underused anonymous mTHP folios Joanne Koong
2026-07-07 20:17 ` [PATCH v1 1/2] mm/huge_memory: extend thp_underused() to " Joanne Koong
2026-07-07 20:17 ` [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list Joanne Koong
@ 2026-07-08 7:46 ` David Hildenbrand (Arm)
2026-07-08 18:19 ` Joanne Koong
2 siblings, 1 reply; 9+ messages in thread
From: David Hildenbrand (Arm) @ 2026-07-08 7:46 UTC (permalink / raw)
To: Joanne Koong, akpm, ljs
Cc: usama.arif, alex, ziy, baolin.wang, liam, npache, ryan.roberts,
dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy,
linux-mm
On 7/7/26 22:17, Joanne Koong wrote:
> PMD-sized THPs that are mostly zero-filled are reclaimed under memory pressure
> by the deferred split shrinker, but this is not done for mTHP folios. At Meta,
> we would like to deploy 2M THP=always on ARM (with 64k base pages), as 2M
> provides contpte benefits and 512M is too big. However, 2M THP=always will
> result in a memory regression unless the extra unused portions of THPs can be
> broken down and reclaimed.
>
> This series has two patches:
> Patch 1 - makes thp_underused() work for non-PMD-sized folios by scaling
> khugepaged_max_ptes_none proportionally to the folio size and using that
> as the threshold for how many zero-filled pages a folio can contain before
> it's considered underused.
Did you look at the khugepaged side, and how we handle it there?
--
Cheers,
David
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 0/2] mm: split underused anonymous mTHP folios
2026-07-08 7:46 ` [PATCH v1 0/2] mm: split underused anonymous mTHP folios David Hildenbrand (Arm)
@ 2026-07-08 18:19 ` Joanne Koong
0 siblings, 0 replies; 9+ messages in thread
From: Joanne Koong @ 2026-07-08 18:19 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: akpm, ljs, usama.arif, alex, ziy, baolin.wang, liam, npache,
ryan.roberts, dev.jain, baohua, lance.yang, vbabka, rppt, surenb,
mhocko, willy, linux-mm
On Wed, Jul 8, 2026 at 12:47 AM David Hildenbrand (Arm)
<david@kernel.org> wrote:
>
> On 7/7/26 22:17, Joanne Koong wrote:
> > PMD-sized THPs that are mostly zero-filled are reclaimed under memory pressure
> > by the deferred split shrinker, but this is not done for mTHP folios. At Meta,
> > we would like to deploy 2M THP=always on ARM (with 64k base pages), as 2M
> > provides contpte benefits and 512M is too big. However, 2M THP=always will
> > result in a memory regression unless the extra unused portions of THPs can be
> > broken down and reclaimed.
> >
> > This series has two patches:
> > Patch 1 - makes thp_underused() work for non-PMD-sized folios by scaling
> > khugepaged_max_ptes_none proportionally to the folio size and using that
> > as the threshold for how many zero-filled pages a folio can contain before
> > it's considered underused.
> Did you look at the khugepaged side, and how we handle it there?
I'm not sure if I'm interpreting your suggestion correctly - is your
suggestion about using the khugepaged mTHP collapse threshold in
collapse_max_ptes_none()? I had looked at Nico's patchset in [1] and
my understanding of it was that the mTHP collapse threshold currently
only supports values of 0 and HPAGE_PMD_NR - 1. The patch 1 changes
use khugepaged_max_ptes_none's value as a proportional per-order
threshold for mTHP because 0 and HPAGE_PMD_NR - 1 are too extreme for
our use case where we're trying to find a middle ground that keeps
mostly-populated 2M folios whole but splits mostly zero-filled ones.
Is that what you had in mind or were you pointing at something else on
the khugepaged side?
Thanks,
Joanne
[1] https://lore.kernel.org/linux-mm/20260605161422.213817-1-npache@redhat.com/
^ permalink raw reply [flat|nested] 9+ messages in thread