brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · e949a43 Raw
369 lines · 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_SOURCE9 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 documented28 * 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 mapping80 * 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 and116	 * 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 no132 * huge pages available).133 *134 * Make sure the system has at least one free huge page, check135 * "HugePages_Free" in /proc/meminfo.136 * Increment /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages if137 * 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		else156			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 page181 * containing the address and a number of subsequent pages (readahead).182 *183 * The actual readahead settings depend on the test environment, so we184 * can't make a lot of assumptions about that. This test covers the most185 * 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 next246	 * 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't269	 * 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 test289 * performs the same steps as the previous one. However, we don't expect290 * 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 only345	 * 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_MAIN369