* [PATCH 0/3] tools/testing: expand mremap testing
@ 2025-07-21 17:33 Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 1/3] tools/testing/selftests: add mremap() shrink test for multiple VMAs Lorenzo Stoakes
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Lorenzo Stoakes @ 2025-07-21 17:33 UTC (permalink / raw)
To: Andrew Morton
Cc: Liam R . Howlett, Vlastimil Babka, Jann Horn, Pedro Falcato,
linux-mm, linux-kernel
Expand our mremap() testing to further assert that behaviour is as
expected.
There is a poorly documented mremap() feature whereby it is possible to
mremap() multiple VMAs (even with gaps) when shrinking, as long as the
resultant shrunk range spans only a single VMA.
So we start by asserting this behaviour functions correctly both with an
in-place shrink and a shrink/move.
Next, we further test the newly introduced ability to mremap() multiple
VMAs when performing a MAP_FIXED move (that is without the size being
changed), firstly by asserting that MREMAP_DONTUNMAP has no bearing on this
behaviour.
Finally, we explicitly test that such moves, when splitting source VMAs,
function correctly.
Lorenzo Stoakes (3):
tools/testing/selftests: add mremap() shrink test for multiple VMAs
tools/testing/selftests: test MREMAP_DONTUNMAP on multiple VMA move
tools/testing/selftests: explicitly test split multi VMA mremap move
tools/testing/selftests/mm/mremap_test.c | 235 ++++++++++++++++++++++-
1 file changed, 225 insertions(+), 10 deletions(-)
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/3] tools/testing/selftests: add mremap() shrink test for multiple VMAs
2025-07-21 17:33 [PATCH 0/3] tools/testing: expand mremap testing Lorenzo Stoakes
@ 2025-07-21 17:33 ` Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 2/3] tools/testing/selftests: test MREMAP_DONTUNMAP on multiple VMA move Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 3/3] tools/testing/selftests: explicitly test split multi VMA mremap move Lorenzo Stoakes
2 siblings, 0 replies; 4+ messages in thread
From: Lorenzo Stoakes @ 2025-07-21 17:33 UTC (permalink / raw)
To: Andrew Morton
Cc: Liam R . Howlett, Vlastimil Babka, Jann Horn, Pedro Falcato,
linux-mm, linux-kernel
There is a an apparently little-known feature of mremap() whereby, in stark
contrast to other modes (other than the recently introduced capacity to
move multiple VMAs), the input source range span multiple VMAs with gaps
between.
This is, when shrinking a VMA, whether moving it or not, and the shrink
would reduce the range to a single VMA - this is permitted, as the shrink
is actioned by an unmap.
This patch adds tests to assert that this behaves as expected.
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
tools/testing/selftests/mm/mremap_test.c | 83 +++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c
index 0a49be11e614..141a9032414e 100644
--- a/tools/testing/selftests/mm/mremap_test.c
+++ b/tools/testing/selftests/mm/mremap_test.c
@@ -523,6 +523,85 @@ static void mremap_move_multiple_vmas(unsigned int pattern_seed,
ksft_test_result_fail("%s\n", test_name);
}
+static void mremap_shrink_multiple_vmas(unsigned long page_size,
+ bool inplace)
+{
+ char *test_name = "mremap shrink multiple vmas";
+ const size_t size = 10 * page_size;
+ bool success = true;
+ char *ptr, *tgt_ptr;
+ void *res;
+ int i;
+
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (ptr == MAP_FAILED) {
+ perror("mmap");
+ success = false;
+ goto out;
+ }
+
+ tgt_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (tgt_ptr == MAP_FAILED) {
+ perror("mmap");
+ success = false;
+ goto out;
+ }
+ if (munmap(tgt_ptr, size)) {
+ perror("munmap");
+ success = false;
+ goto out_unmap;
+ }
+
+ /*
+ * Unmap so we end up with:
+ *
+ * 0 2 4 6 8 10 offset in buffer
+ * |*| |*| |*| |*| |*| |*|
+ * |*| |*| |*| |*| |*| |*|
+ */
+ for (i = 1; i < 10; i += 2) {
+ if (munmap(&ptr[i * page_size], page_size)) {
+ perror("munmap");
+ success = false;
+ goto out_unmap;
+ }
+ }
+
+ /*
+ * Shrink in-place across multiple VMAs and gaps so we end up with:
+ *
+ * 0
+ * |*|
+ * |*|
+ */
+ if (inplace)
+ res = mremap(ptr, size, page_size, 0);
+ else
+ res = mremap(ptr, size, page_size, MREMAP_MAYMOVE | MREMAP_FIXED,
+ tgt_ptr);
+
+ if (res == MAP_FAILED) {
+ perror("mremap");
+ success = false;
+ goto out_unmap;
+ }
+
+out_unmap:
+ if (munmap(tgt_ptr, size))
+ perror("munmap tgt");
+ if (munmap(ptr, size))
+ perror("munmap src");
+out:
+ if (success)
+ ksft_test_result_pass("%s%s\n", test_name,
+ inplace ? " [inplace]" : "");
+ else
+ ksft_test_result_fail("%s%s\n", test_name,
+ inplace ? " [inplace]" : "");
+}
+
/* Returns the time taken for the remap on success else returns -1. */
static long long remap_region(struct config c, unsigned int threshold_mb,
char *rand_addr)
@@ -864,7 +943,7 @@ int main(int argc, char **argv)
char *rand_addr;
size_t rand_size;
int num_expand_tests = 2;
- int num_misc_tests = 3;
+ int num_misc_tests = 5;
struct test test_cases[MAX_TEST] = {};
struct test perf_test_cases[MAX_PERF_TEST];
int page_size;
@@ -992,6 +1071,8 @@ int main(int argc, char **argv)
mremap_move_within_range(pattern_seed, rand_addr);
mremap_move_1mb_from_start(pattern_seed, rand_addr);
mremap_move_multiple_vmas(pattern_seed, page_size);
+ mremap_shrink_multiple_vmas(page_size, /* inplace= */true);
+ mremap_shrink_multiple_vmas(page_size, /* inplace= */false);
if (run_perf_tests) {
ksft_print_msg("\n%s\n",
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] tools/testing/selftests: test MREMAP_DONTUNMAP on multiple VMA move
2025-07-21 17:33 [PATCH 0/3] tools/testing: expand mremap testing Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 1/3] tools/testing/selftests: add mremap() shrink test for multiple VMAs Lorenzo Stoakes
@ 2025-07-21 17:33 ` Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 3/3] tools/testing/selftests: explicitly test split multi VMA mremap move Lorenzo Stoakes
2 siblings, 0 replies; 4+ messages in thread
From: Lorenzo Stoakes @ 2025-07-21 17:33 UTC (permalink / raw)
To: Andrew Morton
Cc: Liam R . Howlett, Vlastimil Babka, Jann Horn, Pedro Falcato,
linux-mm, linux-kernel
We support MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_DONTUNMAP for moving
multiple VMAs via mremap(), so assert that the tests pass with both
MREMAP_DONTUNMAP set and not set.
Additionally, add success = false settings when mremap() fails. This is
something that cannot realistically happen, so in no way impacted test
outcome, but it is incorrect to indicate a test pass when something has
failed.
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
tools/testing/selftests/mm/mremap_test.c | 29 ++++++++++++++++--------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c
index 141a9032414e..d3b4772cd8c9 100644
--- a/tools/testing/selftests/mm/mremap_test.c
+++ b/tools/testing/selftests/mm/mremap_test.c
@@ -406,14 +406,19 @@ static bool is_multiple_vma_range_ok(unsigned int pattern_seed,
}
static void mremap_move_multiple_vmas(unsigned int pattern_seed,
- unsigned long page_size)
+ unsigned long page_size,
+ bool dont_unmap)
{
+ int mremap_flags = MREMAP_FIXED | MREMAP_MAYMOVE;
char *test_name = "mremap move multiple vmas";
const size_t size = 11 * page_size;
bool success = true;
char *ptr, *tgt_ptr;
int i;
+ if (dont_unmap)
+ mremap_flags |= MREMAP_DONTUNMAP;
+
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (ptr == MAP_FAILED) {
@@ -467,8 +472,7 @@ static void mremap_move_multiple_vmas(unsigned int pattern_seed,
}
/* First, just move the whole thing. */
- if (mremap(ptr, size, size,
- MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr) == MAP_FAILED) {
+ if (mremap(ptr, size, size, mremap_flags, tgt_ptr) == MAP_FAILED) {
perror("mremap");
success = false;
goto out_unmap;
@@ -480,9 +484,10 @@ static void mremap_move_multiple_vmas(unsigned int pattern_seed,
}
/* Move next to itself. */
- if (mremap(tgt_ptr, size, size,
- MREMAP_MAYMOVE | MREMAP_FIXED, &tgt_ptr[size]) == MAP_FAILED) {
+ if (mremap(tgt_ptr, size, size, mremap_flags,
+ &tgt_ptr[size]) == MAP_FAILED) {
perror("mremap");
+ success = false;
goto out_unmap;
}
/* Check that the move is ok. */
@@ -500,8 +505,9 @@ static void mremap_move_multiple_vmas(unsigned int pattern_seed,
}
/* Move and overwrite. */
if (mremap(&tgt_ptr[size], size, size,
- MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr) == MAP_FAILED) {
+ mremap_flags, tgt_ptr) == MAP_FAILED) {
perror("mremap");
+ success = false;
goto out_unmap;
}
/* Check that the move is ok. */
@@ -518,9 +524,11 @@ static void mremap_move_multiple_vmas(unsigned int pattern_seed,
out:
if (success)
- ksft_test_result_pass("%s\n", test_name);
+ ksft_test_result_pass("%s%s\n", test_name,
+ dont_unmap ? " [dontunnmap]" : "");
else
- ksft_test_result_fail("%s\n", test_name);
+ ksft_test_result_fail("%s%s\n", test_name,
+ dont_unmap ? " [dontunnmap]" : "");
}
static void mremap_shrink_multiple_vmas(unsigned long page_size,
@@ -943,7 +951,7 @@ int main(int argc, char **argv)
char *rand_addr;
size_t rand_size;
int num_expand_tests = 2;
- int num_misc_tests = 5;
+ int num_misc_tests = 6;
struct test test_cases[MAX_TEST] = {};
struct test perf_test_cases[MAX_PERF_TEST];
int page_size;
@@ -1070,9 +1078,10 @@ int main(int argc, char **argv)
mremap_move_within_range(pattern_seed, rand_addr);
mremap_move_1mb_from_start(pattern_seed, rand_addr);
- mremap_move_multiple_vmas(pattern_seed, page_size);
mremap_shrink_multiple_vmas(page_size, /* inplace= */true);
mremap_shrink_multiple_vmas(page_size, /* inplace= */false);
+ mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ false);
+ mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ true);
if (run_perf_tests) {
ksft_print_msg("\n%s\n",
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] tools/testing/selftests: explicitly test split multi VMA mremap move
2025-07-21 17:33 [PATCH 0/3] tools/testing: expand mremap testing Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 1/3] tools/testing/selftests: add mremap() shrink test for multiple VMAs Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 2/3] tools/testing/selftests: test MREMAP_DONTUNMAP on multiple VMA move Lorenzo Stoakes
@ 2025-07-21 17:33 ` Lorenzo Stoakes
2 siblings, 0 replies; 4+ messages in thread
From: Lorenzo Stoakes @ 2025-07-21 17:33 UTC (permalink / raw)
To: Andrew Morton
Cc: Liam R . Howlett, Vlastimil Babka, Jann Horn, Pedro Falcato,
linux-mm, linux-kernel
Check that moving a range of VMAs where we are offset into the first and
last VMAs works correctly.
This results in the VMAs being split at these points at which we are offset
into VMAs.
We explicitly test both the ordinary MREMAP_FIXED multi VMA move case and
the MREMAP_DONTUNMAP multi VMA move case.
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
tools/testing/selftests/mm/mremap_test.c | 127 ++++++++++++++++++++++-
1 file changed, 126 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/mm/mremap_test.c b/tools/testing/selftests/mm/mremap_test.c
index d3b4772cd8c9..fccf9e797a0c 100644
--- a/tools/testing/selftests/mm/mremap_test.c
+++ b/tools/testing/selftests/mm/mremap_test.c
@@ -610,6 +610,129 @@ static void mremap_shrink_multiple_vmas(unsigned long page_size,
inplace ? " [inplace]" : "");
}
+static void mremap_move_multiple_vmas_split(unsigned int pattern_seed,
+ unsigned long page_size,
+ bool dont_unmap)
+{
+ char *test_name = "mremap move multiple vmas split";
+ int mremap_flags = MREMAP_FIXED | MREMAP_MAYMOVE;
+ const size_t size = 10 * page_size;
+ bool success = true;
+ char *ptr, *tgt_ptr;
+ int i;
+
+ if (dont_unmap)
+ mremap_flags |= MREMAP_DONTUNMAP;
+
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (ptr == MAP_FAILED) {
+ perror("mmap");
+ success = false;
+ goto out;
+ }
+
+ tgt_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (tgt_ptr == MAP_FAILED) {
+ perror("mmap");
+ success = false;
+ goto out;
+ }
+ if (munmap(tgt_ptr, size)) {
+ perror("munmap");
+ success = false;
+ goto out_unmap;
+ }
+
+ /*
+ * Unmap so we end up with:
+ *
+ * 0 1 2 3 4 5 6 7 8 9 10 offset in buffer
+ * |**********| |*******|
+ * |**********| |*******|
+ * 0 1 2 3 4 5 6 7 8 9 pattern offset
+ */
+ if (munmap(&ptr[5 * page_size], page_size)) {
+ perror("munmap");
+ success = false;
+ goto out_unmap;
+ }
+
+ /* Set up random patterns. */
+ srand(pattern_seed);
+ for (i = 0; i < 10; i++) {
+ int j;
+ char *buf = &ptr[i * page_size];
+
+ if (i == 5)
+ continue;
+
+ for (j = 0; j < page_size; j++)
+ buf[j] = rand();
+ }
+
+ /*
+ * Move the below:
+ *
+ * <------------->
+ * 0 1 2 3 4 5 6 7 8 9 10 offset in buffer
+ * |**********| |*******|
+ * |**********| |*******|
+ * 0 1 2 3 4 5 6 7 8 9 pattern offset
+ *
+ * Into:
+ *
+ * 0 1 2 3 4 5 6 7 offset in buffer
+ * |*****| |*****|
+ * |*****| |*****|
+ * 2 3 4 5 6 7 pattern offset
+ */
+ if (mremap(&ptr[2 * page_size], size - 3 * page_size, size - 3 * page_size,
+ mremap_flags, tgt_ptr) == MAP_FAILED) {
+ perror("mremap");
+ success = false;
+ goto out_unmap;
+ }
+
+ /* Offset into random pattern. */
+ srand(pattern_seed);
+ for (i = 0; i < 2 * page_size; i++)
+ rand();
+
+ /* Check pattern. */
+ for (i = 0; i < 7; i++) {
+ int j;
+ char *buf = &tgt_ptr[i * page_size];
+
+ if (i == 3)
+ continue;
+
+ for (j = 0; j < page_size; j++) {
+ char chr = rand();
+
+ if (chr != buf[j]) {
+ ksft_print_msg("page %d offset %d corrupted, expected %d got %d\n",
+ i, j, chr, buf[j]);
+ goto out_unmap;
+ }
+ }
+ }
+
+out_unmap:
+ if (munmap(tgt_ptr, size))
+ perror("munmap tgt");
+ if (munmap(ptr, size))
+ perror("munmap src");
+out:
+ if (success)
+ ksft_test_result_pass("%s%s\n", test_name,
+ dont_unmap ? " [dontunnmap]" : "");
+ else
+ ksft_test_result_fail("%s%s\n", test_name,
+ dont_unmap ? " [dontunnmap]" : "");
+}
+
/* Returns the time taken for the remap on success else returns -1. */
static long long remap_region(struct config c, unsigned int threshold_mb,
char *rand_addr)
@@ -951,7 +1074,7 @@ int main(int argc, char **argv)
char *rand_addr;
size_t rand_size;
int num_expand_tests = 2;
- int num_misc_tests = 6;
+ int num_misc_tests = 8;
struct test test_cases[MAX_TEST] = {};
struct test perf_test_cases[MAX_PERF_TEST];
int page_size;
@@ -1082,6 +1205,8 @@ int main(int argc, char **argv)
mremap_shrink_multiple_vmas(page_size, /* inplace= */false);
mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ false);
mremap_move_multiple_vmas(pattern_seed, page_size, /* dontunmap= */ true);
+ mremap_move_multiple_vmas_split(pattern_seed, page_size, /* dontunmap= */ false);
+ mremap_move_multiple_vmas_split(pattern_seed, page_size, /* dontunmap= */ true);
if (run_perf_tests) {
ksft_print_msg("\n%s\n",
--
2.50.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-07-21 19:03 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-21 17:33 [PATCH 0/3] tools/testing: expand mremap testing Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 1/3] tools/testing/selftests: add mremap() shrink test for multiple VMAs Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 2/3] tools/testing/selftests: test MREMAP_DONTUNMAP on multiple VMA move Lorenzo Stoakes
2025-07-21 17:33 ` [PATCH 3/3] tools/testing/selftests: explicitly test split multi VMA mremap move Lorenzo Stoakes
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).