[RFC v2 20/21] selftests/mm: add partial_mremap test for move_page_tables

From: Usama Arif

Date: Thu Feb 26 2026 - 06:41:35 EST


Add test for partial mremap on THP which exercises move_page_tables().
This verifies that partial mremap correctly splits the PMD, moves
only the requested page, and preserves data integrity in both the
moved region and the original mapping.

Signed-off-by: Usama Arif <usama.arif@xxxxxxxxx>
---
.../testing/selftests/mm/thp_pmd_split_test.c | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)

diff --git a/tools/testing/selftests/mm/thp_pmd_split_test.c b/tools/testing/selftests/mm/thp_pmd_split_test.c
index 3c9f05457efec..1f29296759a5b 100644
--- a/tools/testing/selftests/mm/thp_pmd_split_test.c
+++ b/tools/testing/selftests/mm/thp_pmd_split_test.c
@@ -203,4 +203,54 @@ TEST_F(thp_pmd_split, partial_mlock)
self->split_pmd_failed_before);
}

+/*
+ * Partial mremap (move_page_tables)
+ *
+ * Tests that partial mremap of a THP correctly splits the PMD and
+ * moves only the requested portion. This exercises move_page_tables()
+ * which now handles split failures.
+ */
+TEST_F(thp_pmd_split, partial_mremap)
+{
+ void *new_addr;
+ unsigned long *ptr = (unsigned long *)self->aligned;
+ unsigned long *new_ptr;
+ unsigned long pattern = 0xABCDUL;
+ int ret;
+
+ ret = allocate_thp(self->aligned, self->pmdsize);
+ if (ret)
+ SKIP(return, "Failed to allocate THP");
+
+ /* Write pattern to the page we'll move */
+ ptr[self->pagesize / sizeof(unsigned long)] = pattern;
+
+ /* Also write to first and last page to verify they stay intact */
+ ptr[0] = 0x1234UL;
+ ptr[(self->pmdsize - self->pagesize) / sizeof(unsigned long)] = 0x4567UL;
+
+ /* Partial mremap - move one base page from the THP */
+ new_addr = mremap((char *)self->aligned + self->pagesize, self->pagesize,
+ self->pagesize, MREMAP_MAYMOVE);
+ if (new_addr == MAP_FAILED) {
+ if (errno == ENOMEM)
+ SKIP(return, "mremap failed with ENOMEM");
+ ASSERT_NE(new_addr, MAP_FAILED);
+ }
+
+ /* Verify data was moved correctly */
+ new_ptr = (unsigned long *)new_addr;
+ ASSERT_EQ(new_ptr[0], pattern);
+
+ /* Verify surrounding data is intact */
+ ASSERT_EQ(ptr[0], 0x1234UL);
+ ASSERT_EQ(ptr[(self->pmdsize - self->pagesize) / sizeof(unsigned long)], 0x4567UL);
+
+ /* Cleanup the moved page */
+ munmap(new_addr, self->pagesize);
+
+ log_and_check_pmd_split(_metadata, self->split_pmd_before,
+ self->split_pmd_failed_before);
+}
+
TEST_HARNESS_MAIN
--
2.47.3