| From 5f24d5a579d1eace79d505b148808a850b417d4c Mon Sep 17 00:00:00 2001 |
| From: Christophe Leroy <christophe.leroy@csgroup.eu> |
| Date: Thu, 21 Apr 2022 16:35:46 -0700 |
| Subject: mm, hugetlb: allow for "high" userspace addresses |
| |
| From: Christophe Leroy <christophe.leroy@csgroup.eu> |
| |
| commit 5f24d5a579d1eace79d505b148808a850b417d4c upstream. |
| |
| This is a fix for commit f6795053dac8 ("mm: mmap: Allow for "high" |
| userspace addresses") for hugetlb. |
| |
| This patch adds support for "high" userspace addresses that are |
| optionally supported on the system and have to be requested via a hint |
| mechanism ("high" addr parameter to mmap). |
| |
| Architectures such as powerpc and x86 achieve this by making changes to |
| their architectural versions of hugetlb_get_unmapped_area() function. |
| However, arm64 uses the generic version of that function. |
| |
| So take into account arch_get_mmap_base() and arch_get_mmap_end() in |
| hugetlb_get_unmapped_area(). To allow that, move those two macros out |
| of mm/mmap.c into include/linux/sched/mm.h |
| |
| If these macros are not defined in architectural code then they default |
| to (TASK_SIZE) and (base) so should not introduce any behavioural |
| changes to architectures that do not define them. |
| |
| For the time being, only ARM64 is affected by this change. |
| |
| Catalin (ARM64) said |
| "We should have fixed hugetlb_get_unmapped_area() as well when we added |
| support for 52-bit VA. The reason for commit f6795053dac8 was to |
| prevent normal mmap() from returning addresses above 48-bit by default |
| as some user-space had hard assumptions about this. |
| |
| It's a slight ABI change if you do this for hugetlb_get_unmapped_area() |
| but I doubt anyone would notice. It's more likely that the current |
| behaviour would cause issues, so I'd rather have them consistent. |
| |
| Basically when arm64 gained support for 52-bit addresses we did not |
| want user-space calling mmap() to suddenly get such high addresses, |
| otherwise we could have inadvertently broken some programs (similar |
| behaviour to x86 here). Hence we added commit f6795053dac8. But we |
| missed hugetlbfs which could still get such high mmap() addresses. So |
| in theory that's a potential regression that should have bee addressed |
| at the same time as commit f6795053dac8 (and before arm64 enabled |
| 52-bit addresses)" |
| |
| Link: https://lkml.kernel.org/r/ab847b6edb197bffdfe189e70fb4ac76bfe79e0d.1650033747.git.christophe.leroy@csgroup.eu |
| Fixes: f6795053dac8 ("mm: mmap: Allow for "high" userspace addresses") |
| Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu> |
| Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> |
| Cc: Steve Capper <steve.capper@arm.com> |
| Cc: Will Deacon <will.deacon@arm.com> |
| Cc: <stable@vger.kernel.org> [5.0.x] |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/hugetlbfs/inode.c | 9 +++++---- |
| include/linux/sched/mm.h | 8 ++++++++ |
| mm/mmap.c | 8 -------- |
| 3 files changed, 13 insertions(+), 12 deletions(-) |
| |
| --- a/fs/hugetlbfs/inode.c |
| +++ b/fs/hugetlbfs/inode.c |
| @@ -206,7 +206,7 @@ hugetlb_get_unmapped_area_bottomup(struc |
| info.flags = 0; |
| info.length = len; |
| info.low_limit = current->mm->mmap_base; |
| - info.high_limit = TASK_SIZE; |
| + info.high_limit = arch_get_mmap_end(addr); |
| info.align_mask = PAGE_MASK & ~huge_page_mask(h); |
| info.align_offset = 0; |
| return vm_unmapped_area(&info); |
| @@ -222,7 +222,7 @@ hugetlb_get_unmapped_area_topdown(struct |
| info.flags = VM_UNMAPPED_AREA_TOPDOWN; |
| info.length = len; |
| info.low_limit = max(PAGE_SIZE, mmap_min_addr); |
| - info.high_limit = current->mm->mmap_base; |
| + info.high_limit = arch_get_mmap_base(addr, current->mm->mmap_base); |
| info.align_mask = PAGE_MASK & ~huge_page_mask(h); |
| info.align_offset = 0; |
| addr = vm_unmapped_area(&info); |
| @@ -237,7 +237,7 @@ hugetlb_get_unmapped_area_topdown(struct |
| VM_BUG_ON(addr != -ENOMEM); |
| info.flags = 0; |
| info.low_limit = current->mm->mmap_base; |
| - info.high_limit = TASK_SIZE; |
| + info.high_limit = arch_get_mmap_end(addr); |
| addr = vm_unmapped_area(&info); |
| } |
| |
| @@ -251,6 +251,7 @@ hugetlb_get_unmapped_area(struct file *f |
| struct mm_struct *mm = current->mm; |
| struct vm_area_struct *vma; |
| struct hstate *h = hstate_file(file); |
| + const unsigned long mmap_end = arch_get_mmap_end(addr); |
| |
| if (len & ~huge_page_mask(h)) |
| return -EINVAL; |
| @@ -266,7 +267,7 @@ hugetlb_get_unmapped_area(struct file *f |
| if (addr) { |
| addr = ALIGN(addr, huge_page_size(h)); |
| vma = find_vma(mm, addr); |
| - if (TASK_SIZE - len >= addr && |
| + if (mmap_end - len >= addr && |
| (!vma || addr + len <= vm_start_gap(vma))) |
| return addr; |
| } |
| --- a/include/linux/sched/mm.h |
| +++ b/include/linux/sched/mm.h |
| @@ -106,6 +106,14 @@ static inline void mm_update_next_owner( |
| #endif /* CONFIG_MEMCG */ |
| |
| #ifdef CONFIG_MMU |
| +#ifndef arch_get_mmap_end |
| +#define arch_get_mmap_end(addr) (TASK_SIZE) |
| +#endif |
| + |
| +#ifndef arch_get_mmap_base |
| +#define arch_get_mmap_base(addr, base) (base) |
| +#endif |
| + |
| extern void arch_pick_mmap_layout(struct mm_struct *mm, |
| struct rlimit *rlim_stack); |
| extern unsigned long |
| --- a/mm/mmap.c |
| +++ b/mm/mmap.c |
| @@ -2140,14 +2140,6 @@ unsigned long vm_unmapped_area(struct vm |
| return addr; |
| } |
| |
| -#ifndef arch_get_mmap_end |
| -#define arch_get_mmap_end(addr) (TASK_SIZE) |
| -#endif |
| - |
| -#ifndef arch_get_mmap_base |
| -#define arch_get_mmap_base(addr, base) (base) |
| -#endif |
| - |
| /* Get an address range which is currently unmapped. |
| * For shmat() with addr=0. |
| * |