1
2
3
4
5
6
7
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
25
26#define SIZE (2 * 1024 * 1024)
27
28static size_t pagesize;
29
30static 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
42static bool pagemap_is_populated(int fd, char *start)
43{
44 uint64_t entry = pagemap_get_entry(fd, start);
45
46
47 return entry & 0xc000000000000000ull;
48}
49
50static 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
57static 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
78static 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
99static 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
120static 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
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
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
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
162static 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
176static 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
190static 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
212static 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
234static 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
248static 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
262static 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
276static 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
289 clear_softdirty();
290 ksft_test_result(range_is_not_softdirty(addr, SIZE),
291 "range is not softdirty\n");
292
293
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
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
308int 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
333
334#warning "missing MADV_POPULATE_READ or MADV_POPULATE_WRITE definition"
335
336int 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
343