[PATCH v2 4/4] mm: align file-backed mmap to max folio order in thp_get_unmapped_area

Usama Arif
2026-03-20T14:04:37Z

thp_get_unmapped_area() is the get_unmapped_area callback for
filesystems like ext4, xfs, and btrfs. It attempts to align the virtual
address for PMD_SIZE THP mappings, but on arm64 with 64K base pages
PMD_SIZE is 512M, which is too large for typical shared library mappings,
so the alignment always fails and falls back to PAGE_SIZE.

This means shared libraries loaded by ld.so via mmap() get 64K-aligned
virtual addresses, preventing contpte mapping even when 2M folios are
allocated with properly aligned file offsets and physical addresses.

Add a fallback in thp_get_unmapped_area_vmflags() that uses the
filesystem's mapping_max_folio_size() to determine alignment, capped to
the mapping length via rounddown_pow_of_two(len). This aligns mappings
to the largest folio the page cache will actually allocate, without
over-aligning small mappings.

The fallback is naturally a no-op for filesystems that don't support
large folios and skips the retry when the alignment would equal PMD_SIZE
(already attempted above).

Signed-off-by: Usama Arif <usama.arif@linux.dev>
---
mm/huge_memory.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 8e2746ea74adf..4005084c9c65b 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1242,6 +1242,20 @@ unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long add
if (ret)
return ret;

+ if (filp && filp->f_mapping) {
+ unsigned long max_folio_size =
+ mapping_max_folio_size(filp->f_mapping);
+ unsigned long size = rounddown_pow_of_two(len);
+
+ size = min(size, max_folio_size);
+ if (size > PAGE_SIZE && size != PMD_SIZE) {
+ ret = __thp_get_unmapped_area(filp, addr, len, off,
+ flags, size, vm_flags);
+ if (ret)
+ return ret;
+ }
+ }
+
return mm_get_unmapped_area_vmflags(filp, addr, len, pgoff, flags,
vm_flags);
}
--
2.52.0