[PATCH 1/8] mm,hugetlb: Encode extra padding for aligning directly in hugetlb_get_unmapped_area

From: Oscar Salvador

Date: Fri Jun 05 2026 - 23:51:35 EST


Currently, for hugetlb mappings arches query the align_mask needed in order to
satisfy allocating a gap for the mapping, as it counts the worst-case scenario
which is being off by one page from boundary of the next hugetlb aligned address.

Later on, unmapped_area{_topdown} add this 'align_mask' on top of the
mapping's length we requested to count for worst-case scenario and get a gap
big enough, and then do the proper masking off once we get the address.

This means we have to special case hugetlb in quite a few places to first
1) ignore align_offset and 2) return a proper align_mask, but we can just do
as THP mappings do and encode the align_mask directly in hugetlb_get_unmapped_area
and pass that down to mm_get_unmapped_area_vmflags.

Once we get the address, we can properly mask it off before handing it over to
userspace.

This also allows us to fix a regression on AMD15h family for hugetlb
mappings [1].

[1] https://lore.kernel.org/linux-mm/20260527143643.GO31091@xxxxxxxxxx/

Fixes: 7bd3f1e1a9ae ("mm: make hugetlb mappings go through mm_get_unmapped_area_vmflags")
Reported-by: Karsten Desler <kdesler@xxxxxxxxxx>
Closes: https://lore.kernel.org/linux-mm/20260527143643.GO31091@xxxxxxxxxx/
Signed-off-by: Oscar Salvador <osalvador@xxxxxxx>
Acked-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
---
fs/hugetlbfs/inode.c | 25 ++++++++++++++++++++++++-
include/linux/hugetlb.h | 2 +-
2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 78d61bf2bd9b..da9bfb5b79f7 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -184,7 +184,30 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
if (addr)
addr0 = ALIGN(addr, huge_page_size(h));

- return mm_get_unmapped_area_vmflags(file, addr0, len, pgoff, flags, 0);
+ /*
+ * hugetlb mappings need to be huge page aligned.
+ * mm_get_unmapped_area_vmflags() only gives back PAGE_SIZE aligned
+ * areas.
+ *
+ * Extend the requested unmapped area for the worse case scenario:
+ * the address comes back and needs to be aligned up to by one small
+ * page short of a large page.
+ */
+ len += huge_page_size(h) - PAGE_SIZE;
+ /*
+ * pgoff is being used for coloring, but hugetlb mappings need strict
+ * alignment, so it is always ignored by special casing it down the road.
+ * Set it to 0 here, so we do not need to be hugetlb-aware later on.
+ */
+ pgoff = 0;
+ addr0 = mm_get_unmapped_area_vmflags(file, addr0, len, pgoff, flags, 0);
+ if (IS_ERR_VALUE(addr0))
+ return addr0;
+
+ /* Align the address to the next huge_page_size boundary */
+ addr0 = ALIGN(addr0, huge_page_size(h));
+
+ return addr0;
}

/*
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 5957bc25efa8..924e9a464214 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -1085,7 +1085,7 @@ bool is_raw_hwpoison_page_in_hugepage(struct page *page);

static inline unsigned long huge_page_mask_align(struct file *file)
{
- return PAGE_MASK & ~huge_page_mask(hstate_file(file));
+ return 0;
}

#else /* CONFIG_HUGETLB_PAGE */
--
2.35.3