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
28static size_t pagesize;
29
30static 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(msg: "mmap failed\n");
39
40 ret = madvise(addr, pagesize, MADV_POPULATE_READ);
41 if (ret)
42 ksft_exit_skip(msg: "MADV_POPULATE_READ is not available\n");
43
44 ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);
45 if (ret)
46 ksft_exit_skip(msg: "MADV_POPULATE_WRITE is not available\n");
47
48 munmap(addr, pagesize);
49}
50
51static void test_prot_read(void)
52{
53 char *addr;
54 int ret;
55
56 ksft_print_msg(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(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
72static void test_prot_write(void)
73{
74 char *addr;
75 int ret;
76
77 ksft_print_msg(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(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
93static void test_holes(void)
94{
95 char *addr;
96 int ret;
97
98 ksft_print_msg(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(msg: "mmap failed\n");
104 ret = munmap(addr + pagesize, pagesize);
105 if (ret)
106 ksft_exit_fail_msg(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
135static 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(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
149static 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(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
163static void test_populate_read(void)
164{
165 char *addr;
166 int ret;
167
168 ksft_print_msg(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(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
185static void test_populate_write(void)
186{
187 char *addr;
188 int ret;
189
190 ksft_print_msg(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(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
207static 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(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
221static 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(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
235static void test_softdirty(void)
236{
237 char *addr;
238 int ret;
239
240 ksft_print_msg(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(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
267static 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
284int 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(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(msg: "%d out of %d tests failed\n",
309 err, ksft_test_num());
310 return ksft_exit_pass();
311}
312

source code of linux/tools/testing/selftests/mm/madv_populate.c