Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/tools/testing/selftests/mm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 11 kB image not shown  

Quelle  mremap_dontunmap.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0

/*
 * Tests for mremap w/ MREMAP_DONTUNMAP.
 *
 * Copyright 2020, Brian Geffon <bgeffon@google.com>
 */

#define _GNU_SOURCE
#include <sys/mman.h>
#include <linux/mman.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "../kselftest.h"

unsigned long page_size;
char *page_buffer;

static void dump_maps(void)
{
 char cmd[32];

 snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid());
 system(cmd);
}

#define BUG_ON(condition, description)      \
 do {         \
  if (condition) {      \
   dump_maps();      \
   ksft_exit_fail_msg("[FAIL]\t%s:%d\t%s:%s\n",  \
        __func__, __LINE__, (description), \
        strerror(errno));   \
  }        \
 } while (0)

// Try a simple operation for to "test" for kernel support this prevents
// reporting tests as failed when it's run on an older kernel.
static int kernel_support_for_mremap_dontunmap()
{
 int ret = 0;
 unsigned long num_pages = 1;
 void *source_mapping = mmap(NULL, num_pages * page_size, PROT_NONE,
        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(source_mapping == MAP_FAILED, "mmap");

 // This simple remap should only fail if MREMAP_DONTUNMAP isn't
 // supported.
 void *dest_mapping =
     mremap(source_mapping, num_pages * page_size, num_pages * page_size,
     MREMAP_DONTUNMAP | MREMAP_MAYMOVE, 0);
 if (dest_mapping == MAP_FAILED) {
  ret = errno;
 } else {
  BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
         "unable to unmap destination mapping");
 }

 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
        "unable to unmap source mapping");
 return ret;
}

// This helper will just validate that an entire mapping contains the expected
// byte.
static int check_region_contains_byte(void *addr, unsigned long size, char byte)
{
 BUG_ON(size & (page_size - 1),
        "check_region_contains_byte expects page multiples");
 BUG_ON((unsigned long)addr & (page_size - 1),
        "check_region_contains_byte expects page alignment");

 memset(page_buffer, byte, page_size);

 unsigned long num_pages = size / page_size;
 unsigned long i;

 // Compare each page checking that it contains our expected byte.
 for (i = 0; i < num_pages; ++i) {
  int ret =
      memcmp(addr + (i * page_size), page_buffer, page_size);
  if (ret) {
   return ret;
  }
 }

 return 0;
}

// this test validates that MREMAP_DONTUNMAP moves the pagetables while leaving
// the source mapping mapped.
static void mremap_dontunmap_simple()
{
 unsigned long num_pages = 5;

 void *source_mapping =
     mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(source_mapping == MAP_FAILED, "mmap");

 memset(source_mapping, 'a', num_pages * page_size);

 // Try to just move the whole mapping anywhere (not fixed).
 void *dest_mapping =
     mremap(source_mapping, num_pages * page_size, num_pages * page_size,
     MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
 BUG_ON(dest_mapping == MAP_FAILED, "mremap");

 // Validate that the pages have been moved, we know they were moved if
 // the dest_mapping contains a's.
 BUG_ON(check_region_contains_byte
        (dest_mapping, num_pages * page_size, 'a') != 0,
        "pages did not migrate");
 BUG_ON(check_region_contains_byte
        (source_mapping, num_pages * page_size, 0) != 0,
        "source should have no ptes");

 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
        "unable to unmap destination mapping");
 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
        "unable to unmap source mapping");
 ksft_test_result_pass("%s\n", __func__);
}

