1 // SPDX-License-Identifier: GPL-2.0+
3 * kselftest suite for mincore().
5 * Copyright (C) 2020 Collabora, Ltd.
19 #include "../kselftest.h"
20 #include "../kselftest_harness.h"
22 /* Default test file size: 4MB */
23 #define MB (1UL << 20)
24 #define FILE_SIZE (4 * MB)
28 * Tests the user interface. This test triggers most of the documented
29 * error conditions in mincore().
38 page_size = sysconf(_SC_PAGESIZE);
40 /* Query a 0 byte sized range */
41 retval = mincore(0, 0, vec);
44 /* Addresses in the specified range are invalid or unmapped */
46 retval = mincore(NULL, page_size, vec);
47 EXPECT_EQ(-1, retval);
48 EXPECT_EQ(ENOMEM, errno);
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));
57 /* <addr> argument is not page-aligned */
59 retval = mincore(addr + 1, page_size, vec);
60 EXPECT_EQ(-1, retval);
61 EXPECT_EQ(EINVAL, errno);
63 /* <length> argument is too large */
65 retval = mincore(addr, -1, vec);
66 EXPECT_EQ(-1, retval);
67 EXPECT_EQ(ENOMEM, errno);
69 /* <vec> argument points to an illegal address */
71 retval = mincore(addr, page_size, NULL);
72 EXPECT_EQ(-1, retval);
73 EXPECT_EQ(EFAULT, errno);
74 munmap(addr, page_size);
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.
84 TEST(check_anonymous_locked_pages)
91 page_size = sysconf(_SC_PAGESIZE);
93 /* Map one page and check it's not memory-resident */
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));
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");
106 /* Touch the page and check again. It should now be in memory */
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");
116 * It shouldn't be memory-resident after unlocking it and
117 * marking it as unneeded.
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");
126 munmap(addr, page_size);
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).
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
140 TEST(check_huge_pages)
142 unsigned char vec[1];
147 page_size = sysconf(_SC_PAGESIZE);
150 addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
151 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
153 if (addr == MAP_FAILED) {
155 SKIP(return, "No huge pages available.");
157 TH_LOG("mmap error: %s", strerror(errno));
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");
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");
173 munlock(addr, page_size);
174 munmap(addr, page_size);
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).
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
188 TEST(check_file_mmap)
199 page_size = sysconf(_SC_PAGESIZE);
200 vec_size = FILE_SIZE / page_size;
201 if (FILE_SIZE % page_size)
204 vec = calloc(vec_size, sizeof(unsigned char));
205 ASSERT_NE(NULL, vec) {
206 TH_LOG("Can't allocate array");
210 fd = open(".", O_TMPFILE | O_RDWR, 0600);
212 TH_LOG("Can't create temporary file: %s",
216 retval = fallocate(fd, 0, 0, FILE_SIZE);
217 ASSERT_EQ(0, retval) {
218 TH_LOG("Error allocating space for the temporary file: %s",
223 * Map the whole file, the pages shouldn't be fetched yet.
226 addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
228 ASSERT_NE(MAP_FAILED, addr) {
229 TH_LOG("mmap error: %s", strerror(errno));
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");
240 * Touch a page in the middle of the mapping. We expect the next
241 * few pages (the readahead window) to be populated too.
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");
250 i = FILE_SIZE / 2 / page_size + 1;
251 while (i < vec_size && vec[i]) {
255 EXPECT_GT(ra_pages, 0) {
256 TH_LOG("No read-ahead pages found in memory");
259 EXPECT_LT(i, vec_size) {
260 TH_LOG("Read-ahead pages reached the end of the file");
263 * End of the readahead window. The rest of the pages shouldn't
267 while (i < vec_size && !vec[i])
269 EXPECT_EQ(vec_size, i) {
270 TH_LOG("Unexpected page in memory beyond readahead window");
274 munmap(addr, FILE_SIZE);
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.
285 TEST(check_tmpfs_mmap)
296 page_size = sysconf(_SC_PAGESIZE);
297 vec_size = FILE_SIZE / page_size;
298 if (FILE_SIZE % page_size)
301 vec = calloc(vec_size, sizeof(unsigned char));
302 ASSERT_NE(NULL, vec) {
303 TH_LOG("Can't allocate array");
307 fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600);
309 TH_LOG("Can't create temporary file: %s",
313 retval = fallocate(fd, 0, 0, FILE_SIZE);
314 ASSERT_EQ(0, retval) {
315 TH_LOG("Error allocating space for the temporary file: %s",
320 * Map the whole file, the pages shouldn't be fetched yet.
323 addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
325 ASSERT_NE(MAP_FAILED, addr) {
326 TH_LOG("mmap error: %s", strerror(errno));
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");
337 * Touch a page in the middle of the mapping. We expect only
338 * that page to be fetched into memory.
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");
347 i = FILE_SIZE / 2 / page_size + 1;
348 while (i < vec_size && vec[i]) {
352 ASSERT_EQ(ra_pages, 0) {
353 TH_LOG("Read-ahead pages found in memory");
356 munmap(addr, FILE_SIZE);