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 */ 31 TEST(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 */ 84 TEST(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 */ 140 TEST(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 */ 188 TEST(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 */ 285 TEST(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 361 TEST_HARNESS_MAIN 362