mm: improve large folio readahead for exec memory
Subsystems (3)
v6 cover mm: improve large folio readahead for exec memory
Two checks in do_sync_mmap_readahead() limit large-folio readahead:
1. The mmap_miss heuristic is meant to throttle wasteful speculative readahead. It is currently also applied to the VM_EXEC readahead path, which is targeted rather than speculative. Once mmap_miss exceeds MMAP_LOTSAMISS, exec readahead - including the large-folio order requested by exec_folio_order() - is disabled. On configurations where the mmap_miss decrement paths are not active (see patch 1) the counter only grows, so exec readahead is permanently disabled after the first 100 faults.
2. The force_thp_readahead path is gated only on HPAGE_PMD_ORDER <= MAX_PAGECACHE_ORDER and always drives the readahead at HPAGE_PMD_ORDER. Configurations where HPAGE_PMD_ORDER exceeds MAX_PAGECACHE_ORDER never reach this path, even when the mapping itself supports usefully large folios well below the cap.
Both issues are most visible on arm64 with a 64K base page size, where HPAGE_PMD_ORDER is 13 (512MB) -- above MAX_PAGECACHE_ORDER
(11) -- and where fault_around_pages collapses to 1 disabling should_fault_around() (one of the two mmap_miss decrement sites). However the fixes are architecture-agnostic: patch 1 reflects the nature of VM_EXEC readahead regardless of base page size, and patch 2 generalises the gate so any mapping advertising a usefully large maximum folio order can benefit.
I created a benchmark that mmaps a large executable file madvises it as huge and calls RET-stub functions at PAGE_SIZE offsets across it. "Cold" measures fault + readahead cost. "Random" first faults in all pages with a sequential sweep (not measured), then measures time for calling random offsets, isolating iTLB miss cost for scattered execution.
The benchmark results on Neoverse V2 (Grace), arm64 with 64K base pages, 512MB executable file on ext4, averaged over 3 runs:
Phase | Baseline | Patched | Improvement -----------|--------------|--------------|------------------ Cold fault | 83.4 ms | 41.3 ms | 50% faster Random | 76.0 ms | 58.3 ms | 23% faster
The patches are on top of mm-unstable from 28 May (8a74e22643189e0ae339afc91110ddb4cab1941b) which include patch [1] that make mmap_miss accounting symmetric for VM_SEQ_READ which was pointed out by sashiko in the previous revision.
[1] https://lore.kernel.org/all/[email protected]/
Kiryl and Jan, I have kept your Reviewed-by tags from the previous revision for patch 1 as the concept is still the same, please let me know if that is not ok.
v5 -> v6: https://lore.kernel.org/all/[email protected]/
- Based on top of patch [1] (sashiko)
- Changes to commit message to make it more accurate for patch 1 and skip mmap_miss decrement as well. (sashiko)
- Keep old behaviour if large folio mappings is not enabled (sashiko).
- sashiko pointed to a TOCTOU data race that was pre-existing. My patch could make it worse. Dont make it worse by introducing thp_order local variable.
v3 -> v5: https://lore.kernel.org/all/[email protected]/
- (Looks like I messed up the versioning here and went directly form v3 to v5.)
- Drop patches for elf thp unmapped area alignment and deal with them separately. These patches will just bring folios smaller than PMD
at the same level as PMD. The 2 patches now should be much easier
to merge.
- Tackle size of THP for exec pages at the same point as PMD instead of tackling using exec_folio_order() (Ryan during LSFMM, Thanks!)
v2 -> v3: https://lore.kernel.org/all/[email protected]/
- Take into account READ_ONLY_THP_FOR_FS for elf alignment by aligning to HPAGE_PMD_SIZE limited to 2M (Rui)
- Reviewed-by tags for patch 1 from Kiryl and Jan
- Remove preferred_exec_order() (Jan)
- Change ra->order to HPAGE_PMD_ORDER if vma_pages(vma) >= HPAGE_PMD_NR otherwise use exec_folio_order() with gfp &= ~__GFP_RECLAIM for do_sync_mmap_readahead().
- Change exec_folio_order() to return 2M (cont-pte size) for 64K base page size for arm64.
- remove bprm->file NULL check (Matthew)
- Change filp to file (Matthew)
- Improve checking of p_vaddr and p_vaddr (Rui and Matthew)
v1 -> v2: https://lore.kernel.org/all/[email protected]/
- disable mmap_miss logic for VM_EXEC (Jan Kara)
- Align in elf only when segment VA and file offset are already aligned (Rui)
- preferred_exec_order() for VM_EXEC sync mmap_readahead which takes into account zone high watermarks (as an approximation of memory pressure) (David, or atleast my approach to what David suggested in [1] :))
- Extend max alignment to mapping_max_folio_size() instead of exec_folio_order()
Usama Arif (2):
mm: bypass mmap_miss heuristic for VM_EXEC readahead
mm: use mapping_max_folio_order() for force_thp_readahead order
mm/filemap.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-)
Signature · 1 line
2.52.0
PATCH v6 1/2 mm: bypass mmap_miss heuristic for VM_EXEC readahead
The mmap_miss heuristic is intended to stop speculative mmap readahead when a file looks like a random-access workload. That does not fit the VM_EXEC path very well.
VM_EXEC readahead is already constrained differently from ordinary mmap
read-around: it is bounded by the VMA, uses exec_folio_order() to choose an order useful for executable mappings, and sets async_size to 0 so it does not create follow-on readahead. When VM_HUGEPAGE is also present, the larger readahead is an explicit userspace opt-in.
The mmap_miss counter is decremented from cache-hit paths in do_async_mmap_readahead() and filemap_map_pages(). Those paths are not always enough to balance the synchronous miss increments for executable mappings. In particular, when fault-around is effectively disabled, such as configurations where fault_around_pages is 1, filemap_map_pages() is not reached from the fault path. The counter can then become a stale throttle for VM_EXEC mappings and suppress the readahead behavior that the executable-specific path is trying to provide.
Skip both mmap_miss increments and decrements for VM_EXEC mappings, matching the existing VM_SEQ_READ treatment and keeping the counter accounting symmetric.
--- mm/filemap.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
mm/filemap.c +7 -7
@@ -3339,7 +3339,7 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) } } -if (!(vm_flags &VM_SEQ_READ )) { +if (!(vm_flags & (VM_SEQ_READ |VM_EXEC ))) {/* Avoid banging the cache line if not needed */ mmap_miss =READ_ONCE (ra->mmap_miss);if (mmap_miss <MMAP_LOTSAMISS *10 )
@@ -3434,12 +3434,12 @@ static struct file *do_async_mmap_readahead(struct vm_fault *vmf,* times for a single folio and break the balance with mmap_miss * increase in do_sync_mmap_readahead(). * -* VM_SEQ_READ mappings skip the mmap_miss increment in +* VM_SEQ_READ and VM_EXEC mappings skip the mmap_miss increment in * do_sync_mmap_readahead(), so skip the decrement here as well to * keep the counter symmetric. */ if (likely (!folio_test_locked (folio)) && - !(vmf->vma->vm_flags &VM_SEQ_READ )) { + !(vmf->vma->vm_flags & (VM_SEQ_READ |VM_EXEC ))) { mmap_miss =READ_ONCE (ra->mmap_miss);if (mmap_miss)WRITE_ONCE (ra->mmap_miss, --mmap_miss);
@@ -3941,14 +3941,14 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,* Don't decrease mmap_miss in this scenario to make sure * we can stop read-ahead. * -* VM_SEQ_READ mappings skip the mmap_miss increment in -* do_sync_mmap_readahead(), so skip the decrement here as -* well to keep the counter symmetric. +* VM_SEQ_READ and VM_EXEC mappings skip the mmap_miss +* increment in do_sync_mmap_readahead(), so skip the +* decrement here as well to keep the counter symmetric. */ if ((map_ret &VM_FAULT_NOPAGE ) && !(vmf->flags &FAULT_FLAG_TRIED ) && !folio_test_workingset (folio) && - !(vma->vm_flags &VM_SEQ_READ )) { + !(vma->vm_flags & (VM_SEQ_READ |VM_EXEC ))) {unsigned short mmap_miss; mmap_miss =READ_ONCE (file->f_ra.mmap_miss);
Signature · 1 line
2.52.0
Re: [PATCH v6 1/2] mm: bypass mmap_miss heuristic for VM_EXEC readahead
Quoted 19 lines · Usama Arif
The
mmap_missheuristic is intended to stop speculative mmap readahead when a file looks like a random-access workload. That does not fit theVM_EXECpath very well.
VM_EXECreadahead is already constrained differently from ordinary mmap
read-around: it is bounded by the VMA, usesexec_folio_order()to choose an order useful for executable mappings, and setsasync_sizeto 0 so it does not create follow-on readahead. WhenVM_HUGEPAGEis also present, the larger readahead is an explicit userspace opt-in.The
mmap_misscounter is decremented from cache-hit paths indo_async_mmap_readahead()andfilemap_map_pages(). Those paths are not always enough to balance the synchronous miss increments for executable mappings. In particular, when fault-around is effectively disabled, such as configurations wherefault_around_pagesis 1,filemap_map_pages()is not reached from the fault path. The counter can then become a stale throttle forVM_EXECmappings and suppress the readahead behavior that the executable-specific path is trying to provide.
Quoted 7 lines
Skip both
mmap_missincrements and decrements forVM_EXECmappings, matching the existingVM_SEQ_READtreatment and keeping the counter accounting symmetric.Signed-off-by Usama Arif <[email protected]>Reviewed-by Jan Kara <[email protected]>Reviewed-by Kiryl Shutsemau (Meta) <[email protected]>
This is reasonable, thanks.
Quoted 55 lines
--- mm/filemap.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)mm/filemap.c +7
-7@@ -3339,7 +3339,7 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) } } -if (!(vm_flags &VM_SEQ_READ )) { +if (!(vm_flags & (VM_SEQ_READ |VM_EXEC ))) {/* Avoid banging the cache line if not needed */ mmap_miss =READ_ONCE (ra->mmap_miss);if (mmap_miss <MMAP_LOTSAMISS *10 )@@ -3434,12 +3434,12 @@ static struct file *do_async_mmap_readahead(struct vm_fault *vmf,* times for a single folio and break the balance with mmap_miss * increase in do_sync_mmap_readahead(). * -* VM_SEQ_READ mappings skip the mmap_miss increment in +* VM_SEQ_READ and VM_EXEC mappings skip the mmap_miss increment in * do_sync_mmap_readahead(), so skip the decrement here as well to * keep the counter symmetric. */ if (likely (!folio_test_locked (folio)) && - !(vmf->vma->vm_flags &VM_SEQ_READ )) { + !(vmf->vma->vm_flags & (VM_SEQ_READ |VM_EXEC ))) { mmap_miss =READ_ONCE (ra->mmap_miss);if (mmap_miss)WRITE_ONCE (ra->mmap_miss, --mmap_miss);@@ -3941,14 +3941,14 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,* Don't decrease mmap_miss in this scenario to make sure * we can stop read-ahead. * -* VM_SEQ_READ mappings skip the mmap_miss increment in -* do_sync_mmap_readahead(), so skip the decrement here as -* well to keep the counter symmetric. +* VM_SEQ_READ and VM_EXEC mappings skip the mmap_miss +* increment in do_sync_mmap_readahead(), so skip the +* decrement here as well to keep the counter symmetric. */ if ((map_ret &VM_FAULT_NOPAGE ) && !(vmf->flags &FAULT_FLAG_TRIED ) && !folio_test_workingset (folio) && - !(vma->vm_flags &VM_SEQ_READ )) { + !(vma->vm_flags & (VM_SEQ_READ |VM_EXEC ))) {unsigned short mmap_miss; mmap_miss =READ_ONCE (file->f_ra.mmap_miss);Signature · 1 line
2.52.0
Signature · 1 line
Pedro
PATCH v6 2/2 mm: use mapping_max_folio_order() for force_thp_readahead order
In reply to Usama ArifThe force_thp_readahead path in do_sync_mmap_readahead() is gated on HPAGE_PMD_ORDER <= MAX_PAGECACHE_ORDER and always requests HPAGE_PMD_ORDER / HPAGE_PMD_NR. On configurations where HPAGE_PMD_ORDER exceeds MAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size, VM_HUGEPAGE mappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.
Keep the existing PMD-sized behavior when HPAGE_PMD_ORDER fits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by both mapping_max_folio_order(mapping) and 2MB.
2MB is chosen as the cap because it matches the PMD size on x86_64 and on arm64 with 4K or 16K base pages, so the size/memory-pressure tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.
The final allocation order may still be clamped by page_cache_ra_order() to the mapping and request geometry, but this gives VM_HUGEPAGE mappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.
--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
mm/filemap.c +19 -8
@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) - force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { + force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M )); + } + }if (!force_thp_readahead) {/*
@@ -3354,17 +3363,19 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) }if (force_thp_readahead) { +unsigned long folio_nr_pages =1UL << thp_order; + fpin =maybe_unlock_mmap_for_io (vmf, fpin); - ractl._index &= ~((unsigned long )HPAGE_PMD_NR -1 ); - ra->size =HPAGE_PMD_NR ; + ractl._index &= ~(folio_nr_pages -1 ); + ra->size = folio_nr_pages;/* -* Fetch two PMD folios, so we get the chance to actually +* Fetch two folios so we get the chance to actually * readahead, unless we've been told not to. */ if (!(vm_flags &VM_RAND_READ )) ra->size *=2 ; - ra->async_size =HPAGE_PMD_NR ; - ra->order =HPAGE_PMD_ORDER ; + ra->async_size = folio_nr_pages; + ra->order = thp_order;page_cache_ra_order (&ractl, ra);return fpin; }
Signature · 1 line
2.52.0
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 7 lines · Usama Arif
The
force_thp_readaheadpath indo_sync_mmap_readahead()is gated onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always requestsHPAGE_PMD_ORDER/HPAGE_PMD_NR. On configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size,VM_HUGEPAGEmappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.
Quoted 7 lines
Keep the existing PMD-sized behavior when
HPAGE_PMD_ORDERfits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by bothmapping_max_folio_order(mapping)and 2MB.2MB is chosen as the cap because it matches the PMD size on
x86_64and on arm64 with 4K or 16K base pages, so the size/memory-pressure
16K base page size's PMDs should be 32M, no? Am I misunderstanding what you mean here?
Quoted 31 lines
tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.
The final allocation order may still be clamped by
page_cache_ra_order()to the mapping and request geometry, but this givesVM_HUGEPAGEmappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.Signed-off-by Usama Arif <[email protected]>--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)mm/filemap.c +1
-2@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER )
mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3329
Quoted 10 lines
mm/filemap.c +9
-1- force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { + force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M ));
This looks somewhat arbitrary to me (as does the old logic). It seems more natural (correct?) to do this:
if (THP_ENABLED && (vm_flags & VM_HUGEPAGE)) {
if (mapping_large_folio_support(mapping)) {
force_thp_readahead = true;
/*
* Cap THP readahead to either the max folio order the
* mapping supports, or the max order the page cache
* supports (useless to try more), or the hugepage PMD
* order (CPU can't benefit from larger).
*/
thp_order = min3(mapping_max_folio_order(mapping),
MAX_PAGECACHE_ORDER, HPAGE_PMD_ORDER);
}
}Quoted 9 lines
mm/filemap.c +2
-0+ } + }if (!force_thp_readahead) {/* @@ -3354,17 +3363,19 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) }if (force_thp_readahead) {
mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3372
Quoted 10 lines
mm/filemap.c +5
-3+unsigned long folio_nr_pages =1UL << thp_order; + fpin =maybe_unlock_mmap_for_io (vmf, fpin); - ractl._index &= ~((unsigned long )HPAGE_PMD_NR -1 ); - ra->size =HPAGE_PMD_NR ; + ractl._index &= ~(folio_nr_pages -1 ); + ra->size = folio_nr_pages;/* -* Fetch two PMD folios, so we get the chance to actually +* Fetch two folios so we get the chance to actually
^^ large folios?
mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3378
Quoted 8 lines
mm/filemap.c +2
-2* readahead, unless we've been told not to. */ if (!(vm_flags &VM_RAND_READ )) ra->size *=2 ; - ra->async_size =HPAGE_PMD_NR ; - ra->order =HPAGE_PMD_ORDER ; + ra->async_size = folio_nr_pages; + ra->order = thp_order;
This part LGTM.
Signature · 1 line
Pedro
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 13 lines · Pedro Falcato
Quoted 12 lines · Usama Arif
The
force_thp_readaheadpath indo_sync_mmap_readahead()is gated onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always requestsHPAGE_PMD_ORDER/HPAGE_PMD_NR. On configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size,VM_HUGEPAGEmappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.Keep the existing PMD-sized behavior when
HPAGE_PMD_ORDERfits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by bothmapping_max_folio_order(mapping)and 2MB.
Quoted 6 lines
Quoted 2 lines
2MB is chosen as the cap because it matches the PMD size on
x86_64and on arm64 with 4K or 16K base pages, so the size/memory-pressure16K base page size's PMDs should be 32M, no? Am I misunderstanding what you mean here?
Yes, should have just said 4K. Messed up when rewriting the commit message. Andrew Thanks for merging in mm-new, would it be possible to remove "or 16K" from the commit message. I can send a new revision as well if that is easier and preferred.
Quoted 45 lines
Quoted 31 lines
tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.
The final allocation order may still be clamped by
page_cache_ra_order()to the mapping and request geometry, but this givesVM_HUGEPAGEmappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.Signed-off-by Usama Arif <[email protected]>--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)mm/filemap.c +1
-2@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER )mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3329
Quoted 10 lines
mm/filemap.c +9
-1- force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { + force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M ));This looks somewhat arbitrary to me (as does the old logic). It seems more natural (correct?) to do this:
Quoted 14 lines
if (THP_ENABLED && (vm_flags & VM_HUGEPAGE)) {
if (mapping_large_folio_support(mapping)) {
force_thp_readahead = true;
/*
* Cap THP readahead to either the max folio order the
* mapping supports, or the max order the page cache
* supports (useless to try more), or the hugepage PMD
* order (CPU can't benefit from larger).
*/
thp_order = min3(mapping_max_folio_order(mapping),
MAX_PAGECACHE_ORDER, HPAGE_PMD_ORDER);
}
}So this can result in extreme memory pressure. For example, on 64K base page size, with HPAGE_PMD_ORDER = 13 (512M) and MAX_PAGECACHE_ORDER = 11 (128M), this will evaluate THP order to 128M. I think Jan mentioned in previous versions that there are already reports of high memory pressure due to large file folios, and we are seeing this in our fleet as well with xfs.
I chose 2M cap as that is what most (not all) architectures which currently support this hugepage code flow are currently used to, and on archs + base page size combination which just evaluate this code to base page, for example 64K base page size, it will provide a performance uplift without causing excessive memory pressure.
In pagemap.h, we have
#define PREFERRED_MAX_PAGECACHE_ORDER HPAGE_PMD_ORDER
#define MAX_PAGECACHE_ORDER min(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)
So MAX_PAGECACHE_ORDER <= HPAGE_PMD_ORDER is always true.
At filesystem/inode setup: mapping_set_folio_order_range()
if (max > MAX_PAGECACHE_ORDER) max = MAX_PAGECACHE_ORDER;
so mapping_max_folio_order() <= MAX_PAGECACHE_ORDER is also always true.
which means mapping_max_folio_order(mapping) <= MAX_PAGECACHE_ORDER <= HPAGE_PMD_ORDER is always true, and you dont need the min3(..) in your diff.
Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) {
if (mapping_large_folio_support(mapping)) {
force_thp_readahead = true;
thp_order = min_t(unsigned int,
mapping_max_folio_order(mapping),
get_order(SZ_2M));
}
}This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So IMHO the current code is correct, both in maintaining what the existing behaviour has been, but also trying to provide large folios to architectures that currently dont get them but in a controlled way.
Later on, when we think or evaluate that 2M was too small a cap, it can be raised. I think its very easy to raise a cap, but much more difficult to make the cap smaller, as we might get reports on kernel upgrades that someone was getting a certain size of folio but now its much smaller.
As a summary, what my code in this current form gives is:
1) 4K x86/ARM (HPAGE_PMD_ORDER=9 <= 9): first branch -> 2M unchanged behaviour
2) 16K ARM (HPAGE_PMD_ORDER=11 <= 11): first branch -> 32M unchanged behaviour
3) 64K ARM (HPAGE_PMD_ORDER=13 <= 11): new else-if -> 2M new capability, conservative behaviour, which can be increased later if needed.
I know that 64K base page size having 2M large folios while 16K base page size having 32M sounds counterintuitive, but I think 32M on 16K was a mistake and we shouldnt repeat that mistake for 64K.
What your change with min3(mapping_max_folio_order(mapping), MAX_PAGECACHE_ORDER, HPAGE_PMD_ORDER) will give
3) 64K ARM -> 128M folios, extremely high memory pressure.
What only having min(mapping_max_folio_order, get_order(SZ_2M)) will give:
2) 16K ARM -> 2M folios, (regression since we always had 32M on 16K ARM).
Quoted 31 lines
Quoted 9 lines
mm/filemap.c +2
-0+ } + }if (!force_thp_readahead) {/* @@ -3354,17 +3363,19 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) }if (force_thp_readahead) {mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3372
Quoted 10 lines
mm/filemap.c +5
-3+unsigned long folio_nr_pages =1UL << thp_order; + fpin =maybe_unlock_mmap_for_io (vmf, fpin); - ractl._index &= ~((unsigned long )HPAGE_PMD_NR -1 ); - ra->size =HPAGE_PMD_NR ; + ractl._index &= ~(folio_nr_pages -1 ); + ra->size = folio_nr_pages;/* -* Fetch two PMD folios, so we get the chance to actually +* Fetch two folios so we get the chance to actually ^^ large folios?
mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3378
Quoted 8 lines
mm/filemap.c +2
-2* readahead, unless we've been told not to. */ if (!(vm_flags &VM_RAND_READ )) ra->size *=2 ; - ra->async_size =HPAGE_PMD_NR ; - ra->order =HPAGE_PMD_ORDER ; + ra->async_size = folio_nr_pages; + ra->order = thp_order;This part LGTM.
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 94 lines · Usama Arif
Quoted 13 lines · Pedro Falcato
Quoted 12 lines · Usama Arif
The
force_thp_readaheadpath indo_sync_mmap_readahead()is gated onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always requestsHPAGE_PMD_ORDER/HPAGE_PMD_NR. On configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size,VM_HUGEPAGEmappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.Keep the existing PMD-sized behavior when
HPAGE_PMD_ORDERfits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by bothmapping_max_folio_order(mapping)and 2MB.Quoted 6 lines
Quoted 2 lines
2MB is chosen as the cap because it matches the PMD size on
x86_64and on arm64 with 4K or 16K base pages, so the size/memory-pressure16K base page size's PMDs should be 32M, no? Am I misunderstanding what you mean here?
Yes, should have just said 4K. Messed up when rewriting the commit message. Andrew Thanks for merging in mm-new, would it be possible to remove "or 16K" from the commit message. I can send a new revision as well if that is easier and preferred.
Quoted 45 lines
Quoted 31 lines
tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.
The final allocation order may still be clamped by
page_cache_ra_order()to the mapping and request geometry, but this givesVM_HUGEPAGEmappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.Signed-off-by Usama Arif <[email protected]>--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)mm/filemap.c +1
-2@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER )mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3329
Quoted 10 lines
mm/filemap.c +9
-1- force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { + force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M ));This looks somewhat arbitrary to me (as does the old logic). It seems more natural (correct?) to do this:
Quoted 14 lines
if (THP_ENABLED && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; /* * Cap THP readahead to either the max folio order the * mapping supports, or the max order the page cache * supports (useless to try more), or the hugepage PMD * order (CPU can't benefit from larger). */ thp_order = min3(mapping_max_folio_order(mapping), MAX_PAGECACHE_ORDER, HPAGE_PMD_ORDER); } }So this can result in extreme memory pressure. For example, on 64K base page size, with
HPAGE_PMD_ORDER= 13 (512M) andMAX_PAGECACHE_ORDER= 11 (128M), this will evaluate THP order to 128M. I think Jan mentioned in previous versions that there are already reports of high memory pressure due to large file folios, and we are seeing this in our fleet as well with xfs.
Quoted 4 lines
I chose 2M cap as that is what most (not all) architectures which currently support this hugepage code flow are currently used to, and on archs + base page size combination which just evaluate this code to base page, for example 64K base page size, it will provide a performance uplift without causing excessive memory pressure.
For what it's worth, memory pressure is still a problem even on 2MB folios. But yes, I understand.
For what it's worth, I don't think it's such a big issue in this case - you're asking for THPs, and you're getting them - though maybe not efficiently because these aren't at the PMD level. But this is also true for e.g x86 where an an order-3 folio or order-7 folio are completely useless for the hardware, since there is no mTHP to speak of (and, with that, you also lose PG_active and PG_reference precision as you only keep one of each for each folio, amongst other tradeoffs).
Quoted 6 lines
In
pagemap.h, we have#define
PREFERRED_MAX_PAGECACHE_ORDERHPAGE_PMD_ORDER
#defineMAX_PAGECACHE_ORDERmin(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)
Quoted 8 lines
So
MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true.At filesystem/inode setup:
mapping_set_folio_order_range()if (max > MAX_PAGECACHE_ORDER) max = MAX_PAGECACHE_ORDER;so
mapping_max_folio_order()<=MAX_PAGECACHE_ORDERis also always true.
Right, good point. (well, I suppose it will not be true in the future as someone tries to harness PMD-level cont bits)
Quoted 16 lines
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }
Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to HPAGE_PMD_ORDER, and you get the same result.
Does this sound correct?
Quoted 9 lines
So IMHO the current code is correct, both in maintaining what the existing behaviour has been, but also trying to provide large folios to architectures that currently dont get them but in a controlled way.
Later on, when we think or evaluate that 2M was too small a cap, it can be raised. I think its very easy to raise a cap, but much more difficult to make the cap smaller, as we might get reports on kernel upgrades that someone was getting a certain size of folio but now its much smaller.
I don't think this is a valid argument. You can also have reports that now folios are too large and they're regressing workloads because of thrashing/ compaction/possibly TLB thrashing. And indeed we have several reports of that
(https://lore.kernel.org/linux-fsdevel/[email protected]/
comes to mind, plus all of the anon THP regressions over the years, including in $current_year).
Bottom line is that changing things will always affect someone :) Particularly since the logic we have is not too careful at deciding what should or should not be a THP (both in anon and file cases). And if (once?) we make it smarter, it will surely also regress someone!
Signature · 1 line
Pedro
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 103 lines · Pedro Falcato
Quoted 94 lines · Usama Arif
Quoted 13 lines · Pedro Falcato
Quoted 12 lines · Usama Arif
The
force_thp_readaheadpath indo_sync_mmap_readahead()is gated onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always requestsHPAGE_PMD_ORDER/HPAGE_PMD_NR. On configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size,VM_HUGEPAGEmappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.Keep the existing PMD-sized behavior when
HPAGE_PMD_ORDERfits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by bothmapping_max_folio_order(mapping)and 2MB.Quoted 6 lines
Quoted 2 lines
2MB is chosen as the cap because it matches the PMD size on
x86_64and on arm64 with 4K or 16K base pages, so the size/memory-pressure16K base page size's PMDs should be 32M, no? Am I misunderstanding what you mean here?
Yes, should have just said 4K. Messed up when rewriting the commit message. Andrew Thanks for merging in mm-new, would it be possible to remove "or 16K" from the commit message. I can send a new revision as well if that is easier and preferred.
Quoted 45 lines
Quoted 31 lines
tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.
The final allocation order may still be clamped by
page_cache_ra_order()to the mapping and request geometry, but this givesVM_HUGEPAGEmappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.Signed-off-by Usama Arif <[email protected]>--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)mm/filemap.c +1
-2@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER )mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3329
Quoted 10 lines
mm/filemap.c +9
-1- force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { + force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M ));This looks somewhat arbitrary to me (as does the old logic). It seems more natural (correct?) to do this:
Quoted 14 lines
if (THP_ENABLED && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; /* * Cap THP readahead to either the max folio order the * mapping supports, or the max order the page cache * supports (useless to try more), or the hugepage PMD * order (CPU can't benefit from larger). */ thp_order = min3(mapping_max_folio_order(mapping), MAX_PAGECACHE_ORDER, HPAGE_PMD_ORDER); } }So this can result in extreme memory pressure. For example, on 64K base page size, with
HPAGE_PMD_ORDER= 13 (512M) andMAX_PAGECACHE_ORDER= 11 (128M), this will evaluate THP order to 128M. I think Jan mentioned in previous versions that there are already reports of high memory pressure due to large file folios, and we are seeing this in our fleet as well with xfs.Quoted 4 lines
I chose 2M cap as that is what most (not all) architectures which currently support this hugepage code flow are currently used to, and on archs + base page size combination which just evaluate this code to base page, for example 64K base page size, it will provide a performance uplift without causing excessive memory pressure.
For what it's worth, memory pressure is still a problem even on 2MB folios. But yes, I understand.
Quoted 8 lines
For what it's worth, I don't think it's such a big issue in this case - you're asking for THPs, and you're getting them - though maybe not efficiently because these aren't at the PMD level. But this is also true for e.g x86 where an an order-3 folio or order-7 folio are completely useless for the hardware, since there is no mTHP to speak of (and, with that, you also lose
PG_activeandPG_referenceprecision as you only keep one of each for each folio, amongst other tradeoffs).
So there is hardware TLB coalescing on almost every new AMD CPU, I believe it is at 32kB, and we would benefit from that. On top of that, we would reduce pagefaults.
Quoted 34 lines
Quoted 6 lines
In
pagemap.h, we have#define
PREFERRED_MAX_PAGECACHE_ORDERHPAGE_PMD_ORDER
#defineMAX_PAGECACHE_ORDERmin(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)Quoted 8 lines
So
MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true.At filesystem/inode setup:
mapping_set_folio_order_range()if (max > MAX_PAGECACHE_ORDER) max = MAX_PAGECACHE_ORDER;so
mapping_max_folio_order()<=MAX_PAGECACHE_ORDERis also always true.Right, good point. (well, I suppose it will not be true in the future as someone tries to harness PMD-level cont bits)
Quoted 16 lines
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }
Quoted 9 lines
Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to
HPAGE_PMD_ORDER, and you get the same result.Does this sound correct?
Yes, so if we replace it with SZ_32M, it sounds correct. I just think the 32M size is too large. But as you pointed out, even 2M can be too large...
Quoted 10 lines
Quoted 9 lines
So IMHO the current code is correct, both in maintaining what the existing behaviour has been, but also trying to provide large folios to architectures that currently dont get them but in a controlled way.
Later on, when we think or evaluate that 2M was too small a cap, it can be raised. I think its very easy to raise a cap, but much more difficult to make the cap smaller, as we might get reports on kernel upgrades that someone was getting a certain size of folio but now its much smaller.
Quoted 6 lines
I don't think this is a valid argument. You can also have reports that now folios are too large and they're regressing workloads because of thrashing/ compaction/possibly TLB thrashing. And indeed we have several reports of that
(https://lore.kernel.org/linux-fsdevel/[email protected]/
comes to mind, plus all of the anon THP regressions over the years, including in $current_year).
Ack, and yes we have seen both thrashing and compaction issues due to xfs large folios in the fleet with x86..
Quoted 6 lines
Bottom line is that changing things will always affect someone :) Particularly since the logic we have is not too careful at deciding what should or should not be a THP (both in anon and file cases). And if (once?) we make it smarter, it will surely also regress someone!
Yes completely agree on this as well.
So personally I do have a preference of keeping the cap at 2M atleast initially while we currently try and solve the issues we see with 2M alone. As we are already seeing reports of thrashing and compaction with just 2M, I dont think the logic in this patch with just an if else is that complicated.
Matthew, Jan, do you have any thoughts or strong preferences on cap size?
Thanks!
Jan Kara
maintainerRe: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 25 lines · Usama Arif
Quoted 24 lines · Pedro Falcato
Quoted 16 lines · Usama Arif
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to
HPAGE_PMD_ORDER, and you get the same result.
Quoted 5 lines
Quoted 2 lines
Does this sound correct?
Yes, so if we replace it with
SZ_32M, it sounds correct. I just think the 32M size is too large. But as you pointed out, even 2M can be too large...
So AFAIU the practical discussion is about two options:
1) limiting at 2MB with a slighly more complicated logic to keep mapping at PMD order for 16k pagesize on ARM but use 2MB pages for 64k pagesize on ARM
or
2) limit at 32MB with simple logic which results in larger (32MB) folios with 16k and 64k pagesize on ARM and thus larger memory overhead.
I'd like to maybe offer option 3): limit at 2MB with simple logic. This will reduce folio size on 16k pagesize ARM compared to 1) but do we really care? I.e., is there big enough practical performance impact with conpte and other tricks ARM is playing?
Quoted 5 lines
Quoted 4 lines
Bottom line is that changing things will always affect someone :) Particularly since the logic we have is not too careful at deciding what should or should not be a THP (both in anon and file cases). And if (once?) we make it smarter, it will surely also regress someone!
Quoted 8 lines
Yes completely agree on this as well.
So personally I do have a preference of keeping the cap at 2M atleast initially while we currently try and solve the issues we see with 2M alone. As we are already seeing reports of thrashing and compaction with just 2M, I dont think the logic in this patch with just an if else is that complicated.
Matthew, Jan, do you have any thoughts or strong preferences on cap size?
Frankly, no strong opinion. I'd think 3) is worth trying for its simplicity and seeing whether somebody complains, otherwise I can live with both 1) and 2).
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 39 lines · Jan Kara
Quoted 25 lines · Usama Arif
Quoted 24 lines · Pedro Falcato
Quoted 16 lines · Usama Arif
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to
HPAGE_PMD_ORDER, and you get the same result.Quoted 5 lines
Quoted 2 lines
Does this sound correct?
Yes, so if we replace it with
SZ_32M, it sounds correct. I just think the 32M size is too large. But as you pointed out, even 2M can be too large...So AFAIU the practical discussion is about two options:
1) limiting at 2MB with a slighly more complicated logic to keep mapping at PMD order for 16k pagesize on ARM but use 2MB pages for 64k pagesize on ARM
or
Quoted 8 lines
2) limit at 32MB with simple logic which results in larger (32MB) folios with 16k and 64k pagesize on ARM and thus larger memory overhead.
I'd like to maybe offer option 3): limit at 2MB with simple logic. This will reduce folio size on 16k pagesize ARM compared to 1) but do we really care? I.e., is there big enough practical performance impact with conpte and other tricks ARM is playing?
I think the logic isn't that complicated for 1, but I am happy with option 3.
Quoted 12 lines
Quoted 12 lines
Quoted 4 lines
Bottom line is that changing things will always affect someone :) Particularly since the logic we have is not too careful at deciding what should or should not be a THP (both in anon and file cases). And if (once?) we make it smarter, it will surely also regress someone!
Yes completely agree on this as well.
So personally I do have a preference of keeping the cap at 2M atleast initially while we currently try and solve the issues we see with 2M alone. As we are already seeing reports of thrashing and compaction with just 2M, I dont think the logic in this patch with just an if else is that complicated.
Quoted 5 lines
Quoted 1 line
Matthew, Jan, do you have any thoughts or strong preferences on cap size?
Frankly, no strong opinion. I'd think 3) is worth trying for its simplicity and seeing whether somebody complains, otherwise I can live with both 1) and 2).
Thanks! Yes, let me send this.
Andrew I will send this as a new and hopefully last revision as I am not sure if you would like another fixup on top of this series! Thanks!
Quoted 2 lines
Honza
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
In reply to Jan KaraQuoted 39 lines · Jan Kara
Quoted 25 lines · Usama Arif
Quoted 24 lines · Pedro Falcato
Quoted 16 lines · Usama Arif
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to
HPAGE_PMD_ORDER, and you get the same result.Quoted 5 lines
Quoted 2 lines
Does this sound correct?
Yes, so if we replace it with
SZ_32M, it sounds correct. I just think the 32M size is too large. But as you pointed out, even 2M can be too large...So AFAIU the practical discussion is about two options:
1) limiting at 2MB with a slighly more complicated logic to keep mapping at PMD order for 16k pagesize on ARM but use 2MB pages for 64k pagesize on ARM
or
Quoted 8 lines
2) limit at 32MB with simple logic which results in larger (32MB) folios with 16k and 64k pagesize on ARM and thus larger memory overhead.
I'd like to maybe offer option 3): limit at 2MB with simple logic. This will reduce folio size on 16k pagesize ARM compared to 1) but do we really care? I.e., is there big enough practical performance impact with conpte and other tricks ARM is playing?
arm64 16K contpte tops out at 256KB TLB entries. It's quite a lot smaller than a PMD entry. Also, something that was discussed at LSFMM was its effectiveness. Apparently, most of the gains seem to sit on actually having a larger page size (perhaps Dev/Ryan can comment; sadly the slides were not posted anywhere on the ML, so I don't have numbers).
To me, the question is quite clear: do we trust users that say "please give me hugepages" enough to unconditionally give them hugepages? I would assume the answer lies somewhere between "yes" and "no", but 32MB I would say is not particularly excessive. 512MB is... much worse.
Signature · 1 line
Pedro
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 55 lines · Pedro Falcato
Quoted 39 lines · Jan Kara
Quoted 25 lines · Usama Arif
Quoted 24 lines · Pedro Falcato
Quoted 16 lines · Usama Arif
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to
HPAGE_PMD_ORDER, and you get the same result.Quoted 5 lines
Quoted 2 lines
Does this sound correct?
Yes, so if we replace it with
SZ_32M, it sounds correct. I just think the 32M size is too large. But as you pointed out, even 2M can be too large...So AFAIU the practical discussion is about two options:
1) limiting at 2MB with a slighly more complicated logic to keep mapping at PMD order for 16k pagesize on ARM but use 2MB pages for 64k pagesize on ARM
or
Quoted 8 lines
2) limit at 32MB with simple logic which results in larger (32MB) folios with 16k and 64k pagesize on ARM and thus larger memory overhead.
I'd like to maybe offer option 3): limit at 2MB with simple logic. This will reduce folio size on 16k pagesize ARM compared to 1) but do we really care? I.e., is there big enough practical performance impact with conpte and other tricks ARM is playing?
arm64 16K contpte tops out at 256KB TLB entries. It's quite a lot smaller than a PMD entry. Also, something that was discussed at LSFMM was its effectiveness. Apparently, most of the gains seem to sit on actually having a larger page size (perhaps Dev/Ryan can comment; sadly the slides were not posted anywhere on the ML, so I don't have numbers).
Quoted 5 lines
To me, the question is quite clear: do we trust users that say "please give me hugepages" enough to unconditionally give them hugepages? I would assume the answer lies somewhere between "yes" and "no", but 32MB I would say is not particularly excessive. 512MB is... much worse.
I think the other question also is, if the userspace asks for hugepages, is it asking for the biggest possible one? I think the answer is yes on 4K base page size when largest is 2M, but maybe not the case for 16K and 64K.
/sys/kernel/mm/transparent_hugepage/hugepages-* is supposed to be used for anon only, but maybe in the future we could use that to determine the size of THP to give to the user for file over here? For e.g. over here we could have used it to determine what the biggest size is that has madvise (or always) set and used it over here. Its probably a much bigger discussion.
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
Quoted 64 lines · Usama Arif
Quoted 55 lines · Pedro Falcato
Quoted 39 lines · Jan Kara
Quoted 25 lines · Usama Arif
Quoted 24 lines · Pedro Falcato
Quoted 16 lines · Usama Arif
which means
mapping_max_folio_order(mapping)<=MAX_PAGECACHE_ORDER<=HPAGE_PMD_ORDERis always true, and you dont need the min3(..) in your diff.Now the question is if then why not just do:
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; thp_order = min_t(unsigned int, mapping_max_folio_order(mapping), get_order(SZ_2M)); } }Quoted 2 lines
This is because this will regress the 16K ARM case where we already got 32M folios. Someone might upgrade the kernel and start getting 2M folios now.
So maybe limit to 32MB? It's still arbitrary but at least you get simpler logic. If the architecture does not support 32MiB folios, it will clamp the maximum folio order to
HPAGE_PMD_ORDER, and you get the same result.Quoted 5 lines
Quoted 2 lines
Does this sound correct?
Yes, so if we replace it with
SZ_32M, it sounds correct. I just think the 32M size is too large. But as you pointed out, even 2M can be too large...So AFAIU the practical discussion is about two options:
1) limiting at 2MB with a slighly more complicated logic to keep mapping at PMD order for 16k pagesize on ARM but use 2MB pages for 64k pagesize on ARM
or
Quoted 8 lines
2) limit at 32MB with simple logic which results in larger (32MB) folios with 16k and 64k pagesize on ARM and thus larger memory overhead.
I'd like to maybe offer option 3): limit at 2MB with simple logic. This will reduce folio size on 16k pagesize ARM compared to 1) but do we really care? I.e., is there big enough practical performance impact with conpte and other tricks ARM is playing?
arm64 16K contpte tops out at 256KB TLB entries. It's quite a lot smaller than a PMD entry. Also, something that was discussed at LSFMM was its effectiveness. Apparently, most of the gains seem to sit on actually having a larger page size (perhaps Dev/Ryan can comment; sadly the slides were not posted anywhere on the ML, so I don't have numbers).
Quoted 5 lines
To me, the question is quite clear: do we trust users that say "please give me hugepages" enough to unconditionally give them hugepages? I would assume the answer lies somewhere between "yes" and "no", but 32MB I would say is not particularly excessive. 512MB is... much worse.
Quoted 3 lines
I think the other question also is, if the userspace asks for hugepages, is it asking for the biggest possible one? I think the answer is yes on 4K base page size when largest is 2M, but maybe not the case for 16K and 64K.
Yep, fully agree, the interface itself is limited. Though perhaps userspace itself would not know...
Quoted 6 lines
/sys/kernel/mm/transparent_hugepage/hugepages-*is supposed to be used for anon only, but maybe in the future we could use that to determine the size of THP to give to the user for file over here? For e.g. over here we could have used it to determine what the biggest size is that has madvise (or always) set and used it over here. Its probably a much bigger discussion.
Hmm, I'm not a huge fan of those toggles (that not many people know how to toggle), maybe using those would be a mistake. Do note that shmem already has its own set of confusing toggles, which work similarly to these anon ones.
But, yes, it's a much bigger discussion :)
Signature · 1 line
Pedro
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
In reply to Usama ArifQuoted 115 lines · Usama Arif
Quoted 103 lines · Pedro Falcato
Quoted 94 lines · Usama Arif
Quoted 13 lines · Pedro Falcato
Quoted 12 lines · Usama Arif
The
force_thp_readaheadpath indo_sync_mmap_readahead()is gated onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always requestsHPAGE_PMD_ORDER/HPAGE_PMD_NR. On configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size,VM_HUGEPAGEmappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.Keep the existing PMD-sized behavior when
HPAGE_PMD_ORDERfits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by bothmapping_max_folio_order(mapping)and 2MB.Quoted 6 lines
Quoted 2 lines
2MB is chosen as the cap because it matches the PMD size on
x86_64and on arm64 with 4K or 16K base pages, so the size/memory-pressure16K base page size's PMDs should be 32M, no? Am I misunderstanding what you mean here?
Yes, should have just said 4K. Messed up when rewriting the commit message. Andrew Thanks for merging in mm-new, would it be possible to remove "or 16K" from the commit message. I can send a new revision as well if that is easier and preferred.
Quoted 45 lines
Quoted 31 lines
tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.
The final allocation order may still be clamped by
page_cache_ra_order()to the mapping and request geometry, but this givesVM_HUGEPAGEmappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.Signed-off-by Usama Arif <[email protected]>--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)mm/filemap.c +1
-2@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER )mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3329
Quoted 10 lines
mm/filemap.c +9
-1- force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { + force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M ));This looks somewhat arbitrary to me (as does the old logic). It seems more natural (correct?) to do this:
Quoted 14 lines
if (THP_ENABLED && (vm_flags & VM_HUGEPAGE)) { if (mapping_large_folio_support(mapping)) { force_thp_readahead = true; /* * Cap THP readahead to either the max folio order the * mapping supports, or the max order the page cache * supports (useless to try more), or the hugepage PMD * order (CPU can't benefit from larger). */ thp_order = min3(mapping_max_folio_order(mapping), MAX_PAGECACHE_ORDER, HPAGE_PMD_ORDER); } }So this can result in extreme memory pressure. For example, on 64K base page size, with
HPAGE_PMD_ORDER= 13 (512M) andMAX_PAGECACHE_ORDER= 11 (128M), this will evaluate THP order to 128M. I think Jan mentioned in previous versions that there are already reports of high memory pressure due to large file folios, and we are seeing this in our fleet as well with xfs.Quoted 4 lines
I chose 2M cap as that is what most (not all) architectures which currently support this hugepage code flow are currently used to, and on archs + base page size combination which just evaluate this code to base page, for example 64K base page size, it will provide a performance uplift without causing excessive memory pressure.
For what it's worth, memory pressure is still a problem even on 2MB folios. But yes, I understand.
Quoted 8 lines
For what it's worth, I don't think it's such a big issue in this case - you're asking for THPs, and you're getting them - though maybe not efficiently because these aren't at the PMD level. But this is also true for e.g x86 where an an order-3 folio or order-7 folio are completely useless for the hardware, since there is no mTHP to speak of (and, with that, you also lose
PG_activeandPG_referenceprecision as you only keep one of each for each folio, amongst other tradeoffs).
Quoted 2 lines
So there is hardware TLB coalescing on almost every new AMD CPU, I believe it is at 32kB, and we would benefit from that. On top of that, we would reduce pagefaults.
(just addressing this, because everything else was addressed in some other ways and I'm just going through my email backlog; my apologies for the delay)
AMD's TLB coalescing feature requires (as far as I know) A and D bits to match for every PTE in the cluster. Which means you need to effectively touch every page in that 32KB cluster (that was then later reduced to 16KB in zen3 if I'm not mistaken). And as soon as you write to one of them, the D bit gets set and the TLB entry needs to be torn down. So it's usefulness is unfortunately limited.
But yes, we do reduce page faults. Though I have to wonder how much that matters in a world where we have fault-around.
Signature · 1 line
Pedro
Re: [PATCH v6 2/2] mm: use mapping_max_folio_order() for force_thp_readahead order
In reply to Usama ArifQuoted 48 lines · Usama Arif
The
force_thp_readaheadpath indo_sync_mmap_readahead()is gated onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always requestsHPAGE_PMD_ORDER/HPAGE_PMD_NR. On configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDER, notably arm64 with a 64K base page size,VM_HUGEPAGEmappings cannot use this path and fall back to the non-forced mmap readahead path even when the mapping supports useful large folios.Keep the existing PMD-sized behavior when
HPAGE_PMD_ORDERfits in the page cache. When it does not, enable forced readahead for mappings that support large folios and request an order capped by bothmapping_max_folio_order(mapping)and 2MB.2MB is chosen as the cap because it matches the PMD size on
x86_64and on arm64 with 4K or 16K base pages, so the size/memory-pressure tradeoff for folios of that size is already well understood. On arm64 with a 64K base page size, 2MB is also the contiguous-PTE (contpte) block size, so the resulting folios coalesce into a single TLB entry and reduce TLB pressure on the readahead path.The final allocation order may still be clamped by
page_cache_ra_order()to the mapping and request geometry, but this givesVM_HUGEPAGEmappings on such configurations a large-folio readahead request instead of dropping back to base-page readahead.Signed-off-by Usama Arif <[email protected]>--- mm/filemap.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)mm/filemap.c +3
-3@@ -3312,14 +3312,23 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)struct file *fpin =NULL ;vm_flags_t vm_flags = vmf->vma->vm_flags;bool force_thp_readahead = false; +unsigned int thp_order =0 ;unsigned short mmap_miss; ractl._max_index = vmf->vma->vm_pgoff +vma_pages (vmf->vma) -1 ;/* Use the readahead code, even if readahead is disabled */ -if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && - (vm_flags &VM_HUGEPAGE ) &&HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) - force_thp_readahead = true; +if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) {
mm/filemap.c static struct file *do_sync_mmap_readahead(struct vm_fault *vmf) · line 3332
Quoted 10 lines
mm/filemap.c +9
-0+ force_thp_readahead = true; + thp_order =HPAGE_PMD_ORDER ; + }else if (mapping_large_folio_support (mapping)) { + force_thp_readahead = true; + thp_order = min_t(unsigned int, +mapping_max_folio_order (mapping), +get_order (SZ_2M )); + } + }
I think might be good to include the below comment to explain the decision being made here:
From 6673c04a434df01d449c1bdb9ac8de74e19d6b7e Mon Sep 17 00:00:00 2001
From: Usama Arif <[email protected]>
Date: Fri, 29 May 2026 05:31:05 -0700
Subject: [PATCH] [fixlet] mm/filemap: add comment explaining design decision
--- mm/filemap.c | 8 ++++++++ 1 file changed, 8 insertions(+)
mm/filemap.c +8 -0
@@ -3319,6 +3319,14 @@ static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)/* Use the readahead code, even if readahead is disabled */ if (IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE ) && (vm_flags &VM_HUGEPAGE )) { +/* +* Preserve PMD-sized readahead where it already fits in +* the page cache. Otherwise cap the new fallback path at +* 2MB: this is the common PMD-sized hugepage size, and it +* avoids memory pressure from very large forced readahead +* when mapping_max_folio_order() is high (for example, +* 128MB with 64K base pages on arm64). +*/ if (HPAGE_PMD_ORDER <=MAX_PAGECACHE_ORDER ) { force_thp_readahead = true; thp_order =HPAGE_PMD_ORDER ;
Signature · 1 line
2.53.0-Meta
Andrew Morton
maintainerQuoted 18 lines · On Thu, 28 May 2026 09:55:18 -0700 Usama Arif <[email protected]> wrote:
Two checks in
do_sync_mmap_readahead()limit large-folio readahead:1. The
mmap_missheuristic is meant to throttle wasteful speculative readahead. It is currently also applied to theVM_EXECreadahead path, which is targeted rather than speculative. Oncemmap_missexceedsMMAP_LOTSAMISS, exec readahead - including the large-folio order requested byexec_folio_order()- is disabled. On configurations where themmap_missdecrement paths are not active (see patch 1) the counter only grows, so exec readahead is permanently disabled after the first 100 faults.2. The
force_thp_readaheadpath is gated only onHPAGE_PMD_ORDER<=MAX_PAGECACHE_ORDERand always drives the readahead atHPAGE_PMD_ORDER. Configurations whereHPAGE_PMD_ORDERexceedsMAX_PAGECACHE_ORDERnever reach this path, even when the mapping itself supports usefully large folios well below the cap.
Quoted 8 lines
Both issues are most visible on arm64 with a 64K base page size, where
HPAGE_PMD_ORDERis 13 (512MB) -- aboveMAX_PAGECACHE_ORDER
(11) -- and wherefault_around_pagescollapses to 1 disablingshould_fault_around()(one of the twommap_missdecrement sites). However the fixes are architecture-agnostic: patch 1 reflects the nature ofVM_EXECreadahead regardless of base page size, and patch 2 generalises the gate so any mapping advertising a usefully large maximum folio order can benefit.
OK, thanks, it's getting late and [2/2] hasn't yet been reviewed. I'll add it, just, because I'm a sucker for speedups.