Skip to content

Commit a4cb3b2

Browse files
joelagnelakpm00
authored andcommitted
selftests: mm: add a test for remapping to area immediately after existing mapping
This patch adds support for verifying that we correctly handle the situation where something is already mapped before the destination of the remap. Any realignment of destination address and PMD-copy will destroy that existing mapping. In such cases, we need to avoid doing the optimization. To test this, we map an area called the preamble before the remap region. Then we verify after the mremap operation that this region did not get corrupted. Putting some prints in the kernel, I verified that we optimize correctly in different situations: Optimize when there is alignment and no previous mapping (this is tested by previous patch). <prints> can_align_down(old_vma->vm_start=2900000, old_addr=2900000, mask=-2097152): 0 can_align_down(new_vma->vm_start=2f00000, new_addr=2f00000, mask=-2097152): 0 === Starting move_page_tables === Doing PUD move for 2800000 -> 2e00000 of extent=200000 <-- Optimization Doing PUD move for 2a00000 -> 3000000 of extent=200000 Doing PUD move for 2c00000 -> 3200000 of extent=200000 </prints> Don't optimize when there is alignment but there is previous mapping (this is tested by this patch). Notice that can_align_down() returns 1 for the destination mapping as we detected there is something there. <prints> can_align_down(old_vma->vm_start=2900000, old_addr=2900000, mask=-2097152): 0 can_align_down(new_vma->vm_start=5700000, new_addr=5700000, mask=-2097152): 1 === Starting move_page_tables === Doing move_ptes for 2900000 -> 5700000 of extent=100000 <-- Unoptimized Doing PUD move for 2a00000 -> 5800000 of extent=200000 Doing PUD move for 2c00000 -> 5a00000 of extent=200000 </prints> Link: https://lkml.kernel.org/r/20230903151328.2981432-6-joel@joelfernandes.org Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org> Reviewed-by: Lorenzo Stoakes <lstoakes@gmail.com> Cc: Kalesh Singh <kaleshsingh@google.com> Cc: "Kirill A. Shutemov" <kirill@shutemov.name> Cc: Liam R. Howlett <Liam.Howlett@oracle.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Lokesh Gidra <lokeshgidra@google.com> Cc: Michal Hocko <mhocko@suse.com> Cc: Paul E. McKenney <paulmck@kernel.org> Cc: Shuah Khan <shuah@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 8ed873d commit a4cb3b2

File tree

1 file changed

+52
-5
lines changed

1 file changed

+52
-5
lines changed

tools/testing/selftests/mm/mremap_test.c

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct config {
2929
unsigned long long dest_alignment;
3030
unsigned long long region_size;
3131
int overlapping;
32+
int dest_preamble_size;
3233
};
3334

