soft-dirty.c (3624B)
1// SPDX-License-Identifier: GPL-2.0 2#include <stdio.h> 3#include <string.h> 4#include <stdbool.h> 5#include <fcntl.h> 6#include <stdint.h> 7#include <malloc.h> 8#include <sys/mman.h> 9#include "../kselftest.h" 10#include "vm_util.h" 11 12#define PAGEMAP_FILE_PATH "/proc/self/pagemap" 13#define TEST_ITERATIONS 10000 14 15static void test_simple(int pagemap_fd, int pagesize) 16{ 17 int i; 18 char *map; 19 20 map = aligned_alloc(pagesize, pagesize); 21 if (!map) 22 ksft_exit_fail_msg("mmap failed\n"); 23 24 clear_softdirty(); 25 26 for (i = 0 ; i < TEST_ITERATIONS; i++) { 27 if (pagemap_is_softdirty(pagemap_fd, map) == 1) { 28 ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i); 29 break; 30 } 31 32 clear_softdirty(); 33 // Write something to the page to get the dirty bit enabled on the page 34 map[0]++; 35 36 if (pagemap_is_softdirty(pagemap_fd, map) == 0) { 37 ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i); 38 break; 39 } 40 41 clear_softdirty(); 42 } 43 free(map); 44 45 ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__); 46} 47 48static void test_vma_reuse(int pagemap_fd, int pagesize) 49{ 50 char *map, *map2; 51 52 map = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0); 53 if (map == MAP_FAILED) 54 ksft_exit_fail_msg("mmap failed"); 55 56 // The kernel always marks new regions as soft dirty 57 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1, 58 "Test %s dirty bit of allocated page\n", __func__); 59 60 clear_softdirty(); 61 munmap(map, pagesize); 62 63 map2 = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0); 64 if (map2 == MAP_FAILED) 65 ksft_exit_fail_msg("mmap failed"); 66 67 // Dirty bit is set for new regions even if they are reused 68 if (map == map2) 69 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map2) == 1, 70 "Test %s dirty bit of reused address page\n", __func__); 71 else 72 ksft_test_result_skip("Test %s dirty bit of reused address page\n", __func__); 73 74 munmap(map2, pagesize); 75} 76 77static void test_hugepage(int pagemap_fd, int pagesize) 78{ 79 char *map; 80 int i, ret; 81 size_t hpage_len = read_pmd_pagesize(); 82 83 map = memalign(hpage_len, hpage_len); 84 if (!map) 85 ksft_exit_fail_msg("memalign failed\n"); 86 87 ret = madvise(map, hpage_len, MADV_HUGEPAGE); 88 if (ret) 89 ksft_exit_fail_msg("madvise failed %d\n", ret); 90 91 for (i = 0; i < hpage_len; i++) 92 map[i] = (char)i; 93 94 if (check_huge(map)) { 95 ksft_test_result_pass("Test %s huge page allocation\n", __func__); 96 97 clear_softdirty(); 98 for (i = 0 ; i < TEST_ITERATIONS ; i++) { 99 if (pagemap_is_softdirty(pagemap_fd, map) == 1) { 100 ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i); 101 break; 102 } 103 104 clear_softdirty(); 105 // Write something to the page to get the dirty bit enabled on the page 106 map[0]++; 107 108 if (pagemap_is_softdirty(pagemap_fd, map) == 0) { 109 ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i); 110 break; 111 } 112 clear_softdirty(); 113 } 114 115 ksft_test_result(i == TEST_ITERATIONS, "Test %s huge page dirty bit\n", __func__); 116 } else { 117 // hugepage allocation failed. skip these tests 118 ksft_test_result_skip("Test %s huge page allocation\n", __func__); 119 ksft_test_result_skip("Test %s huge page dirty bit\n", __func__); 120 } 121 free(map); 122} 123 124int main(int argc, char **argv) 125{ 126 int pagemap_fd; 127 int pagesize; 128 129 ksft_print_header(); 130 ksft_set_plan(5); 131 132 pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY); 133 if (pagemap_fd < 0) 134 ksft_exit_fail_msg("Failed to open %s\n", PAGEMAP_FILE_PATH); 135 136 pagesize = getpagesize(); 137 138 test_simple(pagemap_fd, pagesize); 139 test_vma_reuse(pagemap_fd, pagesize); 140 test_hugepage(pagemap_fd, pagesize); 141 142 close(pagemap_fd); 143 144 return ksft_exit_pass(); 145}