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