3435
struct test {
@@ -283,7 +284,7 @@ static void *get_source_mapping(struct config c)
283284
static long long remap_region(struct config c, unsigned int threshold_mb,
284285
char pattern_seed)
285286
{
286-
void *addr, *src_addr, *dest_addr;
287+
void *addr, *src_addr, *dest_addr, *dest_preamble_addr;
287288
unsigned long long i;
288289
struct timespec t_start = {0, 0}, t_end = {0, 0};
289290
long long start_ns, end_ns, align_mask, ret, offset;
@@ -300,7 +301,7 @@ static long long remap_region(struct config c, unsigned int threshold_mb,
300301
goto out;
301302
}
302303

303-
/* Set byte pattern */
304+
/* Set byte pattern for source block. */
304305
srand(pattern_seed);
305306
for (i = 0; i < threshold; i++)
306307
memset((char *) src_addr + i, (char) rand(), 1);
@@ -312,6 +313,9 @@ static long long remap_region(struct config c, unsigned int threshold_mb,
312313
addr = (void *) (((unsigned long long) src_addr + c.region_size
313314
+ offset) & align_mask);
314315

316+
/* Remap after the destination block preamble. */
317+
addr += c.dest_preamble_size;
318+
315319
/* See comment in get_source_mapping() */
316320
if (!((unsigned long long) addr & c.dest_alignment))
317321
addr = (void *) ((unsigned long long) addr | c.dest_alignment);
@@ -327,6 +331,24 @@ static long long remap_region(struct config c, unsigned int threshold_mb,
327331
addr += c.dest_alignment;
328332
}
329333

334+
if (c.dest_preamble_size) {
335+
dest_preamble_addr = mmap((void *) addr - c.dest_preamble_size, c.dest_preamble_size,
336+
PROT_READ | PROT_WRITE,
337+
MAP_FIXED_NOREPLACE | MAP_ANONYMOUS | MAP_SHARED,
338+
-1, 0);
339+
if (dest_preamble_addr == MAP_FAILED) {
340+
ksft_print_msg("Failed to map dest preamble region: %s\n",
341+
strerror(errno));
342+
ret = -1;
343+
goto clean_up_src;
344+
}
345+
346+
/* Set byte pattern for the dest preamble block. */
347+
srand(pattern_seed);
348+
for (i = 0; i < c.dest_preamble_size; i++)
349+
memset((char *) dest_preamble_addr + i, (char) rand(), 1);
350+
}
351+
330352
clock_gettime(CLOCK_MONOTONIC, &t_start);
331353
dest_addr = mremap(src_addr, c.region_size, c.region_size,
332354
MREMAP_MAYMOVE|MREMAP_FIXED, (char *) addr);
@@ -335,7 +357,7 @@ static long long remap_region(struct config c, unsigned int threshold_mb,
335357
if (dest_addr == MAP_FAILED) {
336358
ksft_print_msg("mremap failed: %s\n", strerror(errno));
337359
ret = -1;
338-
goto clean_up_src;
360+
goto clean_up_dest_preamble;
339361
}
340362

341363
/* Verify byte pattern after remapping */
@@ -353,6 +375,23 @@ static long long remap_region(struct config c, unsigned int threshold_mb,
353375
}
354376
}
355377

378+
/* Verify the dest preamble byte pattern after remapping */
379+
if (c.dest_preamble_size) {
380+
srand(pattern_seed);
381+
for (i = 0; i < c.dest_preamble_size; i++) {
382+
char c = (char) rand();
383+
384+
if (((char *) dest_preamble_addr)[i] != c) {
385+
ksft_print_msg("Preamble data after remap doesn't match at offset %d\n",
386+
i);
387+
ksft_print_msg("Expected: %#x\t Got: %#x\n", c & 0xff,
388+
((char *) dest_preamble_addr)[i] & 0xff);
389+
ret = -1;
390+
goto clean_up_dest;
391+
}
392+
}
393+
}
394+
356395
start_ns = t_start.tv_sec * NS_PER_SEC + t_start.tv_nsec;
357396
end_ns = t_end.tv_sec * NS_PER_SEC + t_end.tv_nsec;
358397
ret = end_ns - start_ns;
@@ -365,6 +404,9 @@ static long long remap_region(struct config c, unsigned int threshold_mb,
365404
*/
366405
clean_up_dest:
367406
munmap(dest_addr, c.region_size);
407+
clean_up_dest_preamble:
408+
if (c.dest_preamble_size && dest_preamble_addr)
409+
munmap(dest_preamble_addr, c.dest_preamble_size);
368410
clean_up_src:
369411
munmap(src_addr, c.region_size);
370412
out:
@@ -440,7 +482,7 @@ static int parse_args(int argc, char **argv, unsigned int *threshold_mb,
440482
return 0;
441483
}
442484

443-
#define MAX_TEST 14
485+
#define MAX_TEST 15
444486
#define MAX_PERF_TEST 3
445487
int main(int argc, char **argv)
446488
{
@@ -449,7 +491,7 @@ int main(int argc, char **argv)
449491
unsigned int threshold_mb = VALIDATION_DEFAULT_THRESHOLD;
450492
unsigned int pattern_seed;
451493
int num_expand_tests = 2;
452-
struct test test_cases[MAX_TEST];
494+
struct test test_cases[MAX_TEST] = {};
453495
struct test perf_test_cases[MAX_PERF_TEST];
454496
int page_size;
455497
time_t t;
@@ -510,6 +552,11 @@ int main(int argc, char **argv)
510552
test_cases[13] = MAKE_TEST(_1MB, _1MB, _5MB, NON_OVERLAPPING, EXPECT_SUCCESS,
511553
"5MB mremap - Source 1MB-aligned, Destination 1MB-aligned");
512554

555+
/* Src and Dest addr 1MB aligned. 5MB mremap. */
556+
test_cases[14] = MAKE_TEST(_1MB, _1MB, _5MB, NON_OVERLAPPING, EXPECT_SUCCESS,
557+
"5MB mremap - Source 1MB-aligned, Dest 1MB-aligned with 40MB Preamble");
558+
test_cases[14].config.dest_preamble_size = 10 * _4MB;
559+
513560
perf_test_cases[0] = MAKE_TEST(page_size, page_size, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
514561
"1GB mremap - Source PTE-aligned, Destination PTE-aligned");
515562
/*

0 commit comments

Comments
 (0)