Linux Audio

Check our new training course

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