Skip to content

Commit 96204e1

Browse files
Ryan Robertsakpm00
authored andcommitted
mm: thp_get_unmapped_area must honour topdown preference
The addition of commit efa7df3 ("mm: align larger anonymous mappings on THP boundaries") caused the "virtual_address_range" mm selftest to start failing on arm64. Let's fix that regression. There were 2 visible problems when running the test; 1) it takes much longer to execute, and 2) the test fails. Both are related: The (first part of the) test allocates as many 1GB anonymous blocks as it can in the low 256TB of address space, passing NULL as the addr hint to mmap. Before the faulty patch, all allocations were abutted and contained in a single, merged VMA. However, after this patch, each allocation is in its own VMA, and there is a 2M gap between each VMA. This causes the 2 problems in the test: 1) mmap becomes MUCH slower because there are so many VMAs to check to find a new 1G gap. 2) mmap fails once it hits the VMA limit (/proc/sys/vm/max_map_count). Hitting this limit then causes a subsequent calloc() to fail, which causes the test to fail. The problem is that arm64 (unlike x86) selects ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT. But __thp_get_unmapped_area() allocates len+2M then always aligns to the bottom of the discovered gap. That causes the 2M hole. Fix this by detecting cases where we can still achive the alignment goal when moved to the top of the allocated area, if configured to prefer top-down allocation. While we are at it, fix thp_get_unmapped_area's use of pgoff, which should always be zero for anonymous mappings. Prior to the faulty change, while it was possible for user space to pass in pgoff!=0, the old mm->get_unmapped_area() handler would not use it. thp_get_unmapped_area() does use it, so let's explicitly zero it before calling the handler. This should also be the correct behavior for arches that define their own get_unmapped_area() handler. Link: https://lkml.kernel.org/r/20240123171420.3970220-1-ryan.roberts@arm.com Fixes: efa7df3 ("mm: align larger anonymous mappings on THP boundaries") Closes: https://lore.kernel.org/linux-mm/1e8f5ac7-54ce-433a-ae53-81522b2320e1@arm.com/ Signed-off-by: Ryan Roberts <ryan.roberts@arm.com> Reviewed-by: Yang Shi <shy828301@gmail.com> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Rik van Riel <riel@surriel.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 4ef9ad1 commit 96204e1

File tree

2 files changed

+12
-4
lines changed

2 files changed

+12
-4
lines changed

mm/huge_memory.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ static unsigned long __thp_get_unmapped_area(struct file *filp,
810810
{
811811
loff_t off_end = off + len;
812812
loff_t off_align = round_up(off, size);
813-
unsigned long len_pad, ret;
813+
unsigned long len_pad, ret, off_sub;
814814

815815
if (IS_ENABLED(CONFIG_32BIT) || in_compat_syscall())
816816
return 0;
@@ -839,7 +839,13 @@ static unsigned long __thp_get_unmapped_area(struct file *filp,
839839
if (ret == addr)
840840
return addr;
841841

842-
ret += (off - ret) & (size - 1);
842+
off_sub = (off - ret) & (size - 1);
843+
844+
if (current->mm->get_unmapped_area == arch_get_unmapped_area_topdown &&
845+
!off_sub)
846+
return ret + size;
847+
848+
ret += off_sub;
843849
return ret;
844850
}
845851

mm/mmap.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,15 +1825,17 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
18251825
/*
18261826
* mmap_region() will call shmem_zero_setup() to create a file,
18271827
* so use shmem's get_unmapped_area in case it can be huge.
1828-
* do_mmap() will clear pgoff, so match alignment.
18291828
*/
1830-
pgoff = 0;
18311829
get_area = shmem_get_unmapped_area;
18321830
} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
18331831
/* Ensures that larger anonymous mappings are THP aligned. */
18341832
get_area = thp_get_unmapped_area;
18351833
}
18361834

1835+
/* Always treat pgoff as zero for anonymous memory. */
1836+
if (!file)
1837+
pgoff = 0;
1838+
18371839
addr = get_area(file, addr, len, pgoff, flags);
18381840
if (IS_ERR_VALUE(addr))
18391841
return addr;

0 commit comments

Comments
 (0)