[PATCH 2/2] sh: mm: align hugetlb mappings to the huge page size

From: Karl Mehltretter

Date: Sat Sep 26 2026 - 14:39:55 EST


mmap(MAP_HUGETLB) on SH4 can return an address which is not aligned to
the huge page size. In a QEMU r2d guest with 64 KiB huge pages, a
two-page mapping placed after a single 4 KiB mapping lands at
0x29558000, and process exit then hits the alignment check in
__unmap_hugepage_range():

kernel BUG at mm/hugetlb.c:5215!
Kernel BUG: 003e [#1]
PC is at __unmap_hugepage_range+0x3a4/0x424
PR is at __zap_vma_range+0xa28/0xa80

Commit 7bd3f1e1a9ae ("mm: make hugetlb mappings go through
mm_get_unmapped_area_vmflags") made hugetlb mappings use the
architecture's arch_get_unmapped_area(). The generic implementations
handle hugetlb alignment, but the SH implementations only account for
the cache-colouring constraint in shm_align_mask, so the mapping ends
up aligned to at most the D-cache alias size.

Set the alignment mask from the file's hstate for hugetlb mappings in
both SH implementations, as LoongArch did in commit 3109d5ff484b
("LoongArch: Set hugetlb mmap base address aligned with pmd size").
Huge-page alignment also satisfies the colouring constraint. SH only
uses the bottom-up layout today, but the top-down variant gets the same
change so the two stay in sync.

Fixes: 7bd3f1e1a9ae ("mm: make hugetlb mappings go through mm_get_unmapped_area_vmflags")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@xxxxxxxxx>
---
arch/sh/mm/mmap.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c
index c442734d9b0c..4df807e0df36 100644
--- a/arch/sh/mm/mmap.c
+++ b/arch/sh/mm/mmap.c
@@ -7,6 +7,7 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
+#include <linux/hugetlb.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/sched/mm.h>
@@ -92,7 +93,10 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
info.length = len;
info.low_limit = TASK_UNMAPPED_BASE;
info.high_limit = TASK_SIZE;
- info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
+ if (filp && is_file_hugepages(filp))
+ info.align_mask = huge_page_mask_align(filp);
+ else
+ info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
return vm_unmapped_area(&info);
}
@@ -142,7 +146,10 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
info.length = len;
info.low_limit = PAGE_SIZE;
info.high_limit = mm->mmap_base;
- info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
+ if (filp && is_file_hugepages(filp))
+ info.align_mask = huge_page_mask_align(filp);
+ else
+ info.align_mask = do_colour_align ? (PAGE_MASK & shm_align_mask) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
addr = vm_unmapped_area(&info);

--
2.39.5 (Apple Git-154)