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