1 // SPDX-License-Identifier: GPL-2.0
9 #include "../kselftest.h"
12 #define PAGEMAP_FILE_PATH "/proc/self/pagemap"
13 #define TEST_ITERATIONS 10000
15 static void test_simple(int pagemap_fd, int pagesize)
20 map = aligned_alloc(pagesize, pagesize);
22 ksft_exit_fail_msg("mmap failed\n");
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);
33 // Write something to the page to get the dirty bit enabled on the page
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);
45 ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__);
48 static void test_vma_reuse(int pagemap_fd, int pagesize)
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");
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__);
61 munmap(map, pagesize);
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");
67 // Dirty bit is set for new regions even if they are reused
69 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map2) == 1,
70 "Test %s dirty bit of reused address page\n", __func__);
72 ksft_test_result_skip("Test %s dirty bit of reused address page\n", __func__);
74 munmap(map2, pagesize);
77 static void test_hugepage(int pagemap_fd, int pagesize)
81 size_t hpage_len = read_pmd_pagesize();
83 map = memalign(hpage_len, hpage_len);
85 ksft_exit_fail_msg("memalign failed\n");
87 ret = madvise(map, hpage_len, MADV_HUGEPAGE);
89 ksft_exit_fail_msg("madvise failed %d\n", ret);
91 for (i = 0; i < hpage_len; i++)
94 if (check_huge(map)) {
95 ksft_test_result_pass("Test %s huge page allocation\n", __func__);
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);
105 // Write something to the page to get the dirty bit enabled on the page
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);
115 ksft_test_result(i == TEST_ITERATIONS, "Test %s huge page dirty bit\n", __func__);
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__);
124 static void test_mprotect(int pagemap_fd, int pagesize, bool anon)
126 const char *type[] = {"file", "anon"};
127 const char *fname = "./soft-dirty-test-file";
132 map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
133 MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
135 ksft_exit_fail_msg("anon mmap failed\n");
137 test_fd = open(fname, O_RDWR | O_CREAT);
139 ksft_test_result_skip("Test %s open() file failed\n", __func__);
143 ftruncate(test_fd, pagesize);
144 map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
145 MAP_SHARED, test_fd, 0);
147 ksft_exit_fail_msg("file mmap failed\n");
151 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
152 "Test %s-%s dirty bit of new written page\n",
153 __func__, type[anon]);
155 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
156 "Test %s-%s soft-dirty clear after clear_refs\n",
157 __func__, type[anon]);
158 mprotect(map, pagesize, PROT_READ);
159 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
160 "Test %s-%s soft-dirty clear after marking RO\n",
161 __func__, type[anon]);
162 mprotect(map, pagesize, PROT_READ|PROT_WRITE);
163 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
164 "Test %s-%s soft-dirty clear after marking RW\n",
165 __func__, type[anon]);
167 ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
168 "Test %s-%s soft-dirty after rewritten\n",
169 __func__, type[anon]);
171 munmap(map, pagesize);
177 static void test_mprotect_anon(int pagemap_fd, int pagesize)
179 test_mprotect(pagemap_fd, pagesize, true);
182 static void test_mprotect_file(int pagemap_fd, int pagesize)
184 test_mprotect(pagemap_fd, pagesize, false);
187 int main(int argc, char **argv)
195 pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY);
197 ksft_exit_fail_msg("Failed to open %s\n", PAGEMAP_FILE_PATH);
199 pagesize = getpagesize();
201 test_simple(pagemap_fd, pagesize);
202 test_vma_reuse(pagemap_fd, pagesize);
203 test_hugepage(pagemap_fd, pagesize);
204 test_mprotect_anon(pagemap_fd, pagesize);
205 test_mprotect_file(pagemap_fd, pagesize);
209 return ksft_exit_pass();