• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/mman.h>
18 
19 #include "../kselftest.h"
20 
21 #if defined(MADV_POPULATE_READ) && defined(MADV_POPULATE_WRITE)
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 
pagemap_get_entry(int fd,char * start)30 static uint64_t pagemap_get_entry(int fd, char *start)
31 {
32 	const unsigned long pfn = (unsigned long)start / pagesize;
33 	uint64_t entry;
34 	int ret;
35 
36 	ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
37 	if (ret != sizeof(entry))
38 		ksft_exit_fail_msg("reading pagemap failed\n");
39 	return entry;
40 }
41 
pagemap_is_populated(int fd,char * start)42 static bool pagemap_is_populated(int fd, char *start)
43 {
44 	uint64_t entry = pagemap_get_entry(fd, start);
45 
46 	/* Present or swapped. */
47 	return entry & 0xc000000000000000ull;
48 }
49 
pagemap_is_softdirty(int fd,char * start)50 static bool pagemap_is_softdirty(int fd, char *start)
51 {
52 	uint64_t entry = pagemap_get_entry(fd, start);
53 
54 	return entry & 0x0080000000000000ull;
55 }
56 
sense_support(void)57 static void sense_support(void)
58 {
59 	char *addr;
60 	int ret;
61 
62 	addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
63 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
64 	if (!addr)
65 		ksft_exit_fail_msg("mmap failed\n");
66 
67 	ret = madvise(addr, pagesize, MADV_POPULATE_READ);
68 	if (ret)
69 		ksft_exit_skip("MADV_POPULATE_READ is not available\n");
70 
71 	ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);
72 	if (ret)
73 		ksft_exit_skip("MADV_POPULATE_WRITE is not available\n");
74 
75 	munmap(addr, pagesize);
76 }
77 
test_prot_read(void)78 static void test_prot_read(void)
79 {
80 	char *addr;
81 	int ret;
82 
83 	ksft_print_msg("[RUN] %s\n", __func__);
84 
85 	addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
86 	if (addr == MAP_FAILED)
87 		ksft_exit_fail_msg("mmap failed\n");
88 
89 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
90 	ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n");
91 
92 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
93 	ksft_test_result(ret == -1 && errno == EINVAL,
94 			 "MADV_POPULATE_WRITE with PROT_READ\n");
95 
96 	munmap(addr, SIZE);
97 }
98 
test_prot_write(void)99 static void test_prot_write(void)
100 {
101 	char *addr;
102 	int ret;
103 
104 	ksft_print_msg("[RUN] %s\n", __func__);
105 
106 	addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
107 	if (addr == MAP_FAILED)
108 		ksft_exit_fail_msg("mmap failed\n");
109 
110 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
111 	ksft_test_result(ret == -1 && errno == EINVAL,
112 			 "MADV_POPULATE_READ with PROT_WRITE\n");
113 
114 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
115 	ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n");
116 
117 	munmap(addr, SIZE);
118 }
119 
test_holes(void)120 static void test_holes(void)
121 {
122 	char *addr;
123 	int ret;
124 
125 	ksft_print_msg("[RUN] %s\n", __func__);
126 
127 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
128 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
129 	if (addr == MAP_FAILED)
130 		ksft_exit_fail_msg("mmap failed\n");
131 	ret = munmap(addr + pagesize, pagesize);
132 	if (ret)
133 		ksft_exit_fail_msg("munmap failed\n");
134 
135 	/* Hole in the middle */
136 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
137 	ksft_test_result(ret == -1 && errno == ENOMEM,
138 			 "MADV_POPULATE_READ with holes in the middle\n");
139 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
140 	ksft_test_result(ret == -1 && errno == ENOMEM,
141 			 "MADV_POPULATE_WRITE with holes in the middle\n");
142 
143 	/* Hole at end */
144 	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ);
145 	ksft_test_result(ret == -1 && errno == ENOMEM,
146 			 "MADV_POPULATE_READ with holes at the end\n");
147 	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE);
148 	ksft_test_result(ret == -1 && errno == ENOMEM,
149 			 "MADV_POPULATE_WRITE with holes at the end\n");
150 
151 	/* Hole at beginning */
152 	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ);
153 	ksft_test_result(ret == -1 && errno == ENOMEM,
154 			 "MADV_POPULATE_READ with holes at the beginning\n");
155 	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE);
156 	ksft_test_result(ret == -1 && errno == ENOMEM,
157 			 "MADV_POPULATE_WRITE with holes at the beginning\n");
158 
159 	munmap(addr, SIZE);
160 }
161 
range_is_populated(char * start,ssize_t size)162 static bool range_is_populated(char *start, ssize_t size)
163 {
164 	int fd = open("/proc/self/pagemap", O_RDONLY);
165 	bool ret = true;
166 
167 	if (fd < 0)
168 		ksft_exit_fail_msg("opening pagemap failed\n");
169 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
170 		if (!pagemap_is_populated(fd, start))
171 			ret = false;
172 	close(fd);
173 	return ret;
174 }
175 
range_is_not_populated(char * start,ssize_t size)176 static bool range_is_not_populated(char *start, ssize_t size)
177 {
178 	int fd = open("/proc/self/pagemap", O_RDONLY);
179 	bool ret = true;
180 
181 	if (fd < 0)
182 		ksft_exit_fail_msg("opening pagemap failed\n");
183 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
184 		if (pagemap_is_populated(fd, start))
185 			ret = false;
186 	close(fd);
187 	return ret;
188 }
189 
test_populate_read(void)190 static void test_populate_read(void)
191 {
192 	char *addr;
193 	int ret;
194 
195 	ksft_print_msg("[RUN] %s\n", __func__);
196 
197 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
198 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
199 	if (addr == MAP_FAILED)
200 		ksft_exit_fail_msg("mmap failed\n");
201 	ksft_test_result(range_is_not_populated(addr, SIZE),
202 			 "range initially not populated\n");
203 
204 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
205 	ksft_test_result(!ret, "MADV_POPULATE_READ\n");
206 	ksft_test_result(range_is_populated(addr, SIZE),
207 			 "range is populated\n");
208 
209 	munmap(addr, SIZE);
210 }
211 
test_populate_write(void)212 static void test_populate_write(void)
213 {
214 	char *addr;
215 	int ret;
216 
217 	ksft_print_msg("[RUN] %s\n", __func__);
218 
219 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
220 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
221 	if (addr == MAP_FAILED)
222 		ksft_exit_fail_msg("mmap failed\n");
223 	ksft_test_result(range_is_not_populated(addr, SIZE),
224 			 "range initially not populated\n");
225 
226 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
227 	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
228 	ksft_test_result(range_is_populated(addr, SIZE),
229 			 "range is populated\n");
230 
231 	munmap(addr, SIZE);
232 }
233 
range_is_softdirty(char * start,ssize_t size)234 static bool range_is_softdirty(char *start, ssize_t size)
235 {
236 	int fd = open("/proc/self/pagemap", O_RDONLY);
237 	bool ret = true;
238 
239 	if (fd < 0)
240 		ksft_exit_fail_msg("opening pagemap failed\n");
241 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
242 		if (!pagemap_is_softdirty(fd, start))
243 			ret = false;
244 	close(fd);
245 	return ret;
246 }
247 
range_is_not_softdirty(char * start,ssize_t size)248 static bool range_is_not_softdirty(char *start, ssize_t size)
249 {
250 	int fd = open("/proc/self/pagemap", O_RDONLY);
251 	bool ret = true;
252 
253 	if (fd < 0)
254 		ksft_exit_fail_msg("opening pagemap failed\n");
255 	for (; size > 0 && ret; size -= pagesize, start += pagesize)
256 		if (pagemap_is_softdirty(fd, start))
257 			ret = false;
258 	close(fd);
259 	return ret;
260 }
261 
clear_softdirty(void)262 static void clear_softdirty(void)
263 {
264 	int fd = open("/proc/self/clear_refs", O_WRONLY);
265 	const char *ctrl = "4";
266 	int ret;
267 
268 	if (fd < 0)
269 		ksft_exit_fail_msg("opening clear_refs failed\n");
270 	ret = write(fd, ctrl, strlen(ctrl));
271 	if (ret != strlen(ctrl))
272 		ksft_exit_fail_msg("writing clear_refs failed\n");
273 	close(fd);
274 }
275 
test_softdirty(void)276 static void test_softdirty(void)
277 {
278 	char *addr;
279 	int ret;
280 
281 	ksft_print_msg("[RUN] %s\n", __func__);
282 
283 	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
284 		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
285 	if (addr == MAP_FAILED)
286 		ksft_exit_fail_msg("mmap failed\n");
287 
288 	/* Clear any softdirty bits. */
289 	clear_softdirty();
290 	ksft_test_result(range_is_not_softdirty(addr, SIZE),
291 			 "range is not softdirty\n");
292 
293 	/* Populating READ should set softdirty. */
294 	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
295 	ksft_test_result(!ret, "MADV_POPULATE_READ\n");
296 	ksft_test_result(range_is_not_softdirty(addr, SIZE),
297 			 "range is not softdirty\n");
298 
299 	/* Populating WRITE should set softdirty. */
300 	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
301 	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
302 	ksft_test_result(range_is_softdirty(addr, SIZE),
303 			 "range is softdirty\n");
304 
305 	munmap(addr, SIZE);
306 }
307 
main(int argc,char ** argv)308 int main(int argc, char **argv)
309 {
310 	int err;
311 
312 	pagesize = getpagesize();
313 
314 	ksft_print_header();
315 	ksft_set_plan(21);
316 
317 	sense_support();
318 	test_prot_read();
319 	test_prot_write();
320 	test_holes();
321 	test_populate_read();
322 	test_populate_write();
323 	test_softdirty();
324 
325 	err = ksft_get_fail_cnt();
326 	if (err)
327 		ksft_exit_fail_msg("%d out of %d tests failed\n",
328 				   err, ksft_test_num());
329 	return ksft_exit_pass();
330 }
331 
332 #else /* defined(MADV_POPULATE_READ) && defined(MADV_POPULATE_WRITE) */
333 
334 #warning "missing MADV_POPULATE_READ or MADV_POPULATE_WRITE definition"
335 
main(int argc,char ** argv)336 int main(int argc, char **argv)
337 {
338 	ksft_print_header();
339 	ksft_exit_skip("MADV_POPULATE_READ or MADV_POPULATE_WRITE not defined\n");
340 }
341 
342 #endif /* defined(MADV_POPULATE_READ) && defined(MADV_POPULATE_WRITE) */
343