162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2021, Red Hat, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): David Hildenbrand <david@redhat.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#define _GNU_SOURCE 1062306a36Sopenharmony_ci#include <stdlib.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <stdbool.h> 1362306a36Sopenharmony_ci#include <stdint.h> 1462306a36Sopenharmony_ci#include <unistd.h> 1562306a36Sopenharmony_ci#include <errno.h> 1662306a36Sopenharmony_ci#include <fcntl.h> 1762306a36Sopenharmony_ci#include <linux/mman.h> 1862306a36Sopenharmony_ci#include <sys/mman.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "../kselftest.h" 2162306a36Sopenharmony_ci#include "vm_util.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * For now, we're using 2 MiB of private anonymous memory for all tests. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci#define SIZE (2 * 1024 * 1024) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic size_t pagesize; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void sense_support(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci char *addr; 3362306a36Sopenharmony_ci int ret; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci addr = mmap(0, pagesize, PROT_READ | PROT_WRITE, 3662306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 3762306a36Sopenharmony_ci if (!addr) 3862306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci ret = madvise(addr, pagesize, MADV_POPULATE_READ); 4162306a36Sopenharmony_ci if (ret) 4262306a36Sopenharmony_ci ksft_exit_skip("MADV_POPULATE_READ is not available\n"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci ret = madvise(addr, pagesize, MADV_POPULATE_WRITE); 4562306a36Sopenharmony_ci if (ret) 4662306a36Sopenharmony_ci ksft_exit_skip("MADV_POPULATE_WRITE is not available\n"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci munmap(addr, pagesize); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void test_prot_read(void) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci char *addr; 5462306a36Sopenharmony_ci int ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ksft_print_msg("[RUN] %s\n", __func__); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 5962306a36Sopenharmony_ci if (addr == MAP_FAILED) 6062306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_READ); 6362306a36Sopenharmony_ci ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 6662306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == EINVAL, 6762306a36Sopenharmony_ci "MADV_POPULATE_WRITE with PROT_READ\n"); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci munmap(addr, SIZE); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void test_prot_write(void) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci char *addr; 7562306a36Sopenharmony_ci int ret; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci ksft_print_msg("[RUN] %s\n", __func__); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 8062306a36Sopenharmony_ci if (addr == MAP_FAILED) 8162306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_READ); 8462306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == EINVAL, 8562306a36Sopenharmony_ci "MADV_POPULATE_READ with PROT_WRITE\n"); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 8862306a36Sopenharmony_ci ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n"); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci munmap(addr, SIZE); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void test_holes(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci char *addr; 9662306a36Sopenharmony_ci int ret; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ksft_print_msg("[RUN] %s\n", __func__); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 10162306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 10262306a36Sopenharmony_ci if (addr == MAP_FAILED) 10362306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 10462306a36Sopenharmony_ci ret = munmap(addr + pagesize, pagesize); 10562306a36Sopenharmony_ci if (ret) 10662306a36Sopenharmony_ci ksft_exit_fail_msg("munmap failed\n"); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Hole in the middle */ 10962306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_READ); 11062306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == ENOMEM, 11162306a36Sopenharmony_ci "MADV_POPULATE_READ with holes in the middle\n"); 11262306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 11362306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == ENOMEM, 11462306a36Sopenharmony_ci "MADV_POPULATE_WRITE with holes in the middle\n"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Hole at end */ 11762306a36Sopenharmony_ci ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ); 11862306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == ENOMEM, 11962306a36Sopenharmony_ci "MADV_POPULATE_READ with holes at the end\n"); 12062306a36Sopenharmony_ci ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE); 12162306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == ENOMEM, 12262306a36Sopenharmony_ci "MADV_POPULATE_WRITE with holes at the end\n"); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Hole at beginning */ 12562306a36Sopenharmony_ci ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ); 12662306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == ENOMEM, 12762306a36Sopenharmony_ci "MADV_POPULATE_READ with holes at the beginning\n"); 12862306a36Sopenharmony_ci ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE); 12962306a36Sopenharmony_ci ksft_test_result(ret == -1 && errno == ENOMEM, 13062306a36Sopenharmony_ci "MADV_POPULATE_WRITE with holes at the beginning\n"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci munmap(addr, SIZE); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic bool range_is_populated(char *start, ssize_t size) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int fd = open("/proc/self/pagemap", O_RDONLY); 13862306a36Sopenharmony_ci bool ret = true; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (fd < 0) 14162306a36Sopenharmony_ci ksft_exit_fail_msg("opening pagemap failed\n"); 14262306a36Sopenharmony_ci for (; size > 0 && ret; size -= pagesize, start += pagesize) 14362306a36Sopenharmony_ci if (!pagemap_is_populated(fd, start)) 14462306a36Sopenharmony_ci ret = false; 14562306a36Sopenharmony_ci close(fd); 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic bool range_is_not_populated(char *start, ssize_t size) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int fd = open("/proc/self/pagemap", O_RDONLY); 15262306a36Sopenharmony_ci bool ret = true; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (fd < 0) 15562306a36Sopenharmony_ci ksft_exit_fail_msg("opening pagemap failed\n"); 15662306a36Sopenharmony_ci for (; size > 0 && ret; size -= pagesize, start += pagesize) 15762306a36Sopenharmony_ci if (pagemap_is_populated(fd, start)) 15862306a36Sopenharmony_ci ret = false; 15962306a36Sopenharmony_ci close(fd); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void test_populate_read(void) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci char *addr; 16662306a36Sopenharmony_ci int ret; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ksft_print_msg("[RUN] %s\n", __func__); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 17162306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 17262306a36Sopenharmony_ci if (addr == MAP_FAILED) 17362306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 17462306a36Sopenharmony_ci ksft_test_result(range_is_not_populated(addr, SIZE), 17562306a36Sopenharmony_ci "range initially not populated\n"); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_READ); 17862306a36Sopenharmony_ci ksft_test_result(!ret, "MADV_POPULATE_READ\n"); 17962306a36Sopenharmony_ci ksft_test_result(range_is_populated(addr, SIZE), 18062306a36Sopenharmony_ci "range is populated\n"); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci munmap(addr, SIZE); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void test_populate_write(void) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci char *addr; 18862306a36Sopenharmony_ci int ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ksft_print_msg("[RUN] %s\n", __func__); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 19362306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 19462306a36Sopenharmony_ci if (addr == MAP_FAILED) 19562306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 19662306a36Sopenharmony_ci ksft_test_result(range_is_not_populated(addr, SIZE), 19762306a36Sopenharmony_ci "range initially not populated\n"); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 20062306a36Sopenharmony_ci ksft_test_result(!ret, "MADV_POPULATE_WRITE\n"); 20162306a36Sopenharmony_ci ksft_test_result(range_is_populated(addr, SIZE), 20262306a36Sopenharmony_ci "range is populated\n"); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci munmap(addr, SIZE); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic bool range_is_softdirty(char *start, ssize_t size) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int fd = open("/proc/self/pagemap", O_RDONLY); 21062306a36Sopenharmony_ci bool ret = true; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (fd < 0) 21362306a36Sopenharmony_ci ksft_exit_fail_msg("opening pagemap failed\n"); 21462306a36Sopenharmony_ci for (; size > 0 && ret; size -= pagesize, start += pagesize) 21562306a36Sopenharmony_ci if (!pagemap_is_softdirty(fd, start)) 21662306a36Sopenharmony_ci ret = false; 21762306a36Sopenharmony_ci close(fd); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic bool range_is_not_softdirty(char *start, ssize_t size) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int fd = open("/proc/self/pagemap", O_RDONLY); 22462306a36Sopenharmony_ci bool ret = true; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (fd < 0) 22762306a36Sopenharmony_ci ksft_exit_fail_msg("opening pagemap failed\n"); 22862306a36Sopenharmony_ci for (; size > 0 && ret; size -= pagesize, start += pagesize) 22962306a36Sopenharmony_ci if (pagemap_is_softdirty(fd, start)) 23062306a36Sopenharmony_ci ret = false; 23162306a36Sopenharmony_ci close(fd); 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void test_softdirty(void) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci char *addr; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ksft_print_msg("[RUN] %s\n", __func__); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 24362306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 24462306a36Sopenharmony_ci if (addr == MAP_FAILED) 24562306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Clear any softdirty bits. */ 24862306a36Sopenharmony_ci clear_softdirty(); 24962306a36Sopenharmony_ci ksft_test_result(range_is_not_softdirty(addr, SIZE), 25062306a36Sopenharmony_ci "range is not softdirty\n"); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Populating READ should set softdirty. */ 25362306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_READ); 25462306a36Sopenharmony_ci ksft_test_result(!ret, "MADV_POPULATE_READ\n"); 25562306a36Sopenharmony_ci ksft_test_result(range_is_not_softdirty(addr, SIZE), 25662306a36Sopenharmony_ci "range is not softdirty\n"); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Populating WRITE should set softdirty. */ 25962306a36Sopenharmony_ci ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 26062306a36Sopenharmony_ci ksft_test_result(!ret, "MADV_POPULATE_WRITE\n"); 26162306a36Sopenharmony_ci ksft_test_result(range_is_softdirty(addr, SIZE), 26262306a36Sopenharmony_ci "range is softdirty\n"); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci munmap(addr, SIZE); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int system_has_softdirty(void) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * There is no way to check if the kernel supports soft-dirty, other 27162306a36Sopenharmony_ci * than by writing to a page and seeing if the bit was set. But the 27262306a36Sopenharmony_ci * tests are intended to check that the bit gets set when it should, so 27362306a36Sopenharmony_ci * doing that check would turn a potentially legitimate fail into a 27462306a36Sopenharmony_ci * skip. Fortunately, we know for sure that arm64 does not support 27562306a36Sopenharmony_ci * soft-dirty. So for now, let's just use the arch as a corse guide. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci#if defined(__aarch64__) 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci#else 28062306a36Sopenharmony_ci return 1; 28162306a36Sopenharmony_ci#endif 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciint main(int argc, char **argv) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int nr_tests = 16; 28762306a36Sopenharmony_ci int err; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci pagesize = getpagesize(); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (system_has_softdirty()) 29262306a36Sopenharmony_ci nr_tests += 5; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ksft_print_header(); 29562306a36Sopenharmony_ci ksft_set_plan(nr_tests); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci sense_support(); 29862306a36Sopenharmony_ci test_prot_read(); 29962306a36Sopenharmony_ci test_prot_write(); 30062306a36Sopenharmony_ci test_holes(); 30162306a36Sopenharmony_ci test_populate_read(); 30262306a36Sopenharmony_ci test_populate_write(); 30362306a36Sopenharmony_ci if (system_has_softdirty()) 30462306a36Sopenharmony_ci test_softdirty(); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci err = ksft_get_fail_cnt(); 30762306a36Sopenharmony_ci if (err) 30862306a36Sopenharmony_ci ksft_exit_fail_msg("%d out of %d tests failed\n", 30962306a36Sopenharmony_ci err, ksft_test_num()); 31062306a36Sopenharmony_ci return ksft_exit_pass(); 31162306a36Sopenharmony_ci} 312