xref: /linux/tools/testing/selftests/mm/madv_populate.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests
4  *
5  * Copyright 2021, Red Hat, Inc.
6  *
7  * Author(s): David Hildenbrand <david@redhat.com>
8  */
9 #define _GNU_SOURCE
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <linux/mman.h>
18 #include <sys/mman.h>
19 
20 #include "../kselftest.h"
21 #include "vm_util.h"
22 
23 /*
24  * For now, we're using 2 MiB of private anonymous memory for all tests.
25  */
26 #define SIZE (2 * 1024 * 1024)
27 
28 static size_t pagesize;
29 
30 static void sense_support(void)
31 {
32 	char *addr;
33 	int ret;
34 
35 	addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
36 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
37 	if (!addr)
38 		ksft_exit_fail_msg("mmap failed\n");
39 
40 	ret = madvise(addr, pagesize, MADV_POPULATE_READ);
41 	if (ret)
42 		ksft_exit_skip("MADV_POPULATE_READ is not available\n");
43 
44 	ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);
45 	if (ret)
46 		ksft_exit_skip("MADV_POPULATE_WRITE is not available\n");
47 
48 	munmap(addr, pagesize);
49 }
50 
51 static void test_prot_read(void)
52 {
53 	char *addr;
54 	int ret;
55 
56 	ksft_print_msg("[RUN] %s\n", __func__);
57 
58 	addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
59 	if (addr == MAP_FAILED)
60 		ksft_exit_fail_msg("mmap failed\n");
61 
62 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
63 	ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n");
64 
65 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
66 	ksft_test_result(ret == -1 && errno == EINVAL,
67 			 "MADV_POPULATE_WRITE with PROT_READ\n");
68 
69 	munmap(addr, SIZE);
70 }
71 
72 static void test_prot_write(void)
73 {
74 	char *addr;
75 	int ret;
76 
77 	ksft_print_msg("[RUN] %s\n", __func__);
78 
79 	addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
80 	if (addr == MAP_FAILED)
81 		ksft_exit_fail_msg("mmap failed\n");
82 
83 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
84 	ksft_test_result(ret == -1 && errno == EINVAL,
85 			 "MADV_POPULATE_READ with PROT_WRITE\n");
86 
87 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
88 	ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n");
89 
90 	munmap(addr, SIZE);
91 }
92 
93 static void test_holes(void)
94 {
95 	char *addr;
96 	int ret;
97 
98 	ksft_print_msg("[RUN] %s\n", __func__);
99 
100 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
101 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
102 	if (addr == MAP_FAILED)
103 		ksft_exit_fail_msg("mmap failed\n");
104 	ret = munmap(addr + pagesize, pagesize);
105 	if (ret)
106 		ksft_exit_fail_msg("munmap failed\n");
107 
108 	/* Hole in the middle */
109 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
110 	ksft_test_result(ret == -1 && errno == ENOMEM,
111 			 "MADV_POPULATE_READ with holes in the middle\n");
112 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
113 	ksft_test_result(ret == -1 && errno == ENOMEM,
114 			 "MADV_POPULATE_WRITE with holes in the middle\n");
115 
116 	/* Hole at end */
117 	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ);
118 	ksft_test_result(ret == -1 && errno == ENOMEM,
119 			 "MADV_POPULATE_READ with holes at the end\n");
120 	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE);
121 	ksft_test_result(ret == -1 && errno == ENOMEM,
122 			 "MADV_POPULATE_WRITE with holes at the end\n");
123 
124 	/* Hole at beginning */
125 	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ);
126 	ksft_test_result(ret == -1 && errno == ENOMEM,
127 			 "MADV_POPULATE_READ with holes at the beginning\n");
128 	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE);
129 	ksft_test_result(ret == -1 && errno == ENOMEM,
130 			 "MADV_POPULATE_WRITE with holes at the beginning\n");
131 
132 	munmap(addr, SIZE);
133 }
134 
135 static bool range_is_populated(char *start, ssize_t size)
136 {
137 	int fd = open("/proc/self/pagemap", O_RDONLY);
138 	bool ret = true;
139 
140 	if (fd < 0)
141 		ksft_exit_fail_msg("opening pagemap failed\n");
142 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
143 		if (!pagemap_is_populated(fd, start))
144 			ret = false;
145 	close(fd);
146 	return ret;
147 }
148 
149 static bool range_is_not_populated(char *start, ssize_t size)
150 {
151 	int fd = open("/proc/self/pagemap", O_RDONLY);
152 	bool ret = true;
153 
154 	if (fd < 0)
155 		ksft_exit_fail_msg("opening pagemap failed\n");
156 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
157 		if (pagemap_is_populated(fd, start))
158 			ret = false;
159 	close(fd);
160 	return ret;
161 }
162 
163 static void test_populate_read(void)
164 {
165 	char *addr;
166 	int ret;
167 
168 	ksft_print_msg("[RUN] %s\n", __func__);
169 
170 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
171 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
172 	if (addr == MAP_FAILED)
173 		ksft_exit_fail_msg("mmap failed\n");
174 	ksft_test_result(range_is_not_populated(addr, SIZE),
175 			 "range initially not populated\n");
176 
177 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
178 	ksft_test_result(!ret, "MADV_POPULATE_READ\n");
179 	ksft_test_result(range_is_populated(addr, SIZE),
180 			 "range is populated\n");
181 
182 	munmap(addr, SIZE);
183 }
184 
185 static void test_populate_write(void)
186 {
187 	char *addr;
188 	int ret;
189 
190 	ksft_print_msg("[RUN] %s\n", __func__);
191 
192 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
193 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
194 	if (addr == MAP_FAILED)
195 		ksft_exit_fail_msg("mmap failed\n");
196 	ksft_test_result(range_is_not_populated(addr, SIZE),
197 			 "range initially not populated\n");
198 
199 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
200 	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
201 	ksft_test_result(range_is_populated(addr, SIZE),
202 			 "range is populated\n");
203 
204 	munmap(addr, SIZE);
205 }
206 
207 static bool range_is_softdirty(char *start, ssize_t size)
208 {
209 	int fd = open("/proc/self/pagemap", O_RDONLY);
210 	bool ret = true;
211 
212 	if (fd < 0)
213 		ksft_exit_fail_msg("opening pagemap failed\n");
214 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
215 		if (!pagemap_is_softdirty(fd, start))
216 			ret = false;
217 	close(fd);
218 	return ret;
219 }
220 
221 static bool range_is_not_softdirty(char *start, ssize_t size)
222 {
223 	int fd = open("/proc/self/pagemap", O_RDONLY);
224 	bool ret = true;
225 
226 	if (fd < 0)
227 		ksft_exit_fail_msg("opening pagemap failed\n");
228 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
229 		if (pagemap_is_softdirty(fd, start))
230 			ret = false;
231 	close(fd);
232 	return ret;
233 }
234 
235 static void test_softdirty(void)
236 {
237 	char *addr;
238 	int ret;
239 
240 	ksft_print_msg("[RUN] %s\n", __func__);
241 
242 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
243 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
244 	if (addr == MAP_FAILED)
245 		ksft_exit_fail_msg("mmap failed\n");
246 
247 	/* Clear any softdirty bits. */
248 	clear_softdirty();
249 	ksft_test_result(range_is_not_softdirty(addr, SIZE),
250 			 "range is not softdirty\n");
251 
252 	/* Populating READ should set softdirty. */
253 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
254 	ksft_test_result(!ret, "MADV_POPULATE_READ\n");
255 	ksft_test_result(range_is_not_softdirty(addr, SIZE),
256 			 "range is not softdirty\n");
257 
258 	/* Populating WRITE should set softdirty. */
259 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
260 	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
261 	ksft_test_result(range_is_softdirty(addr, SIZE),
262 			 "range is softdirty\n");
263 
264 	munmap(addr, SIZE);
265 }
266 
267 static int system_has_softdirty(void)
268 {
269 	/*
270 	 * There is no way to check if the kernel supports soft-dirty, other
271 	 * than by writing to a page and seeing if the bit was set. But the
272 	 * tests are intended to check that the bit gets set when it should, so
273 	 * doing that check would turn a potentially legitimate fail into a
274 	 * skip. Fortunately, we know for sure that arm64 does not support
275 	 * soft-dirty. So for now, let's just use the arch as a corse guide.
276 	 */
277 #if defined(__aarch64__)
278 	return 0;
279 #else
280 	return 1;
281 #endif
282 }
283 
284 int main(int argc, char **argv)
285 {
286 	int nr_tests = 16;
287 	int err;
288 
289 	pagesize = getpagesize();
290 
291 	if (system_has_softdirty())
292 		nr_tests += 5;
293 
294 	ksft_print_header();
295 	ksft_set_plan(nr_tests);
296 
297 	sense_support();
298 	test_prot_read();
299 	test_prot_write();
300 	test_holes();
301 	test_populate_read();
302 	test_populate_write();
303 	if (system_has_softdirty())
304 		test_softdirty();
305 
306 	err = ksft_get_fail_cnt();
307 	if (err)
308 		ksft_exit_fail_msg("%d out of %d tests failed\n",
309 				   err, ksft_test_num());
310 	return ksft_exit_pass();
311 }
312