Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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	EXPECT_LT(i, vec_size) {
265		TH_LOG("Read-ahead pages reached the end of the file");
266	}
267	/*
268	 * End of the readahead window. The rest of the pages shouldn't
269	 * be in memory.
270	 */
271	if (i < vec_size) {
272		while (i < vec_size && !vec[i])
273			i++;
274		EXPECT_EQ(vec_size, i) {
275			TH_LOG("Unexpected page in memory beyond readahead window");
276		}
277	}
278
279	munmap(addr, FILE_SIZE);
280out_close:
281	close(fd);
282out_free:
283	free(vec);
284}
285
286
287/*
288 * Test mincore() behavior on a page backed by a tmpfs file.  This test
289 * performs the same steps as the previous one. However, we don't expect
290 * any readahead in this case.
291 */
292TEST(check_tmpfs_mmap)
293{
294	unsigned char *vec;
295	int vec_size;
296	char *addr;
297	int retval;
298	int page_size;
299	int fd;
300	int i;
301	int ra_pages = 0;
302
303	page_size = sysconf(_SC_PAGESIZE);
304	vec_size = FILE_SIZE / page_size;
305	if (FILE_SIZE % page_size)
306		vec_size++;
307
308	vec = calloc(vec_size, sizeof(unsigned char));
309	ASSERT_NE(NULL, vec) {
310		TH_LOG("Can't allocate array");
311	}
312
313	errno = 0;
314	fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600);
315	ASSERT_NE(-1, fd) {
316		TH_LOG("Can't create temporary file: %s",
317			strerror(errno));
318	}
319	errno = 0;
320	retval = fallocate(fd, 0, 0, FILE_SIZE);
321	ASSERT_EQ(0, retval) {
322		TH_LOG("Error allocating space for the temporary file: %s",
323			strerror(errno));
324	}
325
326	/*
327	 * Map the whole file, the pages shouldn't be fetched yet.
328	 */
329	errno = 0;
330	addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
331			MAP_SHARED, fd, 0);
332	ASSERT_NE(MAP_FAILED, addr) {
333		TH_LOG("mmap error: %s", strerror(errno));
334	}
335	retval = mincore(addr, FILE_SIZE, vec);
336	ASSERT_EQ(0, retval);
337	for (i = 0; i < vec_size; i++) {
338		ASSERT_EQ(0, vec[i]) {
339			TH_LOG("Unexpected page in memory");
340		}
341	}
342
343	/*
344	 * Touch a page in the middle of the mapping. We expect only
345	 * that page to be fetched into memory.
346	 */
347	addr[FILE_SIZE / 2] = 1;
348	retval = mincore(addr, FILE_SIZE, vec);
349	ASSERT_EQ(0, retval);
350	ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
351		TH_LOG("Page not found in memory after use");
352	}
353
354	i = FILE_SIZE / 2 / page_size + 1;
355	while (i < vec_size && vec[i]) {
356		ra_pages++;
357		i++;
358	}
359	ASSERT_EQ(ra_pages, 0) {
360		TH_LOG("Read-ahead pages found in memory");
361	}
362
363	munmap(addr, FILE_SIZE);
364	close(fd);
365	free(vec);
366}
367
368TEST_HARNESS_MAIN