File Index
Files and directories
Sign in
FILE

mincore_selftest.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/mincore/mincore_selftest.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * kselftest suite for mincore().
  4 *
  5 * Copyright (C) 2020 Collabora, Ltd.
  6 */
  7
  8#define _GNU_SOURCE
  9
 10#include <stdio.h>
 11#include <errno.h>
 12#include <unistd.h>
 13#include <stdlib.h>
 14#include <sys/mman.h>
 15#include <string.h>
 16#include <fcntl.h>
 17
 18#include "kselftest.h"
 19#include "kselftest_harness.h"
 20
 21/* Default test file size: 4MB */
 22#define MB (1UL << 20)
 23#define FILE_SIZE (4 * MB)
 24
 25
 26/*
 27 * Tests the user interface. This test triggers most of the documented
 28 * error conditions in mincore().
 29 */
 30TEST(basic_interface)
 31{
 32	int retval;
 33	int page_size;
 34	unsigned char vec[1];
 35	char *addr;
 36
 37	page_size = sysconf(_SC_PAGESIZE);
 38
 39	/* Query a 0 byte sized range */
 40	retval = mincore(0, 0, vec);
 41	EXPECT_EQ(0, retval);
 42
 43	/* Addresses in the specified range are invalid or unmapped */
 44	errno = 0;
 45	retval = mincore(NULL, page_size, vec);
 46	EXPECT_EQ(-1, retval);
 47	EXPECT_EQ(ENOMEM, errno);
 48
 49	errno = 0;
 50	addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
 51		MAP_SHARED | MAP_ANONYMOUS, -1, 0);
 52	ASSERT_NE(MAP_FAILED, addr) {
 53		TH_LOG("mmap error: %s", strerror(errno));
 54	}
 55
 56	/* <addr> argument is not page-aligned */
 57	errno = 0;
 58	retval = mincore(addr + 1, page_size, vec);
 59	EXPECT_EQ(-1, retval);
 60	EXPECT_EQ(EINVAL, errno);
 61
 62	/* <length> argument is too large */
 63	errno = 0;
 64	retval = mincore(addr, -1, vec);
 65	EXPECT_EQ(-1, retval);
 66	EXPECT_EQ(ENOMEM, errno);
 67
 68	/* <vec> argument points to an illegal address */
 69	errno = 0;
 70	retval = mincore(addr, page_size, NULL);
 71	EXPECT_EQ(-1, retval);
 72	EXPECT_EQ(EFAULT, errno);
 73	munmap(addr, page_size);
 74}
 75
 76
 77/*
 78 * Test mincore() behavior on a private anonymous page mapping.
 79 * Check that the page is not loaded into memory right after the mapping
 80 * but after accessing it (on-demand allocation).
 81 * Then free the page and check that it's not memory-resident.
 82 */
 83TEST(check_anonymous_locked_pages)
 84{
 85	unsigned char vec[1];
 86	char *addr;
 87	int retval;
 88	int page_size;
 89
 90	page_size = sysconf(_SC_PAGESIZE);
 91
 92	/* Map one page and check it's not memory-resident */
 93	errno = 0;
 94	addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
 95			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 96	ASSERT_NE(MAP_FAILED, addr) {
 97		TH_LOG("mmap error: %s", strerror(errno));
 98	}
 99	retval = mincore(addr, page_size, vec);
100	ASSERT_EQ(0, retval);
101	ASSERT_EQ(0, vec[0]) {
102		TH_LOG("Page found in memory before use");
103	}
104
105	/* Touch the page and check again. It should now be in memory */
106	addr[0] = 1;
107	mlock(addr, page_size);
108	retval = mincore(addr, page_size, vec);
109	ASSERT_EQ(0, retval);
110	ASSERT_EQ(1, vec[0]) {
111		TH_LOG("Page not found in memory after use");
112	}
113
114	/*
115	 * It shouldn't be memory-resident after unlocking it and
116	 * marking it as unneeded.
117	 */
118	munlock(addr, page_size);
119	madvise(addr, page_size, MADV_DONTNEED);
120	retval = mincore(addr, page_size, vec);
121	ASSERT_EQ(0, retval);
122	ASSERT_EQ(0, vec[0]) {
123		TH_LOG("Page in memory after being zapped");
124	}
125	munmap(addr, page_size);
126}
127
128
129/*
130 * Check mincore() behavior on huge pages.
131 * This test will be skipped if the mapping fails (ie. if there are no
132 * huge pages available).
133 *
134 * Make sure the system has at least one free huge page, check
135 * "HugePages_Free" in /proc/meminfo.
136 * Increment /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages if
137 * needed.
138 */
139TEST(check_huge_pages)
140{
141	unsigned char vec[1];
142	char *addr;
143	int retval;
144	int page_size;
145
146	page_size = sysconf(_SC_PAGESIZE);
147
148	errno = 0;
149	addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
150		MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
151		-1, 0);
152	if (addr == MAP_FAILED) {
153		if (errno == ENOMEM || errno == EINVAL)
154			SKIP(return, "No huge pages available or CONFIG_HUGETLB_PAGE disabled.");
155		else
156			TH_LOG("mmap error: %s", strerror(errno));
157	}
158	retval = mincore(addr, page_size, vec);
159	ASSERT_EQ(0, retval);
160	ASSERT_EQ(0, vec[0]) {
161		TH_LOG("Page found in memory before use");
162	}
163
164	addr[0] = 1;
165	mlock(addr, page_size);
166	retval = mincore(addr, page_size, vec);
167	ASSERT_EQ(0, retval);
168	ASSERT_EQ(1, vec[0]) {
169		TH_LOG("Page not found in memory after use");
170	}
171
172	munlock(addr, page_size);
173	munmap(addr, page_size);
174}
175
176
177/*
178 * Test mincore() behavior on a file-backed page.
179 * No pages should be loaded into memory right after the mapping. Then,
180 * accessing any address in the mapping range should load the page
181 * containing the address and a number of subsequent pages (readahead).
182 *
183 * The actual readahead settings depend on the test environment, so we
184 * can't make a lot of assumptions about that. This test covers the most
185 * general cases.
186 */
187TEST(check_file_mmap)
188{
189	unsigned char *vec;
190	int vec_size;
191	char *addr;
192	int retval;
193	int page_size;
194	int fd;
195	int i;
196	int ra_pages = 0;
197
198	page_size = sysconf(_SC_PAGESIZE);
199	vec_size = FILE_SIZE / page_size;
200	if (FILE_SIZE % page_size)
201		vec_size++;
202
203	vec = calloc(vec_size, sizeof(unsigned char));
204	ASSERT_NE(NULL, vec) {
205		TH_LOG("Can't allocate array");
206	}
207
208	errno = 0;
209	fd = open(".", O_TMPFILE | O_RDWR, 0600);
210	if (fd < 0) {
211		ASSERT_EQ(errno, EOPNOTSUPP) {
212			TH_LOG("Can't create temporary file: %s",
213			       strerror(errno));
214		}
215		SKIP(goto out_free, "O_TMPFILE not supported by filesystem.");
216	}
217	errno = 0;
218	retval = fallocate(fd, 0, 0, FILE_SIZE);
219	if (retval) {
220		ASSERT_EQ(errno, EOPNOTSUPP) {
221			TH_LOG("Error allocating space for the temporary file: %s",
222			       strerror(errno));
223		}
224		SKIP(goto out_close, "fallocate not supported by filesystem.");
225	}
226
227	/*
228	 * Map the whole file, the pages shouldn't be fetched yet.
229	 */
230	errno = 0;
231	addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
232			MAP_SHARED, fd, 0);
233	ASSERT_NE(MAP_FAILED, addr) {
234		TH_LOG("mmap error: %s", strerror(errno));
235	}
236	retval = mincore(addr, FILE_SIZE, vec);
237	ASSERT_EQ(0, retval);
238	for (i = 0; i < vec_size; i++) {
239		ASSERT_EQ(0, vec[i]) {
240			TH_LOG("Unexpected page in memory");
241		}
242	}
243
244	/*
245	 * Touch a page in the middle of the mapping. We expect the next
246	 * few pages (the readahead window) to be populated too.
247	 */
248	addr[FILE_SIZE / 2] = 1;
249	retval = mincore(addr, FILE_SIZE, vec);
250	ASSERT_EQ(0, retval);
251	ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
252		TH_LOG("Page not found in memory after use");
253	}
254
255	i = FILE_SIZE / 2 / page_size + 1;
256	while (i < vec_size && vec[i]) {
257		ra_pages++;
258		i++;
259	}
260	EXPECT_GT(ra_pages, 0) {
261		TH_LOG("No read-ahead pages found in memory");
262	}
263
264	/*
265	 * End of the readahead window. The rest of the pages shouldn't
266	 * be in memory.
267	 */
268	if (i < vec_size) {
269		while (i < vec_size && !vec[i])
270			i++;
271		EXPECT_EQ(vec_size, i) {
272			TH_LOG("Unexpected page in memory beyond readahead window");
273		}
274	}
275
276	munmap(addr, FILE_SIZE);
277out_close:
278	close(fd);
279out_free:
280	free(vec);
281}
282
283
284/*
285 * Test mincore() behavior on a page backed by a tmpfs file.  This test
286 * performs the same steps as the previous one.
287 */
288TEST(check_tmpfs_mmap)
289{
290	unsigned char *vec;
291	int vec_size;
292	char *addr;
293	int retval;
294	int page_size;
295	int fd;
296	int i;
297
298	page_size = sysconf(_SC_PAGESIZE);
299	vec_size = FILE_SIZE / page_size;
300	if (FILE_SIZE % page_size)
301		vec_size++;
302
303	vec = calloc(vec_size, sizeof(unsigned char));
304	ASSERT_NE(NULL, vec) {
305		TH_LOG("Can't allocate array");
306	}
307
308	errno = 0;
309	fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600);
310	ASSERT_NE(-1, fd) {
311		TH_LOG("Can't create temporary file: %s",
312			strerror(errno));
313	}
314	errno = 0;
315	retval = fallocate(fd, 0, 0, FILE_SIZE);
316	ASSERT_EQ(0, retval) {
317		TH_LOG("Error allocating space for the temporary file: %s",
318			strerror(errno));
319	}
320
321	/*
322	 * Map the whole file, the pages shouldn't be fetched yet.
323	 */
324	errno = 0;
325	addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
326			MAP_SHARED, fd, 0);
327	ASSERT_NE(MAP_FAILED, addr) {
328		TH_LOG("mmap error: %s", strerror(errno));
329	}
330	retval = mincore(addr, FILE_SIZE, vec);
331	ASSERT_EQ(0, retval);
332	for (i = 0; i < vec_size; i++) {
333		ASSERT_EQ(0, vec[i]) {
334			TH_LOG("Unexpected page in memory");
335		}
336	}
337
338	/*
339	 * Touch a page in the middle of the mapping.
340	 */
341	addr[FILE_SIZE / 2] = 1;
342	retval = mincore(addr, FILE_SIZE, vec);
343	ASSERT_EQ(0, retval);
344	ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
345		TH_LOG("Page not found in memory after use");
346	}
347
348	munmap(addr, FILE_SIZE);
349	close(fd);
350	free(vec);
351}
352
353TEST_HARNESS_MAIN
~/ Read-only file preview.