Re: [PATCH v3 08/13] selftests/mm: ensure destination is hugetlb-backed in hugepage-mremap

From: Sayali Patil

Date: Wed Apr 01 2026 - 16:48:10 EST




On 01/04/26 20:10, Lorenzo Stoakes (Oracle) wrote:
On Wed, Apr 01, 2026 at 04:21:55PM +0200, David Hildenbrand (Arm) wrote:
On 3/27/26 08:16, Sayali Patil wrote:
The hugepage-mremap selftest reserves the destination address using a
anonymous base-page mapping before calling mremap() with MREMAP_FIXED,
while the source region is hugetlb-backed.

When remapping a hugetlb mapping into a base-page VMA may fail with:

mremap: Device or resource busy

This is observed on powerpc hash MMU systems where slice constraints
and page size incompatibilities prevent the remap.

OK so digging in:

mremap -> ... -> vrm_set_new_addr() -> get_unmapped_area() -> ... (in ppc arch
code) -> slice_get_unmapped_area():

unsigned long slice_get_unmapped_area(unsigned long addr, unsigned long len,
unsigned long flags, unsigned int psize,
int topdown)
{
...
/* bunch of checks */

/* If we have MAP_FIXED and failed the above steps, then error out */
if (fixed)
return -EBUSY;

...
}

Is presumably where we hit the issue.



That is weird. An mremap(MREMAP_FIXED) is really just an munmap() + move.

Yeah the weird bit I guess is that we _still_ invoke get_unmapped_area() but
with MAP_FIXED set to indicate that we want the specific address, so it's
subject to the above checks.


Are we sure this is not some actual problem in the hugetlb implementation?

It seems the 'slices' check sees if the _target address_ has an equivalent page
size, presumably hugetlb-mandated, and fails if they're not equivalent, so this
change is just accounting for that.

Yes, this change accounts for that by ensuring the destination is created with MAP_HUGETLB so it has the same page size as the source.


Ensure the destination region is created using MAP_HUGETLB so that both
source and destination VMAs are hugetlb-backed and compatible. Also add
MAP_POPULATE to the destination mapping to prefault hugepages,
matching the behaviour used for other hugetlb mapping in the test and
ensuring deterministic behaviour.

But then the test suddenly requires more hugetlb pages, no? I don't see
a good reason for the MAP_POPULATE, really. It will be discarded either way.

Yeah I'm not sure about the MAP_POPULATE being all that important here.

As far as I understand, without MAP_POPULATE, memory accesses would trigger userfaults, and since the test is single-threaded and has no background handler for the uffd, it would deadlock. MAP_POPULATE ensures the test runs correctly by prefaulting all pages, but please let me know if I’m mistaken.


Update the FLAGS macro to include MAP_HUGETLB | MAP_SHARED |
MAP_POPULATE so that both mappings are hugetlb-backed and compatible.
Also use the macro for the mmap() calls to avoid repeating
the flag combination.

This ensures the test reliably exercises hugetlb mremap instead of
failing due to VMA type mismatch.

Fixes: 12b613206474 ("mm, hugepages: add hugetlb vma mremap() test")
Acked-by: Zi Yan <ziy@xxxxxxxxxx>
Tested-by: Venkat Rao Bagalkote <venkat88@xxxxxxxxxxxxx>
Signed-off-by: Sayali Patil <sayalip@xxxxxxxxxxxxx>
---
tools/testing/selftests/mm/hugepage-mremap.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/tools/testing/selftests/mm/hugepage-mremap.c b/tools/testing/selftests/mm/hugepage-mremap.c
index e611249080d6..48c24a4ba9a7 100644
--- a/tools/testing/selftests/mm/hugepage-mremap.c
+++ b/tools/testing/selftests/mm/hugepage-mremap.c
@@ -31,7 +31,7 @@
#define MB_TO_BYTES(x) (x * 1024 * 1024)

#define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC)
-#define FLAGS (MAP_SHARED | MAP_ANONYMOUS)
+#define FLAGS (MAP_HUGETLB | MAP_SHARED | MAP_POPULATE)

static void check_bytes(char *addr)
{
@@ -121,23 +121,20 @@ int main(int argc, char *argv[])

/* mmap to a PUD aligned address to hopefully trigger pmd sharing. */
unsigned long suggested_addr = 0x7eaa40000000;
- void *haddr = mmap((void *)suggested_addr, length, PROTECTION,
- MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
+ void *haddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0);
ksft_print_msg("Map haddr: Returned address is %p\n", haddr);
if (haddr == MAP_FAILED)
ksft_exit_fail_msg("mmap1: %s\n", strerror(errno));

/* mmap again to a dummy address to hopefully trigger pmd sharing. */
suggested_addr = 0x7daa40000000;
- void *daddr = mmap((void *)suggested_addr, length, PROTECTION,
- MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
+ void *daddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0);
ksft_print_msg("Map daddr: Returned address is %p\n", daddr);
if (daddr == MAP_FAILED)
ksft_exit_fail_msg("mmap3: %s\n", strerror(errno));

suggested_addr = 0x7faa40000000;
- void *vaddr =
- mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0);
+ void *vaddr = mmap((void *)suggested_addr, length, PROTECTION, FLAGS, fd, 0);
ksft_print_msg("Map vaddr: Returned address is %p\n", vaddr);
if (vaddr == MAP_FAILED)
ksft_exit_fail_msg("mmap2: %s\n", strerror(errno));


--
Cheers,

David

Cheers, Lorenzo