Re: [PATCH] selftest/vm: add mremap expand merge offset test

From: Lorenzo Stoakes
Date: Mon Jan 02 2023 - 08:58:31 EST


On Mon, Jan 02, 2023 at 02:32:55PM +0100, David Hildenbrand wrote:
> On 16.12.22 22:44, Lorenzo Stoakes wrote:
> > Add a test to assert that we can mremap() and expand a mapping starting
> > from an offset within an existing mapping. We unmap the last page in a 3
> > page mapping to ensure that the remap should always succeed, before
> > remapping from the 2nd page.
> >
> > This is additionally a regression test for the issue solved in "mm, mremap:
> > fix mremap() expanding vma with addr inside vma" and confirmed to fail
> > prior to the change and pass after it.
> >
> > Finally, this patch updates the existing mremap expand merge test to check
> > error conditions and reduce code duplication between the two tests.
> >
> > Signed-off-by: Lorenzo Stoakes <lstoakes@xxxxxxxxx>
> > ---
> > tools/testing/selftests/vm/mremap_test.c | 111 +++++++++++++++++++----
> > 1 file changed, 93 insertions(+), 18 deletions(-)
> >
> > diff --git a/tools/testing/selftests/vm/mremap_test.c b/tools/testing/selftests/vm/mremap_test.c
> > index 9496346973d4..28a17d4e8afd 100644
> > --- a/tools/testing/selftests/vm/mremap_test.c
> > +++ b/tools/testing/selftests/vm/mremap_test.c
> > @@ -119,30 +119,19 @@ static unsigned long long get_mmap_min_addr(void)
> > }
> >
> > /*
> > - * This test validates that merge is called when expanding a mapping.
> > - * Mapping containing three pages is created, middle page is unmapped
> > - * and then the mapping containing the first page is expanded so that
> > - * it fills the created hole. The two parts should merge creating
> > - * single mapping with three pages.
> > + * Using /proc/self/maps, assert that the specified address range is contained
> > + * within a single mapping.
> > */
> > -static void mremap_expand_merge(unsigned long page_size)
> > +static bool is_range_mapped(void *start, void *end)
> > {
> > - char *test_name = "mremap expand merge";
> > FILE *fp;
> > char *line = NULL;
> > size_t len = 0;
> > bool success = false;
> > - char *start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
> > - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> > -
> > - munmap(start + page_size, page_size);
> > - mremap(start, page_size, 2 * page_size, 0);
> >
> > fp = fopen("/proc/self/maps", "r");
> > - if (fp == NULL) {
> > - ksft_test_result_fail("%s\n", test_name);
> > - return;
> > - }
> > + if (fp == NULL)
> > + return false;
>
> This is unexpected. It would be valuable to ksft_print_msg("[INFO] .."
> something, indicating that we don't know because we cannot access that info.
>
> ksft_print_msg("[INFO] Opening /proc/self/maps failed"
>
>
> But I'd even suggest opening "/proc/self/maps" once in main() and failing
> directly there. Then we don't have to worry about it here.
>
> >
> > while (getline(&line, &len, fp) != -1) {
> > char *first = strtok(line, "- ");
> > @@ -150,16 +139,101 @@ static void mremap_expand_merge(unsigned long page_size)
> > char *second = strtok(NULL, "- ");
> > void *second_val = (void *) strtol(second, NULL, 16);
> >
> > - if (first_val == start && second_val == start + 3 * page_size) {
> > + if (first_val <= start && second_val >= end) {
> > success = true;
> > break;
> > }
> > }
> > +
> > + fclose(fp);
> > + return success;
> > +}
> > +
> > +/*
> > + * This test validates that merge is called when expanding a mapping.
> > + * Mapping containing three pages is created, middle page is unmapped
> > + * and then the mapping containing the first page is expanded so that
> > + * it fills the created hole. The two parts should merge creating
> > + * single mapping with three pages.
> > + */
> > +static void mremap_expand_merge(unsigned long page_size)
> > +{
> > + char *test_name = "mremap expand merge";
> > + bool success = false;
> > + int errsv = 0;
> > + char *remap;
> > + char *start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
> > + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
>
> I'd suggest
>
>
> char *remap, *start;
>
> start = mmap()
> if (start == MAP_FAILED) { ...
>
> to make this easier to read.
>
> > +
> > + if (start == MAP_FAILED) {
> > + errsv = errno;
> > + goto error;
> > + }
> > +
> > + munmap(start + page_size, page_size);
> > + remap = mremap(start, page_size, 2 * page_size, 0);
> > + if (remap == MAP_FAILED) {
> > + errsv = errno;
> > + munmap(start, page_size);
> > + munmap(start + 2 * page_size, page_size);
> > + goto error;
> > + }
> > +
> > + success = is_range_mapped(start, start + 3 * page_size);
> > +
> > + munmap(start, 3 * page_size);
> > + goto out;
> > +
> > +error:
> > + ksft_print_msg("Unexpected mapping/remapping error: %s\n",
> > + strerror(errsv));
>
> Please avoid the "error" label and just print proper errors directly at the
> two callsites. Then, remove the "goto out".
>
> > +out:
> > + if (success)
> > + ksft_test_result_pass("%s\n", test_name);
> > + else
> > + ksft_test_result_fail("%s\n", test_name);
> > +}
> > +
> > +/*
> > + * Similar to mremap_expand_merge() except instead of removing the middle page,
> > + * we remove the last then attempt to remap offset from the second page. This
> > + * should result in the mapping being restored to its former state.
> > + */
> > +static void mremap_expand_merge_offset(unsigned long page_size)
> > +{
> > +
> > + char *test_name = "mremap expand merge offset";
> > + bool success = false;
> > + int errsv = 0;
> > + char *remap;
> > + char *start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
> > + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
>
> Dito.
>
> > +
> > + if (start == MAP_FAILED) {
> > + errsv = errno;
> > + goto error;
> > + }
> > +
> > + /* Unmap final page to ensure we have space to expand. */
> > + munmap(start + 2 * page_size, page_size);
> > + remap = mremap(start + page_size, page_size, 2 * page_size, 0);
> > + if (remap == MAP_FAILED) {
> > + errsv = errno;
> > + munmap(start, 2 * page_size);
> > + goto error;
> > + }
> > +
> > + success = is_range_mapped(start, start + 3 * page_size);
> > + goto out;
> > +
> > +error:
> > + ksft_print_msg("Unexpected mapping/remapping error: %s\n",
> > + strerror(errsv));
>
> Dito.
>
> > +out:
>
>
> --
> Thanks,
>
> David / dhildenb
>

Ack on all, will spin a v2. Thanks for review!