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