162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <stdio.h> 362306a36Sopenharmony_ci#include <string.h> 462306a36Sopenharmony_ci#include <stdbool.h> 562306a36Sopenharmony_ci#include <fcntl.h> 662306a36Sopenharmony_ci#include <stdint.h> 762306a36Sopenharmony_ci#include <malloc.h> 862306a36Sopenharmony_ci#include <sys/mman.h> 962306a36Sopenharmony_ci#include "../kselftest.h" 1062306a36Sopenharmony_ci#include "vm_util.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define PAGEMAP_FILE_PATH "/proc/self/pagemap" 1362306a36Sopenharmony_ci#define TEST_ITERATIONS 10000 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic void test_simple(int pagemap_fd, int pagesize) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci int i; 1862306a36Sopenharmony_ci char *map; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci map = aligned_alloc(pagesize, pagesize); 2162306a36Sopenharmony_ci if (!map) 2262306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed\n"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci clear_softdirty(); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci for (i = 0 ; i < TEST_ITERATIONS; i++) { 2762306a36Sopenharmony_ci if (pagemap_is_softdirty(pagemap_fd, map) == 1) { 2862306a36Sopenharmony_ci ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i); 2962306a36Sopenharmony_ci break; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci clear_softdirty(); 3362306a36Sopenharmony_ci // Write something to the page to get the dirty bit enabled on the page 3462306a36Sopenharmony_ci map[0]++; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (pagemap_is_softdirty(pagemap_fd, map) == 0) { 3762306a36Sopenharmony_ci ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i); 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci clear_softdirty(); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci free(map); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void test_vma_reuse(int pagemap_fd, int pagesize) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci char *map, *map2; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci map = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0); 5362306a36Sopenharmony_ci if (map == MAP_FAILED) 5462306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci // The kernel always marks new regions as soft dirty 5762306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1, 5862306a36Sopenharmony_ci "Test %s dirty bit of allocated page\n", __func__); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci clear_softdirty(); 6162306a36Sopenharmony_ci munmap(map, pagesize); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci map2 = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0); 6462306a36Sopenharmony_ci if (map2 == MAP_FAILED) 6562306a36Sopenharmony_ci ksft_exit_fail_msg("mmap failed"); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci // Dirty bit is set for new regions even if they are reused 6862306a36Sopenharmony_ci if (map == map2) 6962306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map2) == 1, 7062306a36Sopenharmony_ci "Test %s dirty bit of reused address page\n", __func__); 7162306a36Sopenharmony_ci else 7262306a36Sopenharmony_ci ksft_test_result_skip("Test %s dirty bit of reused address page\n", __func__); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci munmap(map2, pagesize); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void test_hugepage(int pagemap_fd, int pagesize) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci char *map; 8062306a36Sopenharmony_ci int i, ret; 8162306a36Sopenharmony_ci size_t hpage_len = read_pmd_pagesize(); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!hpage_len) 8462306a36Sopenharmony_ci ksft_exit_fail_msg("Reading PMD pagesize failed"); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci map = memalign(hpage_len, hpage_len); 8762306a36Sopenharmony_ci if (!map) 8862306a36Sopenharmony_ci ksft_exit_fail_msg("memalign failed\n"); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ret = madvise(map, hpage_len, MADV_HUGEPAGE); 9162306a36Sopenharmony_ci if (ret) 9262306a36Sopenharmony_ci ksft_exit_fail_msg("madvise failed %d\n", ret); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci for (i = 0; i < hpage_len; i++) 9562306a36Sopenharmony_ci map[i] = (char)i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (check_huge_anon(map, 1, hpage_len)) { 9862306a36Sopenharmony_ci ksft_test_result_pass("Test %s huge page allocation\n", __func__); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci clear_softdirty(); 10162306a36Sopenharmony_ci for (i = 0 ; i < TEST_ITERATIONS ; i++) { 10262306a36Sopenharmony_ci if (pagemap_is_softdirty(pagemap_fd, map) == 1) { 10362306a36Sopenharmony_ci ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i); 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci clear_softdirty(); 10862306a36Sopenharmony_ci // Write something to the page to get the dirty bit enabled on the page 10962306a36Sopenharmony_ci map[0]++; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (pagemap_is_softdirty(pagemap_fd, map) == 0) { 11262306a36Sopenharmony_ci ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i); 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci clear_softdirty(); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ksft_test_result(i == TEST_ITERATIONS, "Test %s huge page dirty bit\n", __func__); 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci // hugepage allocation failed. skip these tests 12162306a36Sopenharmony_ci ksft_test_result_skip("Test %s huge page allocation\n", __func__); 12262306a36Sopenharmony_ci ksft_test_result_skip("Test %s huge page dirty bit\n", __func__); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci free(map); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void test_mprotect(int pagemap_fd, int pagesize, bool anon) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci const char *type[] = {"file", "anon"}; 13062306a36Sopenharmony_ci const char *fname = "./soft-dirty-test-file"; 13162306a36Sopenharmony_ci int test_fd; 13262306a36Sopenharmony_ci char *map; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (anon) { 13562306a36Sopenharmony_ci map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, 13662306a36Sopenharmony_ci MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 13762306a36Sopenharmony_ci if (!map) 13862306a36Sopenharmony_ci ksft_exit_fail_msg("anon mmap failed\n"); 13962306a36Sopenharmony_ci } else { 14062306a36Sopenharmony_ci test_fd = open(fname, O_RDWR | O_CREAT); 14162306a36Sopenharmony_ci if (test_fd < 0) { 14262306a36Sopenharmony_ci ksft_test_result_skip("Test %s open() file failed\n", __func__); 14362306a36Sopenharmony_ci return; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci unlink(fname); 14662306a36Sopenharmony_ci ftruncate(test_fd, pagesize); 14762306a36Sopenharmony_ci map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, 14862306a36Sopenharmony_ci MAP_SHARED, test_fd, 0); 14962306a36Sopenharmony_ci if (!map) 15062306a36Sopenharmony_ci ksft_exit_fail_msg("file mmap failed\n"); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci *map = 1; 15462306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1, 15562306a36Sopenharmony_ci "Test %s-%s dirty bit of new written page\n", 15662306a36Sopenharmony_ci __func__, type[anon]); 15762306a36Sopenharmony_ci clear_softdirty(); 15862306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0, 15962306a36Sopenharmony_ci "Test %s-%s soft-dirty clear after clear_refs\n", 16062306a36Sopenharmony_ci __func__, type[anon]); 16162306a36Sopenharmony_ci mprotect(map, pagesize, PROT_READ); 16262306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0, 16362306a36Sopenharmony_ci "Test %s-%s soft-dirty clear after marking RO\n", 16462306a36Sopenharmony_ci __func__, type[anon]); 16562306a36Sopenharmony_ci mprotect(map, pagesize, PROT_READ|PROT_WRITE); 16662306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0, 16762306a36Sopenharmony_ci "Test %s-%s soft-dirty clear after marking RW\n", 16862306a36Sopenharmony_ci __func__, type[anon]); 16962306a36Sopenharmony_ci *map = 2; 17062306a36Sopenharmony_ci ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1, 17162306a36Sopenharmony_ci "Test %s-%s soft-dirty after rewritten\n", 17262306a36Sopenharmony_ci __func__, type[anon]); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci munmap(map, pagesize); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!anon) 17762306a36Sopenharmony_ci close(test_fd); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void test_mprotect_anon(int pagemap_fd, int pagesize) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci test_mprotect(pagemap_fd, pagesize, true); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void test_mprotect_file(int pagemap_fd, int pagesize) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci test_mprotect(pagemap_fd, pagesize, false); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciint main(int argc, char **argv) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int pagemap_fd; 19362306a36Sopenharmony_ci int pagesize; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ksft_print_header(); 19662306a36Sopenharmony_ci ksft_set_plan(15); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY); 19962306a36Sopenharmony_ci if (pagemap_fd < 0) 20062306a36Sopenharmony_ci ksft_exit_fail_msg("Failed to open %s\n", PAGEMAP_FILE_PATH); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci pagesize = getpagesize(); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci test_simple(pagemap_fd, pagesize); 20562306a36Sopenharmony_ci test_vma_reuse(pagemap_fd, pagesize); 20662306a36Sopenharmony_ci test_hugepage(pagemap_fd, pagesize); 20762306a36Sopenharmony_ci test_mprotect_anon(pagemap_fd, pagesize); 20862306a36Sopenharmony_ci test_mprotect_file(pagemap_fd, pagesize); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci close(pagemap_fd); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return ksft_exit_pass(); 21362306a36Sopenharmony_ci} 214