Skip to content

Commit 8859025

Browse files
Shijie Hutorvalds
authored andcommitted
hugetlbfs: get unmapped area below TASK_UNMAPPED_BASE for hugetlbfs
In a 32-bit program, running on arm64 architecture. When the address space below mmap base is completely exhausted, shmat() for huge pages will return ENOMEM, but shmat() for normal pages can still success on no-legacy mode. This seems not fair. For normal pages, the calling trace of get_unmapped_area() is: => mm->get_unmapped_area() if on legacy mode, => arch_get_unmapped_area() => vm_unmapped_area() if on no-legacy mode, => arch_get_unmapped_area_topdown() => vm_unmapped_area() For huge pages, the calling trace of get_unmapped_area() is: => file->f_op->get_unmapped_area() => hugetlb_get_unmapped_area() => vm_unmapped_area() To solve this issue, we only need to make hugetlb_get_unmapped_area() take the same way as mm->get_unmapped_area(). Add *bottomup() and *topdown() for hugetlbfs, and check current mm->get_unmapped_area() to decide which one to use. If mm->get_unmapped_area is equal to arch_get_unmapped_area_topdown(), hugetlb_get_unmapped_area() calls topdown routine, otherwise calls bottomup routine. Reported-by: kbuild test robot <[email protected]> Signed-off-by: Shijie Hu <[email protected]> Signed-off-by: Mike Kravetz <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Cc: Will Deacon <[email protected]> Cc: Xiaoming Ni <[email protected]> Cc: Kefeng Wang <[email protected]> Cc: yangerkun <[email protected]> Cc: ChenGang <[email protected]> Cc: Chen Jie <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 4360dfa commit 8859025

File tree

1 file changed

+59
-8
lines changed

1 file changed

+59
-8
lines changed

fs/hugetlbfs/inode.c

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <linux/uio.h>
3939

4040
#include <linux/uaccess.h>
41+
#include <linux/sched/mm.h>
4142

4243
static const struct super_operations hugetlbfs_ops;
4344
static const struct address_space_operations hugetlbfs_aops;
@@ -190,14 +191,61 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
190191
*/
191192

192193
#ifndef HAVE_ARCH_HUGETLB_UNMAPPED_AREA
194+
static unsigned long
195+
hugetlb_get_unmapped_area_bottomup(struct file *file, unsigned long addr,
196+
unsigned long len, unsigned long pgoff, unsigned long flags)
197+
{
198+
struct hstate *h = hstate_file(file);
199+
struct vm_unmapped_area_info info;
200+
201+
info.flags = 0;
202+
info.length = len;
203+
info.low_limit = current->mm->mmap_base;
204+
info.high_limit = TASK_SIZE;
205+
info.align_mask = PAGE_MASK & ~huge_page_mask(h);
206+
info.align_offset = 0;
207+
return vm_unmapped_area(&info);
208+
}
209+
210+
static unsigned long
211+
hugetlb_get_unmapped_area_topdown(struct file *file, unsigned long addr,
212+
unsigned long len, unsigned long pgoff, unsigned long flags)
213+
{
214+
struct hstate *h = hstate_file(file);
215+
struct vm_unmapped_area_info info;
216+
217+
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
218+
info.length = len;
219+
info.low_limit = max(PAGE_SIZE, mmap_min_addr);
220+
info.high_limit = current->mm->mmap_base;
221+
info.align_mask = PAGE_MASK & ~huge_page_mask(h);
222+
info.align_offset = 0;
223+
addr = vm_unmapped_area(&info);
224+
225+
/*
226+
* A failed mmap() very likely causes application failure,
227+
* so fall back to the bottom-up function here. This scenario
228+
* can happen with large stack limits and large mmap()
229+
* allocations.
230+
*/
231+
if (unlikely(offset_in_page(addr))) {
232+
VM_BUG_ON(addr != -ENOMEM);
233+
info.flags = 0;
234+
info.low_limit = current->mm->mmap_base;
235+
info.high_limit = TASK_SIZE;
236+
addr = vm_unmapped_area(&info);
237+
}
238+
239+
return addr;
240+
}
241+
193242
static unsigned long
194243
hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
195244
unsigned long len, unsigned long pgoff, unsigned long flags)
196245
{
197246
struct mm_struct *mm = current->mm;
198247
struct vm_area_struct *vma;
199248
struct hstate *h = hstate_file(file);
200-
struct vm_unmapped_area_info info;
201249

202250
if (len & ~huge_page_mask(h))
203251
return -EINVAL;
@@ -218,13 +266,16 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
218266
return addr;
219267
}
220268

221-
info.flags = 0;
222-
info.length = len;
223-
info.low_limit = TASK_UNMAPPED_BASE;
224-
info.high_limit = TASK_SIZE;
225-
info.align_mask = PAGE_MASK & ~huge_page_mask(h);
226-
info.align_offset = 0;
227-
return vm_unmapped_area(&info);
269+
/*
270+
* Use mm->get_unmapped_area value as a hint to use topdown routine.
271+
* If architectures have special needs, they should define their own
272+
* version of hugetlb_get_unmapped_area.
273+
*/
274+
if (mm->get_unmapped_area == arch_get_unmapped_area_topdown)
275+
return hugetlb_get_unmapped_area_topdown(file, addr, len,
276+
pgoff, flags);
277+
return hugetlb_get_unmapped_area_bottomup(file, addr, len,
278+
pgoff, flags);
228279
}
229280
#endif
230281

0 commit comments

Comments
 (0)