* [PATCH v1 0/2] mm: split underused anonymous mTHP folios
@ 2026-07-07 20:17 Joanne Koong
2026-07-07 20:17 ` [PATCH v1 1/2] mm/huge_memory: extend thp_underused() to " Joanne Koong
` (2 more replies)
0 siblings, 3 replies; 14+ 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
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. This is compatible with the mTHP collapse threshold
at both of the values mTHP collapse supports (0 and
KHUGEPAGED_MAX_PTES_LIMIT). If/when collapse supports intermediate values in
the future, the thresholds can be unified. There is no functional change for
PMD-sized folios.
Patch 2 - adds anonymous mTHP folios to the deferred split list from the
anonymous fault / collapse path so the shrinker can find and split them. This
is the same mechanism PMD THPs use, extended to mTHP. It places more folios
on the shrinker's list, but a folio that the shrinker determines is not
underused gets removed from the list and doesn't get rescanned in the future
unless the folio later becomes partially mapped. The logic to determine
whether a folio is underused or not (thp_underused()) is bounded work that
does a memcmp on at most nr_pages subpages, and runs only if shrink_underused
is on.
Testing:
This was sanity-checked by running a VM with 64K mTHP enabled (PMD THP
disabled, shrink_underused = 1, khugepaged/max_ptes_none=0) and having a
process fault in 256MB of mapped mostly zero-filled 64K mTHP inside a cgroup
with swap disabled, and then triggering reclaim (via memory.reclaim).
- /sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/anon_fault_alloc
showed that the mTHP were allocated during fault handling
- /sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/split and
/proc/vmstat's thp_underused_split_page each increased by 4096
- the process's resident set size stat (/proc/<pid>/status) showed a drop
from 257 MB to 17 MB
The scanning in patch 2 was checked on the same setup with 256 MB of fully
populated 64k mTHP folios. All 4096 folios were processed by the deferred
split shrinker only once and did not reappear on the list in future scans.
Joanne Koong (2):
mm/huge_memory: extend thp_underused() to mTHP folios
mm/memory: add anonymous mTHP folios to deferred split list
mm/huge_memory.c | 12 ++++++++----
mm/memory.c | 2 ++
2 files changed, 10 insertions(+), 4 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 14+ messages in thread* [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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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-29 12:36 ` David Hildenbrand (Arm) 2026-07-08 17:52 ` Joanne Koong 1 sibling, 1 reply; 14+ 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] 14+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list 2026-07-08 9:58 ` Usama Arif @ 2026-07-29 12:36 ` David Hildenbrand (Arm) 2026-07-29 13:43 ` Usama Arif 0 siblings, 1 reply; 14+ messages in thread From: David Hildenbrand (Arm) @ 2026-07-29 12:36 UTC (permalink / raw) To: Usama Arif, 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 7/8/26 11:58, Usama Arif wrote: > > > 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. That doesn't really help lock contention. And the lock contention is indeed a thing, as we now also want to enable the LRU cache for smaller large folios, which improves performance: https://lore.kernel.org/r/20260709081536.82768-1-baohua@kernel.org > > 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? I don't see a problem with large large folios, only with small large folios :) > > 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. Can you elaborate how this change helps here? I assume you want to enable mTHP in production. Which sizes? large large ones? :) -- Cheers, David ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list 2026-07-29 12:36 ` David Hildenbrand (Arm) @ 2026-07-29 13:43 ` Usama Arif 2026-07-29 14:02 ` Usama Arif 0 siblings, 1 reply; 14+ messages in thread From: Usama Arif @ 2026-07-29 13:43 UTC (permalink / raw) To: David Hildenbrand (Arm), Joanne Koong, akpm, ljs, Johannes Weiner Cc: alex, ziy, baolin.wang, liam, npache, ryan.roberts, dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy, linux-mm On 29/07/2026 13:36, David Hildenbrand (Arm) wrote: > On 7/8/26 11:58, Usama Arif wrote: >> >> >> 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. > > That doesn't really help lock contention. And the lock contention is indeed a > thing, as we now also want to enable the LRU cache for smaller large folios, > which improves performance: > > https://lore.kernel.org/r/20260709081536.82768-1-baohua@kernel.org > >> >> 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? > > I don't see a problem with large large folios, only with small large folios :) > >> >> 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. > > Can you elaborate how this change helps here? I assume you want to enable mTHP > in production. Which sizes? large large ones? :) > One of the differences in our production between x86 and ARM hosts which we think should perform at a similar level (but currently aren't) is TLB misses. On x86, we have 2M THP set to always. On ARM, we have base page size of 64K. 2M mTHPs should help bridge the gap due to TLB coalescing. We can't really set 2M mTHP to always as without the mTHP shrinker we will end up with severe memory pressure and a significant increase in OOMs. We don't plan to enable other mTHP sizes on ARM, only 2M. The series by Barry is for folios above costly order, so won't impact this. David, Joanne, Johannes: what do you think about only adding folios that are larger than COSTLY_ORDER to deferred split list? ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list 2026-07-29 13:43 ` Usama Arif @ 2026-07-29 14:02 ` Usama Arif 0 siblings, 0 replies; 14+ messages in thread From: Usama Arif @ 2026-07-29 14:02 UTC (permalink / raw) To: David Hildenbrand (Arm), Joanne Koong, akpm, ljs, Johannes Weiner Cc: alex, ziy, baolin.wang, liam, npache, ryan.roberts, dev.jain, baohua, lance.yang, vbabka, rppt, surenb, mhocko, willy, linux-mm On 29/07/2026 14:43, Usama Arif wrote: > > > On 29/07/2026 13:36, David Hildenbrand (Arm) wrote: >> On 7/8/26 11:58, Usama Arif wrote: >>> >>> >>> 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. >> >> That doesn't really help lock contention. And the lock contention is indeed a >> thing, as we now also want to enable the LRU cache for smaller large folios, >> which improves performance: >> >> https://lore.kernel.org/r/20260709081536.82768-1-baohua@kernel.org >> >>> >>> 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? >> >> I don't see a problem with large large folios, only with small large folios :) >> >>> >>> 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. >> >> Can you elaborate how this change helps here? I assume you want to enable mTHP >> in production. Which sizes? large large ones? :) >> > > One of the differences in our production between x86 and ARM hosts which we think > should perform at a similar level (but currently aren't) is TLB misses. > > On x86, we have 2M THP set to always. > On ARM, we have base page size of 64K. 2M mTHPs should help bridge the gap due to > TLB coalescing. We can't really set 2M mTHP to always as without the mTHP shrinker > we will end up with severe memory pressure and a significant increase in OOMs. > > We don't plan to enable other mTHP sizes on ARM, only 2M. > > The series by Barry is for folios above costly order, so won't impact this. s/above/below/ > > David, Joanne, Johannes: what do you think about only adding folios that are larger > than COSTLY_ORDER to deferred split list? ^ permalink raw reply [flat|nested] 14+ 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 2026-07-29 12:38 ` David Hildenbrand (Arm) 1 sibling, 1 reply; 14+ 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] 14+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list 2026-07-08 17:52 ` Joanne Koong @ 2026-07-29 12:38 ` David Hildenbrand (Arm) 2026-07-29 15:04 ` Johannes Weiner 0 siblings, 1 reply; 14+ messages in thread From: David Hildenbrand (Arm) @ 2026-07-29 12:38 UTC (permalink / raw) To: Joanne Koong 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, Johannes Weiner On 7/8/26 19:52, Joanne Koong wrote: > 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? Sorry for the late reply. Unfortunately, it wasn't clear yet if we could reporpuse the LRU, whereby we would just naturally benefit from the LRU cache (soon) and only manage pages on a single list. The concern was that we might end up scanning many items on the LRU to detect splitting candidates. I am not 100% sure if that is a real problem. As raised during the last THP cabal, my gut feeling is that Johannes might have an idea on how to improve things here. -- Cheers, David ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v1 2/2] mm/memory: add anonymous mTHP folios to deferred split list 2026-07-29 12:38 ` David Hildenbrand (Arm) @ 2026-07-29 15:04 ` Johannes Weiner 0 siblings, 0 replies; 14+ messages in thread From: Johannes Weiner @ 2026-07-29 15:04 UTC (permalink / raw) To: David Hildenbrand (Arm) Cc: Joanne Koong, 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 29, 2026 at 02:38:53PM +0200, David Hildenbrand (Arm) wrote: > On 7/8/26 19:52, Joanne Koong wrote: > > 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? > > Sorry for the late reply. Unfortunately, it wasn't clear yet if we could > reporpuse the LRU, whereby we would just naturally benefit from the LRU cache > (soon) and only manage pages on a single list. > > The concern was that we might end up scanning many items on the LRU to detect > splitting candidates. > > I am not 100% sure if that is a real problem. > > As raised during the last THP cabal, my gut feeling is that Johannes might have > an idea on how to improve things here. I've been trying to reconstruct all the details we talked about at LSFMM ;) Going over this again, I still have to conclude using the anon LRU for splitting is not a good idea. Let me try to lay it out: (1) The anon LRU isn't scanned at all when there is no swap. This is fixable, but requires some re-architecting of the vmscan stack. (2) With a mix of basepages and THPs, there could indeed be a lot of basepages ahead of underused THPs. That means swapping before getting to space that is much cheaper to reclaim. The current setup isn't perfect in that regard, as the shrinker runs simultaneously as the LRU. But it's making guaranteed forward progress through the THPs, even as the first LRU pages are scanned. (3) The anon LRU has folio lifetime, but the splitqueue is one-shot: we scan each THP once, and then it's either split and dropped, or found full and dropped. That THP never needs to be revisited. The queue actually empties as the workload establishes itself. The anon LRU ~ splitqueue argument is only true around startup. If we used the anon LRU, we'd need per-page state to avoid repeat underused checks. And we need external state to not scan the anon LRU at all if there are no new THPs (and no swap). And if that's just a counter for "new, not yet scanned THPs", a single fault will cause you to walk the entire anon LRU before you get to it. (4) The anon LRU is driven based on the cost of swap and observed refaults. These metrics are inherently bad modulators for scanning underused THP space. Using the anon LRU for splits means that if anon scanning slows down and we lean more on the file cache, we'd also slow down the search for unused THP space. This is undesirable. File cache is still more valuable than uninitialized anonymous memory: | anon | file | uTHP | ----------------+--------------------+ cost to reclaim | 1 | 0 | 0 | ----------------+--------------------+ cost to refault | 1 | 1 | 0 | We're thinking anon LRU because those splittable, potentially underused THPs happen to be anon. But anon user data that needs to be swapped out is an inherently different class of reclaim targets than the uninitialized space *between* such anon user data. We really want uTHP -> clean cache -> swap reclaim ordering. Classic LRU takes this even further. Because of how the page cache grows endlessly compared to heap memory, classic will scan *only* the file LRU until those pages start refaulting. Using the anon LRU would get us a clean cache -> swap / uTHP ordering. So I think reusing the anon LRU is flawed. It's fundamentally different needles in fundamentally different haystacks. If we can agree on that, then the lock contention problem has a different scope as well: it's a simple optimization issue, not a fundamental data structure arrangement issue. If I understand you correctly, the concern is that people will enable all manner of mTHP orders, and 99% of the anon faults, including all the order-3, order-4 pagelets, will go through the list_lru lock on fault, with no batching. I do think that's valid, but how concrete is that right now? Is anyone actually doing that? I would have some concerns purely from a servability POV: the page allocator, watermarking, compaction etc. are still a bottle neck for high rates of lower orders, as we've been noticing with the page cache and the optimistic vmalloc higher orders. The concrete proposal I've seen from several places was much simpler: I have ARM 64k basepages and I want 2M THPs. But in that case, the splitqueue looks no different than on x86 today. My take is that we should add mTHPs to the split queue as-is. Deal with the locking/batching concern when real usecases say we should. ^ permalink raw reply [flat|nested] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ messages in thread
end of thread, other threads:[~2026-07-29 15:04 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 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-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:56 ` David Hildenbrand (Arm) 2026-07-08 9:58 ` Usama Arif 2026-07-29 12:36 ` David Hildenbrand (Arm) 2026-07-29 13:43 ` Usama Arif 2026-07-29 14:02 ` Usama Arif 2026-07-08 17:52 ` Joanne Koong 2026-07-29 12:38 ` David Hildenbrand (Arm) 2026-07-29 15:04 ` Johannes Weiner 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
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).