123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests
- *
- * Copyright 2021, Red Hat, Inc.
- *
- * Author(s): David Hildenbrand <[email protected]>
- */
- #define _GNU_SOURCE
- #include <stdlib.h>
- #include <string.h>
- #include <stdbool.h>
- #include <stdint.h>
- #include <unistd.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <linux/mman.h>
- #include <sys/mman.h>
- #include "../kselftest.h"
- #include "vm_util.h"
- /*
- * For now, we're using 2 MiB of private anonymous memory for all tests.
- */
- #define SIZE (2 * 1024 * 1024)
- static size_t pagesize;
- static bool pagemap_is_populated(int fd, char *start)
- {
- uint64_t entry = pagemap_get_entry(fd, start);
- /* Present or swapped. */
- return entry & 0xc000000000000000ull;
- }
- static void sense_support(void)
- {
- char *addr;
- int ret;
- addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (!addr)
- ksft_exit_fail_msg("mmap failed\n");
- ret = madvise(addr, pagesize, MADV_POPULATE_READ);
- if (ret)
- ksft_exit_skip("MADV_POPULATE_READ is not available\n");
- ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);
- if (ret)
- ksft_exit_skip("MADV_POPULATE_WRITE is not available\n");
- munmap(addr, pagesize);
- }
- static void test_prot_read(void)
- {
- char *addr;
- int ret;
- ksft_print_msg("[RUN] %s\n", __func__);
- addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (addr == MAP_FAILED)
- ksft_exit_fail_msg("mmap failed\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_READ);
- ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
- ksft_test_result(ret == -1 && errno == EINVAL,
- "MADV_POPULATE_WRITE with PROT_READ\n");
- munmap(addr, SIZE);
- }
- static void test_prot_write(void)
- {
- char *addr;
- int ret;
- ksft_print_msg("[RUN] %s\n", __func__);
- addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (addr == MAP_FAILED)
- ksft_exit_fail_msg("mmap failed\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_READ);
- ksft_test_result(ret == -1 && errno == EINVAL,
- "MADV_POPULATE_READ with PROT_WRITE\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
- ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n");
- munmap(addr, SIZE);
- }
- static void test_holes(void)
- {
- char *addr;
- int ret;
- ksft_print_msg("[RUN] %s\n", __func__);
- addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (addr == MAP_FAILED)
- ksft_exit_fail_msg("mmap failed\n");
- ret = munmap(addr + pagesize, pagesize);
- if (ret)
- ksft_exit_fail_msg("munmap failed\n");
- /* Hole in the middle */
- ret = madvise(addr, SIZE, MADV_POPULATE_READ);
- ksft_test_result(ret == -1 && errno == ENOMEM,
- "MADV_POPULATE_READ with holes in the middle\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
- ksft_test_result(ret == -1 && errno == ENOMEM,
- "MADV_POPULATE_WRITE with holes in the middle\n");
- /* Hole at end */
- ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ);
- ksft_test_result(ret == -1 && errno == ENOMEM,
- "MADV_POPULATE_READ with holes at the end\n");
- ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE);
- ksft_test_result(ret == -1 && errno == ENOMEM,
- "MADV_POPULATE_WRITE with holes at the end\n");
- /* Hole at beginning */
- ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ);
- ksft_test_result(ret == -1 && errno == ENOMEM,
- "MADV_POPULATE_READ with holes at the beginning\n");
- ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE);
- ksft_test_result(ret == -1 && errno == ENOMEM,
- "MADV_POPULATE_WRITE with holes at the beginning\n");
- munmap(addr, SIZE);
- }
- static bool range_is_populated(char *start, ssize_t size)
- {
- int fd = open("/proc/self/pagemap", O_RDONLY);
- bool ret = true;
- if (fd < 0)
- ksft_exit_fail_msg("opening pagemap failed\n");
- for (; size > 0 && ret; size -= pagesize, start += pagesize)
- if (!pagemap_is_populated(fd, start))
- ret = false;
- close(fd);
- return ret;
- }
- static bool range_is_not_populated(char *start, ssize_t size)
- {
- int fd = open("/proc/self/pagemap", O_RDONLY);
- bool ret = true;
- if (fd < 0)
- ksft_exit_fail_msg("opening pagemap failed\n");
- for (; size > 0 && ret; size -= pagesize, start += pagesize)
- if (pagemap_is_populated(fd, start))
- ret = false;
- close(fd);
- return ret;
- }
- static void test_populate_read(void)
- {
- char *addr;
- int ret;
- ksft_print_msg("[RUN] %s\n", __func__);
- addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (addr == MAP_FAILED)
- ksft_exit_fail_msg("mmap failed\n");
- ksft_test_result(range_is_not_populated(addr, SIZE),
- "range initially not populated\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_READ);
- ksft_test_result(!ret, "MADV_POPULATE_READ\n");
- ksft_test_result(range_is_populated(addr, SIZE),
- "range is populated\n");
- munmap(addr, SIZE);
- }
- static void test_populate_write(void)
- {
- char *addr;
- int ret;
- ksft_print_msg("[RUN] %s\n", __func__);
- addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (addr == MAP_FAILED)
- ksft_exit_fail_msg("mmap failed\n");
- ksft_test_result(range_is_not_populated(addr, SIZE),
- "range initially not populated\n");
- ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
- ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
- ksft_test_result(range_is_populated(addr, SIZE),
- "range is populated\n");
- munmap(addr, SIZE);
- }
- static bool range_is_softdirty(char *start, ssize_t size)
- {
- int fd = open("/proc/self/pagemap", O_RDONLY);
- bool ret = true;
- if (fd < 0)
- ksft_exit_fail_msg("opening pagemap failed\n");
- for (; size > 0 && ret; size -= pagesize, start += pagesize)
- if (!pagemap_is_softdirty(fd, start))
- ret = false;
- close(fd);
- return ret;
- }
- static bool range_is_not_softdirty(char *start, ssize_t size)
- {
- int fd = open("/proc/self/pagemap", O_RDONLY);
- bool ret = true;
- if (fd < 0)
- ksft_exit_fail_msg("opening pagemap failed\n");
- for (; size > 0 && ret; size -= pagesize, start += pagesize)
- if (pagemap_is_softdirty(fd, start))
- ret = false;
- close(fd);
- return ret;
- }
- static void test_softdirty(void)
- {
- char *addr;
- int ret;
- ksft_print_msg("[RUN] %s\n", __func__);
- addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
- if (addr == MAP_FAILED)
- ksft_exit_fail_msg("mmap failed\n");
- /* Clear any softdirty bits. */
- clear_softdirty();
- ksft_test_result(range_is_not_softdirty(addr, SIZE),
- "range is not softdirty\n");
- /* Populating READ should set softdirty. */
- ret = madvise(addr, SIZE, MADV_POPULATE_READ);
- ksft_test_result(!ret, "MADV_POPULATE_READ\n");
- ksft_test_result(range_is_not_softdirty(addr, SIZE),
- "range is not softdirty\n");
- /* Populating WRITE should set softdirty. */
- ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
- ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
- ksft_test_result(range_is_softdirty(addr, SIZE),
- "range is softdirty\n");
- munmap(addr, SIZE);
- }
- int main(int argc, char **argv)
- {
- int err;
- pagesize = getpagesize();
- ksft_print_header();
- ksft_set_plan(21);
- sense_support();
- test_prot_read();
- test_prot_write();
- test_holes();
- test_populate_read();
- test_populate_write();
- test_softdirty();
- err = ksft_get_fail_cnt();
- if (err)
- ksft_exit_fail_msg("%d out of %d tests failed\n",
- err, ksft_test_num());
- return ksft_exit_pass();
- }
|