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
2/*
3 * hugepage-madvise:
4 *
5 * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE
6 * on hugetlb mappings.
7 *
8 * Before running this test, make sure the administrator has pre-allocated
9 * at least MIN_FREE_PAGES hugetlb pages and they are free. In addition,
10 * the test takes an argument that is the path to a file in a hugetlbfs
11 * filesystem. Therefore, a hugetlbfs filesystem must be mounted on some
12 * directory.
13 */
14
15#define _GNU_SOURCE
16#include <stdlib.h>
17#include <stdio.h>
18#include <unistd.h>
19#include <sys/mman.h>
20#include <fcntl.h>
21#include "vm_util.h"
22
23#define MIN_FREE_PAGES 20
24#define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */
25
26#define validate_free_pages(exp_free) \
27 do { \
28 int fhp = get_free_hugepages(); \
29 if (fhp != (exp_free)) { \
30 printf("Unexpected number of free huge " \
31 "pages line %d\n", __LINE__); \
32 exit(1); \
33 } \
34 } while (0)
35
36unsigned long huge_page_size;
37unsigned long base_page_size;
38
39unsigned long get_free_hugepages(void)
40{
41 unsigned long fhp = 0;
42 char *line = NULL;
43 size_t linelen = 0;
44 FILE *f = fopen("/proc/meminfo", "r");
45
46 if (!f)
47 return fhp;
48 while (getline(&line, &linelen, f) > 0) {
49 if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1)
50 break;
51 }
52
53 free(line);
54 fclose(f);
55 return fhp;
56}
57
58void write_fault_pages(void *addr, unsigned long nr_pages)
59{
60 unsigned long i;
61
62 for (i = 0; i < nr_pages; i++)
63 *((unsigned long *)(addr + (i * huge_page_size))) = i;
64}
65
66void read_fault_pages(void *addr, unsigned long nr_pages)
67{
68 unsigned long dummy = 0;
69 unsigned long i;
70
71 for (i = 0; i < nr_pages; i++)
72 dummy += *((unsigned long *)(addr + (i * huge_page_size)));
73}
74
75int main(int argc, char **argv)
76{
77 unsigned long free_hugepages;
78 void *addr, *addr2;
79 int fd;
80 int ret;
81
82 huge_page_size = default_huge_page_size();
83 if (!huge_page_size) {
84 printf("Unable to determine huge page size, exiting!\n");
85 exit(1);
86 }
87 base_page_size = sysconf(_SC_PAGE_SIZE);
88 if (!huge_page_size) {
89 printf("Unable to determine base page size, exiting!\n");
90 exit(1);
91 }
92
93 free_hugepages = get_free_hugepages();
94 if (free_hugepages < MIN_FREE_PAGES) {
95 printf("Not enough free huge pages to test, exiting!\n");
96 exit(1);
97 }
98
99 fd = memfd_create(argv[0], MFD_HUGETLB);
100 if (fd < 0) {
101 perror("memfd_create() failed");
102 exit(1);
103 }
104
105 /*
106 * Test validity of MADV_DONTNEED addr and length arguments. mmap
107 * size is NR_HUGE_PAGES + 2. One page at the beginning and end of
108 * the mapping will be unmapped so we KNOW there is nothing mapped
109 * there.
110 */
111 addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size,
112 PROT_READ | PROT_WRITE,
113 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
114 -1, 0);
115 if (addr == MAP_FAILED) {
116 perror("mmap");
117 exit(1);
118 }
119 if (munmap(addr, huge_page_size) ||
120 munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size,
121 huge_page_size)) {
122 perror("munmap");
123 exit(1);
124 }
125 addr = addr + huge_page_size;
126
127 write_fault_pages(addr, NR_HUGE_PAGES);
128 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
129
130 /* addr before mapping should fail */
131 ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size,
132 MADV_DONTNEED);
133 if (!ret) {
134 printf("Unexpected success of madvise call with invalid addr line %d\n",
135 __LINE__);
136 exit(1);
137 }
138
139 /* addr + length after mapping should fail */
140 ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size,
141 MADV_DONTNEED);
142 if (!ret) {
143 printf("Unexpected success of madvise call with invalid length line %d\n",
144 __LINE__);
145 exit(1);
146 }
147
148 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
149
150 /*
151 * Test alignment of MADV_DONTNEED addr and length arguments
152 */
153 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
154 PROT_READ | PROT_WRITE,
155 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
156 -1, 0);
157 if (addr == MAP_FAILED) {
158 perror("mmap");
159 exit(1);
160 }
161 write_fault_pages(addr, NR_HUGE_PAGES);
162 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
163
164 /* addr is not huge page size aligned and should fail */
165 ret = madvise(addr + base_page_size,
166 NR_HUGE_PAGES * huge_page_size - base_page_size,
167 MADV_DONTNEED);
168 if (!ret) {
169 printf("Unexpected success of madvise call with unaligned start address %d\n",
170 __LINE__);
171 exit(1);
172 }
173
174 /* addr + length should be aligned down to huge page size */
175 if (madvise(addr,
176 ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size,
177 MADV_DONTNEED)) {
178 perror("madvise");
179 exit(1);
180 }
181
182 /* should free all but last page in mapping */
183 validate_free_pages(free_hugepages - 1);
184
185 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
186 validate_free_pages(free_hugepages);
187
188 /*
189 * Test MADV_DONTNEED on anonymous private mapping
190 */
191 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
192 PROT_READ | PROT_WRITE,
193 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
194 -1, 0);
195 if (addr == MAP_FAILED) {
196 perror("mmap");
197 exit(1);
198 }
199 write_fault_pages(addr, NR_HUGE_PAGES);
200 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
201
202 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
203 perror("madvise");
204 exit(1);
205 }
206
207 /* should free all pages in mapping */
208 validate_free_pages(free_hugepages);
209
210 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
211
212 /*
213 * Test MADV_DONTNEED on private mapping of hugetlb file
214 */
215 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
216 perror("fallocate");
217 exit(1);
218 }
219 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
220
221 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
222 PROT_READ | PROT_WRITE,
223 MAP_PRIVATE, fd, 0);
224 if (addr == MAP_FAILED) {
225 perror("mmap");
226 exit(1);
227 }
228
229 /* read should not consume any pages */
230 read_fault_pages(addr, NR_HUGE_PAGES);
231 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
232
233 /* madvise should not free any pages */
234 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
235 perror("madvise");
236 exit(1);
237 }
238 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
239
240 /* writes should allocate private pages */
241 write_fault_pages(addr, NR_HUGE_PAGES);
242 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
243
244 /* madvise should free private pages */
245 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
246 perror("madvise");
247 exit(1);
248 }
249 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
250
251 /* writes should allocate private pages */
252 write_fault_pages(addr, NR_HUGE_PAGES);
253 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
254
255 /*
256 * The fallocate below certainly should free the pages associated
257 * with the file. However, pages in the private mapping are also
258 * freed. This is not the 'correct' behavior, but is expected
259 * because this is how it has worked since the initial hugetlb
260 * implementation.
261 */
262 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
263 0, NR_HUGE_PAGES * huge_page_size)) {
264 perror("fallocate");
265 exit(1);
266 }
267 validate_free_pages(free_hugepages);
268
269 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
270
271 /*
272 * Test MADV_DONTNEED on shared mapping of hugetlb file
273 */
274 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
275 perror("fallocate");
276 exit(1);
277 }
278 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
279
280 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
281 PROT_READ | PROT_WRITE,
282 MAP_SHARED, fd, 0);
283 if (addr == MAP_FAILED) {
284 perror("mmap");
285 exit(1);
286 }
287
288 /* write should not consume any pages */
289 write_fault_pages(addr, NR_HUGE_PAGES);
290 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
291
292 /* madvise should not free any pages */
293 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
294 perror("madvise");
295 exit(1);
296 }
297 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
298
299 /*
300 * Test MADV_REMOVE on shared mapping of hugetlb file
301 *
302 * madvise is same as hole punch and should free all pages.
303 */
304 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
305 perror("madvise");
306 exit(1);
307 }
308 validate_free_pages(free_hugepages);
309 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
310
311 /*
312 * Test MADV_REMOVE on shared and private mapping of hugetlb file
313 */
314 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
315 perror("fallocate");
316 exit(1);
317 }
318 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
319
320 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
321 PROT_READ | PROT_WRITE,
322 MAP_SHARED, fd, 0);
323 if (addr == MAP_FAILED) {
324 perror("mmap");
325 exit(1);
326 }
327
328 /* shared write should not consume any additional pages */
329 write_fault_pages(addr, NR_HUGE_PAGES);
330 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
331
332 addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
333 PROT_READ | PROT_WRITE,
334 MAP_PRIVATE, fd, 0);
335 if (addr2 == MAP_FAILED) {
336 perror("mmap");
337 exit(1);
338 }
339
340 /* private read should not consume any pages */
341 read_fault_pages(addr2, NR_HUGE_PAGES);
342 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
343
344 /* private write should consume additional pages */
345 write_fault_pages(addr2, NR_HUGE_PAGES);
346 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
347
348 /* madvise of shared mapping should not free any pages */
349 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
350 perror("madvise");
351 exit(1);
352 }
353 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
354
355 /* madvise of private mapping should free private pages */
356 if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
357 perror("madvise");
358 exit(1);
359 }
360 validate_free_pages(free_hugepages - NR_HUGE_PAGES);
361
362 /* private write should consume additional pages again */
363 write_fault_pages(addr2, NR_HUGE_PAGES);
364 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
365
366 /*
367 * madvise should free both file and private pages although this is
368 * not correct. private pages should not be freed, but this is
369 * expected. See comment associated with FALLOC_FL_PUNCH_HOLE call.
370 */
371 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
372 perror("madvise");
373 exit(1);
374 }
375 validate_free_pages(free_hugepages);
376
377 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
378 (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size);
379
380 close(fd);
381 return 0;
382}