// This test validates that MREMAP_DONTUNMAP on a shared mapping works as expected.
static void mremap_dontunmap_simple_shmem()
{
 unsigned long num_pages = 5;

 int mem_fd = memfd_create("memfd", MFD_CLOEXEC);
 BUG_ON(mem_fd < 0, "memfd_create");

 BUG_ON(ftruncate(mem_fd, num_pages * page_size) < 0,
   "ftruncate");

 void *source_mapping =
     mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
   MAP_FILE | MAP_SHARED, mem_fd, 0);
 BUG_ON(source_mapping == MAP_FAILED, "mmap");

 BUG_ON(close(mem_fd) < 0, "close");

 memset(source_mapping, 'a', num_pages * page_size);

 // Try to just move the whole mapping anywhere (not fixed).
 void *dest_mapping =
     mremap(source_mapping, num_pages * page_size, num_pages * page_size,
     MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
 if (dest_mapping == MAP_FAILED && errno == EINVAL) {
  // Old kernel which doesn't support MREMAP_DONTUNMAP on shmem.
  BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
   "unable to unmap source mapping");
  return;
 }

 BUG_ON(dest_mapping == MAP_FAILED, "mremap");

 // Validate that the pages have been moved, we know they were moved if
 // the dest_mapping contains a's.
 BUG_ON(check_region_contains_byte
        (dest_mapping, num_pages * page_size, 'a') != 0,
        "pages did not migrate");

 // Because the region is backed by shmem, we will actually see the same
 // memory at the source location still.
 BUG_ON(check_region_contains_byte
        (source_mapping, num_pages * page_size, 'a') != 0,
        "source should have no ptes");

 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
        "unable to unmap destination mapping");
 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
        "unable to unmap source mapping");
 ksft_test_result_pass("%s\n", __func__);
}

// This test validates MREMAP_DONTUNMAP will move page tables to a specific
// destination using MREMAP_FIXED, also while validating that the source
// remains intact.
static void mremap_dontunmap_simple_fixed()
{
 unsigned long num_pages = 5;

 // Since we want to guarantee that we can remap to a point, we will
 // create a mapping up front.
 void *dest_mapping =
     mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(dest_mapping == MAP_FAILED, "mmap");
 memset(dest_mapping, 'X', num_pages * page_size);

 void *source_mapping =
     mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(source_mapping == MAP_FAILED, "mmap");
 memset(source_mapping, 'a', num_pages * page_size);

 void *remapped_mapping =
     mremap(source_mapping, num_pages * page_size, num_pages * page_size,
     MREMAP_FIXED | MREMAP_DONTUNMAP | MREMAP_MAYMOVE,
     dest_mapping);
 BUG_ON(remapped_mapping == MAP_FAILED, "mremap");
 BUG_ON(remapped_mapping != dest_mapping,
        "mremap should have placed the remapped mapping at dest_mapping");

 // The dest mapping will have been unmap by mremap so we expect the Xs
 // to be gone and replaced with a's.
 BUG_ON(check_region_contains_byte
        (dest_mapping, num_pages * page_size, 'a') != 0,
        "pages did not migrate");

 // And the source mapping will have had its ptes dropped.
 BUG_ON(check_region_contains_byte
        (source_mapping, num_pages * page_size, 0) != 0,
        "source should have no ptes");

 BUG_ON(munmap(dest_mapping, num_pages * page_size) == -1,
        "unable to unmap destination mapping");
 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
        "unable to unmap source mapping");
 ksft_test_result_pass("%s\n", __func__);
}

// This test validates that we can MREMAP_DONTUNMAP for a portion of an
// existing mapping.
static void mremap_dontunmap_partial_mapping()
{
 /*
 *  source mapping:
 *  --------------
 *  | aaaaaaaaaa |
 *  --------------
 *  to become:
 *  --------------
 *  | aaaaa00000 |
 *  --------------
 *  With the destination mapping containing 5 pages of As.
 *  ---------
 *  | aaaaa |
 *  ---------
 */

 unsigned long num_pages = 10;
 void *source_mapping =
     mmap(NULL, num_pages * page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(source_mapping == MAP_FAILED, "mmap");
 memset(source_mapping, 'a', num_pages * page_size);

 // We will grab the last 5 pages of the source and move them.
 void *dest_mapping =
     mremap(source_mapping + (5 * page_size), 5 * page_size,
     5 * page_size,
     MREMAP_DONTUNMAP | MREMAP_MAYMOVE, NULL);
 BUG_ON(dest_mapping == MAP_FAILED, "mremap");

 // We expect the first 5 pages of the source to contain a's and the
 // final 5 pages to contain zeros.
 BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 'a') !=
        0, "first 5 pages of source should have original pages");
 BUG_ON(check_region_contains_byte
        (source_mapping + (5 * page_size), 5 * page_size, 0) != 0,
        "final 5 pages of source should have no ptes");

 // Finally we expect the destination to have 5 pages worth of a's.
 BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') !=
        0, "dest mapping should contain ptes from the source");

 BUG_ON(munmap(dest_mapping, 5 * page_size) == -1,
        "unable to unmap destination mapping");
 BUG_ON(munmap(source_mapping, num_pages * page_size) == -1,
        "unable to unmap source mapping");
 ksft_test_result_pass("%s\n", __func__);
}

