1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2baa489faSSeongJae Park #include <stdio.h>
3baa489faSSeongJae Park #include <string.h>
4baa489faSSeongJae Park #include <stdbool.h>
5baa489faSSeongJae Park #include <fcntl.h>
6baa489faSSeongJae Park #include <stdint.h>
7baa489faSSeongJae Park #include <malloc.h>
8baa489faSSeongJae Park #include <sys/mman.h>
9baa489faSSeongJae Park #include "../kselftest.h"
10baa489faSSeongJae Park #include "vm_util.h"
11baa489faSSeongJae Park
12baa489faSSeongJae Park #define PAGEMAP_FILE_PATH "/proc/self/pagemap"
13baa489faSSeongJae Park #define TEST_ITERATIONS 10000
14baa489faSSeongJae Park
test_simple(int pagemap_fd,int pagesize)15baa489faSSeongJae Park static void test_simple(int pagemap_fd, int pagesize)
16baa489faSSeongJae Park {
17baa489faSSeongJae Park int i;
18baa489faSSeongJae Park char *map;
19baa489faSSeongJae Park
20baa489faSSeongJae Park map = aligned_alloc(pagesize, pagesize);
21baa489faSSeongJae Park if (!map)
22baa489faSSeongJae Park ksft_exit_fail_msg("mmap failed\n");
23baa489faSSeongJae Park
24baa489faSSeongJae Park clear_softdirty();
25baa489faSSeongJae Park
26baa489faSSeongJae Park for (i = 0 ; i < TEST_ITERATIONS; i++) {
27baa489faSSeongJae Park if (pagemap_is_softdirty(pagemap_fd, map) == 1) {
28baa489faSSeongJae Park ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i);
29baa489faSSeongJae Park break;
30baa489faSSeongJae Park }
31baa489faSSeongJae Park
32baa489faSSeongJae Park clear_softdirty();
33baa489faSSeongJae Park // Write something to the page to get the dirty bit enabled on the page
34baa489faSSeongJae Park map[0]++;
35baa489faSSeongJae Park
36baa489faSSeongJae Park if (pagemap_is_softdirty(pagemap_fd, map) == 0) {
37baa489faSSeongJae Park ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i);
38baa489faSSeongJae Park break;
39baa489faSSeongJae Park }
40baa489faSSeongJae Park
41baa489faSSeongJae Park clear_softdirty();
42baa489faSSeongJae Park }
43baa489faSSeongJae Park free(map);
44baa489faSSeongJae Park
45baa489faSSeongJae Park ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__);
46baa489faSSeongJae Park }
47baa489faSSeongJae Park
test_vma_reuse(int pagemap_fd,int pagesize)48baa489faSSeongJae Park static void test_vma_reuse(int pagemap_fd, int pagesize)
49baa489faSSeongJae Park {
50baa489faSSeongJae Park char *map, *map2;
51baa489faSSeongJae Park
52baa489faSSeongJae Park map = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0);
53baa489faSSeongJae Park if (map == MAP_FAILED)
54baa489faSSeongJae Park ksft_exit_fail_msg("mmap failed");
55baa489faSSeongJae Park
56baa489faSSeongJae Park // The kernel always marks new regions as soft dirty
57baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
58baa489faSSeongJae Park "Test %s dirty bit of allocated page\n", __func__);
59baa489faSSeongJae Park
60baa489faSSeongJae Park clear_softdirty();
61baa489faSSeongJae Park munmap(map, pagesize);
62baa489faSSeongJae Park
63baa489faSSeongJae Park map2 = mmap(NULL, pagesize, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANON), -1, 0);
64baa489faSSeongJae Park if (map2 == MAP_FAILED)
65baa489faSSeongJae Park ksft_exit_fail_msg("mmap failed");
66baa489faSSeongJae Park
67baa489faSSeongJae Park // Dirty bit is set for new regions even if they are reused
68baa489faSSeongJae Park if (map == map2)
69baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map2) == 1,
70baa489faSSeongJae Park "Test %s dirty bit of reused address page\n", __func__);
71baa489faSSeongJae Park else
72baa489faSSeongJae Park ksft_test_result_skip("Test %s dirty bit of reused address page\n", __func__);
73baa489faSSeongJae Park
74baa489faSSeongJae Park munmap(map2, pagesize);
75baa489faSSeongJae Park }
76baa489faSSeongJae Park
test_hugepage(int pagemap_fd,int pagesize)77baa489faSSeongJae Park static void test_hugepage(int pagemap_fd, int pagesize)
78baa489faSSeongJae Park {
79baa489faSSeongJae Park char *map;
80baa489faSSeongJae Park int i, ret;
81baa489faSSeongJae Park size_t hpage_len = read_pmd_pagesize();
82baa489faSSeongJae Park
83d6e61afbSDavid Hildenbrand if (!hpage_len)
84d6e61afbSDavid Hildenbrand ksft_exit_fail_msg("Reading PMD pagesize failed");
85d6e61afbSDavid Hildenbrand
86baa489faSSeongJae Park map = memalign(hpage_len, hpage_len);
87baa489faSSeongJae Park if (!map)
88baa489faSSeongJae Park ksft_exit_fail_msg("memalign failed\n");
89baa489faSSeongJae Park
90baa489faSSeongJae Park ret = madvise(map, hpage_len, MADV_HUGEPAGE);
91baa489faSSeongJae Park if (ret)
92baa489faSSeongJae Park ksft_exit_fail_msg("madvise failed %d\n", ret);
93baa489faSSeongJae Park
94baa489faSSeongJae Park for (i = 0; i < hpage_len; i++)
95baa489faSSeongJae Park map[i] = (char)i;
96baa489faSSeongJae Park
97baa489faSSeongJae Park if (check_huge_anon(map, 1, hpage_len)) {
98baa489faSSeongJae Park ksft_test_result_pass("Test %s huge page allocation\n", __func__);
99baa489faSSeongJae Park
100baa489faSSeongJae Park clear_softdirty();
101baa489faSSeongJae Park for (i = 0 ; i < TEST_ITERATIONS ; i++) {
102baa489faSSeongJae Park if (pagemap_is_softdirty(pagemap_fd, map) == 1) {
103baa489faSSeongJae Park ksft_print_msg("dirty bit was 1, but should be 0 (i=%d)\n", i);
104baa489faSSeongJae Park break;
105baa489faSSeongJae Park }
106baa489faSSeongJae Park
107baa489faSSeongJae Park clear_softdirty();
108baa489faSSeongJae Park // Write something to the page to get the dirty bit enabled on the page
109baa489faSSeongJae Park map[0]++;
110baa489faSSeongJae Park
111baa489faSSeongJae Park if (pagemap_is_softdirty(pagemap_fd, map) == 0) {
112baa489faSSeongJae Park ksft_print_msg("dirty bit was 0, but should be 1 (i=%d)\n", i);
113baa489faSSeongJae Park break;
114baa489faSSeongJae Park }
115baa489faSSeongJae Park clear_softdirty();
116baa489faSSeongJae Park }
117baa489faSSeongJae Park
118baa489faSSeongJae Park ksft_test_result(i == TEST_ITERATIONS, "Test %s huge page dirty bit\n", __func__);
119baa489faSSeongJae Park } else {
120baa489faSSeongJae Park // hugepage allocation failed. skip these tests
121baa489faSSeongJae Park ksft_test_result_skip("Test %s huge page allocation\n", __func__);
122baa489faSSeongJae Park ksft_test_result_skip("Test %s huge page dirty bit\n", __func__);
123baa489faSSeongJae Park }
124baa489faSSeongJae Park free(map);
125baa489faSSeongJae Park }
126baa489faSSeongJae Park
test_mprotect(int pagemap_fd,int pagesize,bool anon)127baa489faSSeongJae Park static void test_mprotect(int pagemap_fd, int pagesize, bool anon)
128baa489faSSeongJae Park {
129baa489faSSeongJae Park const char *type[] = {"file", "anon"};
130baa489faSSeongJae Park const char *fname = "./soft-dirty-test-file";
131baa489faSSeongJae Park int test_fd;
132baa489faSSeongJae Park char *map;
133baa489faSSeongJae Park
134baa489faSSeongJae Park if (anon) {
135baa489faSSeongJae Park map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
136baa489faSSeongJae Park MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
137baa489faSSeongJae Park if (!map)
138baa489faSSeongJae Park ksft_exit_fail_msg("anon mmap failed\n");
139baa489faSSeongJae Park } else {
140*78142322SVitaly Chikunov test_fd = open(fname, O_RDWR | O_CREAT, 0664);
141baa489faSSeongJae Park if (test_fd < 0) {
142baa489faSSeongJae Park ksft_test_result_skip("Test %s open() file failed\n", __func__);
143baa489faSSeongJae Park return;
144baa489faSSeongJae Park }
145baa489faSSeongJae Park unlink(fname);
146baa489faSSeongJae Park ftruncate(test_fd, pagesize);
147baa489faSSeongJae Park map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
148baa489faSSeongJae Park MAP_SHARED, test_fd, 0);
149baa489faSSeongJae Park if (!map)
150baa489faSSeongJae Park ksft_exit_fail_msg("file mmap failed\n");
151baa489faSSeongJae Park }
152baa489faSSeongJae Park
153baa489faSSeongJae Park *map = 1;
154baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
155baa489faSSeongJae Park "Test %s-%s dirty bit of new written page\n",
156baa489faSSeongJae Park __func__, type[anon]);
157baa489faSSeongJae Park clear_softdirty();
158baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
159baa489faSSeongJae Park "Test %s-%s soft-dirty clear after clear_refs\n",
160baa489faSSeongJae Park __func__, type[anon]);
161baa489faSSeongJae Park mprotect(map, pagesize, PROT_READ);
162baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
163baa489faSSeongJae Park "Test %s-%s soft-dirty clear after marking RO\n",
164baa489faSSeongJae Park __func__, type[anon]);
165baa489faSSeongJae Park mprotect(map, pagesize, PROT_READ|PROT_WRITE);
166baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 0,
167baa489faSSeongJae Park "Test %s-%s soft-dirty clear after marking RW\n",
168baa489faSSeongJae Park __func__, type[anon]);
169baa489faSSeongJae Park *map = 2;
170baa489faSSeongJae Park ksft_test_result(pagemap_is_softdirty(pagemap_fd, map) == 1,
171baa489faSSeongJae Park "Test %s-%s soft-dirty after rewritten\n",
172baa489faSSeongJae Park __func__, type[anon]);
173baa489faSSeongJae Park
174baa489faSSeongJae Park munmap(map, pagesize);
175baa489faSSeongJae Park
176baa489faSSeongJae Park if (!anon)
177baa489faSSeongJae Park close(test_fd);
178baa489faSSeongJae Park }
179baa489faSSeongJae Park
test_mprotect_anon(int pagemap_fd,int pagesize)180baa489faSSeongJae Park static void test_mprotect_anon(int pagemap_fd, int pagesize)
181baa489faSSeongJae Park {
182baa489faSSeongJae Park test_mprotect(pagemap_fd, pagesize, true);
183baa489faSSeongJae Park }
184baa489faSSeongJae Park
test_mprotect_file(int pagemap_fd,int pagesize)185baa489faSSeongJae Park static void test_mprotect_file(int pagemap_fd, int pagesize)
186baa489faSSeongJae Park {
187baa489faSSeongJae Park test_mprotect(pagemap_fd, pagesize, false);
188baa489faSSeongJae Park }
189baa489faSSeongJae Park
main(int argc,char ** argv)190baa489faSSeongJae Park int main(int argc, char **argv)
191baa489faSSeongJae Park {
192baa489faSSeongJae Park int pagemap_fd;
193baa489faSSeongJae Park int pagesize;
194baa489faSSeongJae Park
195baa489faSSeongJae Park ksft_print_header();
196baa489faSSeongJae Park ksft_set_plan(15);
197baa489faSSeongJae Park
198baa489faSSeongJae Park pagemap_fd = open(PAGEMAP_FILE_PATH, O_RDONLY);
199baa489faSSeongJae Park if (pagemap_fd < 0)
200baa489faSSeongJae Park ksft_exit_fail_msg("Failed to open %s\n", PAGEMAP_FILE_PATH);
201baa489faSSeongJae Park
202baa489faSSeongJae Park pagesize = getpagesize();
203baa489faSSeongJae Park
204baa489faSSeongJae Park test_simple(pagemap_fd, pagesize);
205baa489faSSeongJae Park test_vma_reuse(pagemap_fd, pagesize);
206baa489faSSeongJae Park test_hugepage(pagemap_fd, pagesize);
207baa489faSSeongJae Park test_mprotect_anon(pagemap_fd, pagesize);
208baa489faSSeongJae Park test_mprotect_file(pagemap_fd, pagesize);
209baa489faSSeongJae Park
210baa489faSSeongJae Park close(pagemap_fd);
211baa489faSSeongJae Park
212baa489faSSeongJae Park return ksft_exit_pass();
213baa489faSSeongJae Park }
214