| From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> |
| Subject: tools/testing/selftests: add mremap() unfaulted/faulted test cases |
| Date: Wed, 2 Jul 2025 09:47:17 +0100 |
| |
| Assert that mremap() behaviour is as expected when moving around unfaulted |
| VMAs immediately adjacent to faulted ones, as well as moving around |
| faulted VMAs and placing them back immediately adjacent to the VMA from |
| which they were moved. |
| |
| This also introduces a shared helper for the syscall version of mremap() |
| so we don't encounter any issues with libc filtering parameters. |
| |
| Link: https://lkml.kernel.org/r/20250702084717.21360-1-lorenzo.stoakes@oracle.com |
| Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> |
| Cc: Jann Horn <jannh@google.com> |
| Cc: Liam Howlett <liam.howlett@oracle.com> |
| Cc: Vlastimil Babka <vbabka@suse.cz> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| tools/testing/selftests/mm/merge.c | 599 ++++++++++++++++++++++++- |
| tools/testing/selftests/mm/vm_util.c | 8 |
| tools/testing/selftests/mm/vm_util.h | 3 |
| 3 files changed, 608 insertions(+), 2 deletions(-) |
| |
| --- a/tools/testing/selftests/mm/merge.c~tools-testing-selftests-add-mremap-unfaulted-faulted-test-cases |
| +++ a/tools/testing/selftests/mm/merge.c |
| @@ -13,6 +13,7 @@ |
| #include <sys/wait.h> |
| #include <linux/perf_event.h> |
| #include "vm_util.h" |
| +#include <linux/mman.h> |
| |
| FIXTURE(merge) |
| { |
| @@ -25,7 +26,7 @@ FIXTURE_SETUP(merge) |
| { |
| self->page_size = psize(); |
| /* Carve out PROT_NONE region to map over. */ |
| - self->carveout = mmap(NULL, 12 * self->page_size, PROT_NONE, |
| + self->carveout = mmap(NULL, 30 * self->page_size, PROT_NONE, |
| MAP_ANON | MAP_PRIVATE, -1, 0); |
| ASSERT_NE(self->carveout, MAP_FAILED); |
| /* Setup PROCMAP_QUERY interface. */ |
| @@ -34,7 +35,7 @@ FIXTURE_SETUP(merge) |
| |
| FIXTURE_TEARDOWN(merge) |
| { |
| - ASSERT_EQ(munmap(self->carveout, 12 * self->page_size), 0); |
| + ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0); |
| ASSERT_EQ(close_procmap(&self->procmap), 0); |
| /* |
| * Clear unconditionally, as some tests set this. It is no issue if this |
| @@ -576,4 +577,598 @@ TEST_F(merge, ksm_merge) |
| ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size); |
| } |
| |
| +TEST_F(merge, mremap_unfaulted_to_faulted) |
| +{ |
| + unsigned int page_size = self->page_size; |
| + char *carveout = self->carveout; |
| + struct procmap_fd *procmap = &self->procmap; |
| + char *ptr, *ptr2; |
| + |
| + /* |
| + * Map two distinct areas: |
| + * |
| + * |-----------| |-----------| |
| + * | unfaulted | | unfaulted | |
| + * |-----------| |-----------| |
| + * ptr ptr2 |
| + */ |
| + ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* Offset ptr2 further away. */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Fault in ptr: |
| + * \ |
| + * |-----------| / |-----------| |
| + * | faulted | \ | unfaulted | |
| + * |-----------| / |-----------| |
| + * ptr \ ptr2 |
| + */ |
| + ptr[0] = 'x'; |
| + |
| + /* |
| + * Now move ptr2 adjacent to ptr: |
| + * |
| + * |-----------|-----------| |
| + * | faulted | unfaulted | |
| + * |-----------|-----------| |
| + * ptr ptr2 |
| + * |
| + * It should merge: |
| + * |
| + * |----------------------| |
| + * | faulted | |
| + * |----------------------| |
| + * ptr |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size); |
| +} |
| + |
| +TEST_F(merge, mremap_unfaulted_behind_faulted) |
| +{ |
| + unsigned int page_size = self->page_size; |
| + char *carveout = self->carveout; |
| + struct procmap_fd *procmap = &self->procmap; |
| + char *ptr, *ptr2; |
| + |
| + /* |
| + * Map two distinct areas: |
| + * |
| + * |-----------| |-----------| |
| + * | unfaulted | | unfaulted | |
| + * |-----------| |-----------| |
| + * ptr ptr2 |
| + */ |
| + ptr = mmap(&carveout[6 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + ptr2 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* Offset ptr2 further away. */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Fault in ptr: |
| + * \ |
| + * |-----------| / |-----------| |
| + * | faulted | \ | unfaulted | |
| + * |-----------| / |-----------| |
| + * ptr \ ptr2 |
| + */ |
| + ptr[0] = 'x'; |
| + |
| + /* |
| + * Now move ptr2 adjacent, but behind, ptr: |
| + * |
| + * |-----------|-----------| |
| + * | unfaulted | faulted | |
| + * |-----------|-----------| |
| + * ptr2 ptr |
| + * |
| + * It should merge: |
| + * |
| + * |----------------------| |
| + * | faulted | |
| + * |----------------------| |
| + * ptr2 |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr2)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 10 * page_size); |
| +} |
| + |
| +TEST_F(merge, mremap_unfaulted_between_faulted) |
| +{ |
| + unsigned int page_size = self->page_size; |
| + char *carveout = self->carveout; |
| + struct procmap_fd *procmap = &self->procmap; |
| + char *ptr, *ptr2, *ptr3; |
| + |
| + /* |
| + * Map three distinct areas: |
| + * |
| + * |-----------| |-----------| |-----------| |
| + * | unfaulted | | unfaulted | | unfaulted | |
| + * |-----------| |-----------| |-----------| |
| + * ptr ptr2 ptr3 |
| + */ |
| + ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + ptr3 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + /* Offset ptr3 further away. */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + /* Offset ptr2 further away. */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Fault in ptr, ptr3: |
| + * \ \ |
| + * |-----------| / |-----------| / |-----------| |
| + * | faulted | \ | unfaulted | \ | faulted | |
| + * |-----------| / |-----------| / |-----------| |
| + * ptr \ ptr2 \ ptr3 |
| + */ |
| + ptr[0] = 'x'; |
| + ptr3[0] = 'x'; |
| + |
| + /* |
| + * Move ptr3 back into place, leaving a place for ptr2: |
| + * \ |
| + * |-----------| |-----------| / |-----------| |
| + * | faulted | | faulted | \ | unfaulted | |
| + * |-----------| |-----------| / |-----------| |
| + * ptr ptr3 \ ptr2 |
| + */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + /* |
| + * Finally, move ptr2 into place: |
| + * |
| + * |-----------|-----------|-----------| |
| + * | faulted | unfaulted | faulted | |
| + * |-----------|-----------|-----------| |
| + * ptr ptr2 ptr3 |
| + * |
| + * It should merge, but only ptr, ptr2: |
| + * |
| + * |-----------------------|-----------| |
| + * | faulted | unfaulted | |
| + * |-----------------------|-----------| |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr3)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr3); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr3 + 5 * page_size); |
| +} |
| + |
| +TEST_F(merge, mremap_unfaulted_between_faulted_unfaulted) |
| +{ |
| + unsigned int page_size = self->page_size; |
| + char *carveout = self->carveout; |
| + struct procmap_fd *procmap = &self->procmap; |
| + char *ptr, *ptr2, *ptr3; |
| + |
| + /* |
| + * Map three distinct areas: |
| + * |
| + * |-----------| |-----------| |-----------| |
| + * | unfaulted | | unfaulted | | unfaulted | |
| + * |-----------| |-----------| |-----------| |
| + * ptr ptr2 ptr3 |
| + */ |
| + ptr = mmap(&carveout[page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + ptr2 = mmap(&carveout[7 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + ptr3 = mmap(&carveout[14 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + /* Offset ptr3 further away. */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + |
| + /* Offset ptr2 further away. */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Fault in ptr: |
| + * \ \ |
| + * |-----------| / |-----------| / |-----------| |
| + * | faulted | \ | unfaulted | \ | unfaulted | |
| + * |-----------| / |-----------| / |-----------| |
| + * ptr \ ptr2 \ ptr3 |
| + */ |
| + ptr[0] = 'x'; |
| + |
| + /* |
| + * Move ptr3 back into place, leaving a place for ptr2: |
| + * \ |
| + * |-----------| |-----------| / |-----------| |
| + * | faulted | | unfaulted | \ | unfaulted | |
| + * |-----------| |-----------| / |-----------| |
| + * ptr ptr3 \ ptr2 |
| + */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + /* |
| + * Finally, move ptr2 into place: |
| + * |
| + * |-----------|-----------|-----------| |
| + * | faulted | unfaulted | unfaulted | |
| + * |-----------|-----------|-----------| |
| + * ptr ptr2 ptr3 |
| + * |
| + * It should merge: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); |
| +} |
| + |
| +TEST_F(merge, mremap_unfaulted_between_correctly_placed_faulted) |
| +{ |
| + unsigned int page_size = self->page_size; |
| + char *carveout = self->carveout; |
| + struct procmap_fd *procmap = &self->procmap; |
| + char *ptr, *ptr2; |
| + |
| + /* |
| + * Map one larger area: |
| + * |
| + * |-----------------------------------| |
| + * | unfaulted | |
| + * |-----------------------------------| |
| + */ |
| + ptr = mmap(&carveout[page_size], 15 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + |
| + /* |
| + * Fault in ptr: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + */ |
| + ptr[0] = 'x'; |
| + |
| + /* |
| + * Unmap middle: |
| + * |
| + * |-----------| |-----------| |
| + * | faulted | | faulted | |
| + * |-----------| |-----------| |
| + * |
| + * Now the faulted areas are compatible with each other (anon_vma the |
| + * same, vma->vm_pgoff equal to virtual page offset). |
| + */ |
| + ASSERT_EQ(munmap(&ptr[5 * page_size], 5 * page_size), 0); |
| + |
| + /* |
| + * Map a new area, ptr2: |
| + * \ |
| + * |-----------| |-----------| / |-----------| |
| + * | faulted | | faulted | \ | unfaulted | |
| + * |-----------| |-----------| / |-----------| |
| + * ptr \ ptr2 |
| + */ |
| + ptr2 = mmap(&carveout[20 * page_size], 5 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Finally, move ptr2 into place: |
| + * |
| + * |-----------|-----------|-----------| |
| + * | faulted | unfaulted | faulted | |
| + * |-----------|-----------|-----------| |
| + * ptr ptr2 ptr3 |
| + * |
| + * It should merge: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); |
| +} |
| + |
| +TEST_F(merge, mremap_correct_placed_faulted) |
| +{ |
| + unsigned int page_size = self->page_size; |
| + char *carveout = self->carveout; |
| + struct procmap_fd *procmap = &self->procmap; |
| + char *ptr, *ptr2, *ptr3; |
| + |
| + /* |
| + * Map one larger area: |
| + * |
| + * |-----------------------------------| |
| + * | unfaulted | |
| + * |-----------------------------------| |
| + */ |
| + ptr = mmap(&carveout[page_size], 15 * page_size, PROT_READ | PROT_WRITE, |
| + MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + |
| + /* |
| + * Fault in ptr: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + */ |
| + ptr[0] = 'x'; |
| + |
| + /* |
| + * Offset the final and middle 5 pages further away: |
| + * \ \ |
| + * |-----------| / |-----------| / |-----------| |
| + * | faulted | \ | faulted | \ | faulted | |
| + * |-----------| / |-----------| / |-----------| |
| + * ptr \ ptr2 \ ptr3 |
| + */ |
| + ptr3 = &ptr[10 * page_size]; |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + ptr2 = &ptr[5 * page_size]; |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Move ptr2 into its correct place: |
| + * \ |
| + * |-----------|-----------| / |-----------| |
| + * | faulted | faulted | \ | faulted | |
| + * |-----------|-----------| / |-----------| |
| + * ptr ptr2 \ ptr3 |
| + * |
| + * It should merge: |
| + * \ |
| + * |-----------------------| / |-----------| |
| + * | faulted | \ | faulted | |
| + * |-----------------------| / |-----------| |
| + * ptr \ ptr3 |
| + */ |
| + |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size); |
| + |
| + /* |
| + * Now move ptr out of place: |
| + * \ \ |
| + * |-----------| / |-----------| / |-----------| |
| + * | faulted | \ | faulted | \ | faulted | |
| + * |-----------| / |-----------| / |-----------| |
| + * ptr2 \ ptr \ ptr3 |
| + */ |
| + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + |
| + /* |
| + * Now move ptr back into place: |
| + * \ |
| + * |-----------|-----------| / |-----------| |
| + * | faulted | faulted | \ | faulted | |
| + * |-----------|-----------| / |-----------| |
| + * ptr ptr2 \ ptr3 |
| + * |
| + * It should merge: |
| + * \ |
| + * |-----------------------| / |-----------| |
| + * | faulted | \ | faulted | |
| + * |-----------------------| / |-----------| |
| + * ptr \ ptr3 |
| + */ |
| + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 10 * page_size); |
| + |
| + /* |
| + * Now move ptr out of place again: |
| + * \ \ |
| + * |-----------| / |-----------| / |-----------| |
| + * | faulted | \ | faulted | \ | faulted | |
| + * |-----------| / |-----------| / |-----------| |
| + * ptr2 \ ptr \ ptr3 |
| + */ |
| + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + |
| + /* |
| + * Now move ptr3 back into place: |
| + * \ |
| + * |-----------|-----------| / |-----------| |
| + * | faulted | faulted | \ | faulted | |
| + * |-----------|-----------| / |-----------| |
| + * ptr2 ptr3 \ ptr |
| + * |
| + * It should merge: |
| + * \ |
| + * |-----------------------| / |-----------| |
| + * | faulted | \ | faulted | |
| + * |-----------------------| / |-----------| |
| + * ptr2 \ ptr |
| + */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr2[5 * page_size]); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr2)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr2); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 10 * page_size); |
| + |
| + /* |
| + * Now move ptr back into place: |
| + * |
| + * |-----------|-----------------------| |
| + * | faulted | faulted | |
| + * |-----------|-----------------------| |
| + * ptr ptr2 |
| + * |
| + * It should merge: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + * ptr |
| + */ |
| + ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &carveout[page_size]); |
| + ASSERT_NE(ptr, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); |
| + |
| + /* |
| + * Now move ptr2 out of the way: |
| + * \ |
| + * |-----------| |-----------| / |-----------| |
| + * | faulted | | faulted | \ | faulted | |
| + * |-----------| |-----------| / |-----------| |
| + * ptr ptr3 \ ptr2 |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + /* |
| + * Now move it back: |
| + * |
| + * |-----------|-----------|-----------| |
| + * | faulted | faulted | faulted | |
| + * |-----------|-----------|-----------| |
| + * ptr ptr2 ptr3 |
| + * |
| + * It should merge: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + * ptr |
| + */ |
| + ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[5 * page_size]); |
| + ASSERT_NE(ptr2, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); |
| + |
| + /* |
| + * Move ptr3 out of place: |
| + * \ |
| + * |-----------------------| / |-----------| |
| + * | faulted | \ | faulted | |
| + * |-----------------------| / |-----------| |
| + * ptr \ ptr3 |
| + */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 1000); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + /* |
| + * Now move it back: |
| + * |
| + * |-----------|-----------|-----------| |
| + * | faulted | faulted | faulted | |
| + * |-----------|-----------|-----------| |
| + * ptr ptr2 ptr3 |
| + * |
| + * It should merge: |
| + * |
| + * |-----------------------------------| |
| + * | faulted | |
| + * |-----------------------------------| |
| + * ptr |
| + */ |
| + ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, |
| + MREMAP_MAYMOVE | MREMAP_FIXED, &ptr[10 * page_size]); |
| + ASSERT_NE(ptr3, MAP_FAILED); |
| + |
| + ASSERT_TRUE(find_vma_procmap(procmap, ptr)); |
| + ASSERT_EQ(procmap->query.vma_start, (unsigned long)ptr); |
| + ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 15 * page_size); |
| +} |
| + |
| TEST_HARNESS_MAIN |
| --- a/tools/testing/selftests/mm/vm_util.c~tools-testing-selftests-add-mremap-unfaulted-faulted-test-cases |
| +++ a/tools/testing/selftests/mm/vm_util.c |
| @@ -524,3 +524,11 @@ int read_sysfs(const char *file_path, un |
| |
| return 0; |
| } |
| + |
| +void *sys_mremap(void *old_address, unsigned long old_size, |
| + unsigned long new_size, int flags, void *new_address) |
| +{ |
| + return (void *)syscall(__NR_mremap, (unsigned long)old_address, |
| + old_size, new_size, flags, |
| + (unsigned long)new_address); |
| +} |
| --- a/tools/testing/selftests/mm/vm_util.h~tools-testing-selftests-add-mremap-unfaulted-faulted-test-cases |
| +++ a/tools/testing/selftests/mm/vm_util.h |
| @@ -117,6 +117,9 @@ static inline void log_test_result(int r |
| ksft_test_result_report(result, "%s\n", test_name); |
| } |
| |
| +void *sys_mremap(void *old_address, unsigned long old_size, |
| + unsigned long new_size, int flags, void *new_address); |
| + |
| /* |
| * On ppc64 this will only work with radix 2M hugepage size |
| */ |
| _ |