// This test validates that we can remap over only a portion of a mapping.
static void mremap_dontunmap_partial_mapping_overwrite(void)
{
 /*
 *  source mapping:
 *  ---------
 *  |aaaaa|
 *  ---------
 *  dest mapping initially:
 *  -----------
 *  |XXXXXXXXXX|
 *  ------------
 *  Source to become:
 *  ---------
 *  |00000|
 *  ---------
 *  With the destination mapping containing 5 pages of As.
 *  ------------
 *  |aaaaaXXXXX|
 *  ------------
 */

 void *source_mapping =
     mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(source_mapping == MAP_FAILED, "mmap");
 memset(source_mapping, 'a', 5 * page_size);

 void *dest_mapping =
     mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(dest_mapping == MAP_FAILED, "mmap");
 memset(dest_mapping, 'X', 10 * page_size);

 // We will grab the last 5 pages of the source and move them.
 void *remapped_mapping =
     mremap(source_mapping, 5 * page_size,
     5 * page_size,
     MREMAP_DONTUNMAP | MREMAP_MAYMOVE | MREMAP_FIXED, dest_mapping);
 BUG_ON(dest_mapping == MAP_FAILED, "mremap");
 BUG_ON(dest_mapping != remapped_mapping, "expected to remap to dest_mapping");

 BUG_ON(check_region_contains_byte(source_mapping, 5 * page_size, 0) !=
        0, "first 5 pages of source should have no ptes");

 // Finally we expect the destination to have 5 pages worth of a's.
 BUG_ON(check_region_contains_byte(dest_mapping, 5 * page_size, 'a') != 0,
   "dest mapping should contain ptes from the source");

 // Finally the last 5 pages shouldn't have been touched.
 BUG_ON(check_region_contains_byte(dest_mapping + (5 * page_size),
    5 * page_size, 'X') != 0,
   "dest mapping should have retained the last 5 pages");

 BUG_ON(munmap(dest_mapping, 10 * page_size) == -1,
        "unable to unmap destination mapping");
 BUG_ON(munmap(source_mapping, 5 * page_size) == -1,
        "unable to unmap source mapping");
 ksft_test_result_pass("%s\n", __func__);
}

int main(void)
{
 ksft_print_header();

 page_size = sysconf(_SC_PAGE_SIZE);

 // test for kernel support for MREMAP_DONTUNMAP skipping the test if
 // not.
 if (kernel_support_for_mremap_dontunmap() != 0) {
  ksft_print_msg("No kernel support for MREMAP_DONTUNMAP\n");
  ksft_finished();
 }

 ksft_set_plan(5);

 // Keep a page sized buffer around for when we need it.
 page_buffer =
     mmap(NULL, page_size, PROT_READ | PROT_WRITE,
   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 BUG_ON(page_buffer == MAP_FAILED, "unable to mmap a page.");

 mremap_dontunmap_simple();
 mremap_dontunmap_simple_shmem();
 mremap_dontunmap_simple_fixed();
 mremap_dontunmap_partial_mapping();
 mremap_dontunmap_partial_mapping_overwrite();

 BUG_ON(munmap(page_buffer, page_size) == -1,
        "unable to unmap page buffer");

 ksft_finished();
}

Messung V0.5
C=94 H=98 G=95

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.