Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.16 677 lines 14 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#define _GNU_SOURCE 3#include <sys/mman.h> 4#include <stdint.h> 5#include <unistd.h> 6#include <string.h> 7#include <sys/time.h> 8#include <sys/resource.h> 9#include <stdbool.h> 10#include "mlock2.h" 11 12struct vm_boundaries { 13 unsigned long start; 14 unsigned long end; 15}; 16 17static int get_vm_area(unsigned long addr, struct vm_boundaries *area) 18{ 19 FILE *file; 20 int ret = 1; 21 char line[1024] = {0}; 22 char *end_addr; 23 char *stop; 24 unsigned long start; 25 unsigned long end; 26 27 if (!area) 28 return ret; 29 30 file = fopen("/proc/self/maps", "r"); 31 if (!file) { 32 perror("fopen"); 33 return ret; 34 } 35 36 memset(area, 0, sizeof(struct vm_boundaries)); 37 38 while(fgets(line, 1024, file)) { 39 end_addr = strchr(line, '-'); 40 if (!end_addr) { 41 printf("cannot parse /proc/self/maps\n"); 42 goto out; 43 } 44 *end_addr = '\0'; 45 end_addr++; 46 stop = strchr(end_addr, ' '); 47 if (!stop) { 48 printf("cannot parse /proc/self/maps\n"); 49 goto out; 50 } 51 stop = '\0'; 52 53 sscanf(line, "%lx", &start); 54 sscanf(end_addr, "%lx", &end); 55 56 if (start <= addr && end > addr) { 57 area->start = start; 58 area->end = end; 59 ret = 0; 60 goto out; 61 } 62 } 63out: 64 fclose(file); 65 return ret; 66} 67 68static uint64_t get_pageflags(unsigned long addr) 69{ 70 FILE *file; 71 uint64_t pfn; 72 unsigned long offset; 73 74 file = fopen("/proc/self/pagemap", "r"); 75 if (!file) { 76 perror("fopen pagemap"); 77 _exit(1); 78 } 79 80 offset = addr / getpagesize() * sizeof(pfn); 81 82 if (fseek(file, offset, SEEK_SET)) { 83 perror("fseek pagemap"); 84 _exit(1); 85 } 86 87 if (fread(&pfn, sizeof(pfn), 1, file) != 1) { 88 perror("fread pagemap"); 89 _exit(1); 90 } 91 92 fclose(file); 93 return pfn; 94} 95 96static uint64_t get_kpageflags(unsigned long pfn) 97{ 98 uint64_t flags; 99 FILE *file; 100 101 file = fopen("/proc/kpageflags", "r"); 102 if (!file) { 103 perror("fopen kpageflags"); 104 _exit(1); 105 } 106 107 if (fseek(file, pfn * sizeof(flags), SEEK_SET)) { 108 perror("fseek kpageflags"); 109 _exit(1); 110 } 111 112 if (fread(&flags, sizeof(flags), 1, file) != 1) { 113 perror("fread kpageflags"); 114 _exit(1); 115 } 116 117 fclose(file); 118 return flags; 119} 120 121#define VMFLAGS "VmFlags:" 122 123static bool is_vmflag_set(unsigned long addr, const char *vmflag) 124{ 125 char *line = NULL; 126 char *flags; 127 size_t size = 0; 128 bool ret = false; 129 FILE *smaps; 130 131 smaps = seek_to_smaps_entry(addr); 132 if (!smaps) { 133 printf("Unable to parse /proc/self/smaps\n"); 134 goto out; 135 } 136 137 while (getline(&line, &size, smaps) > 0) { 138 if (!strstr(line, VMFLAGS)) { 139 free(line); 140 line = NULL; 141 size = 0; 142 continue; 143 } 144 145 flags = line + strlen(VMFLAGS); 146 ret = (strstr(flags, vmflag) != NULL); 147 goto out; 148 } 149 150out: 151 free(line); 152 fclose(smaps); 153 return ret; 154} 155 156#define SIZE "Size:" 157#define RSS "Rss:" 158#define LOCKED "lo" 159 160static bool is_vma_lock_on_fault(unsigned long addr) 161{ 162 bool ret = false; 163 bool locked; 164 FILE *smaps = NULL; 165 unsigned long vma_size, vma_rss; 166 char *line = NULL; 167 char *value; 168 size_t size = 0; 169 170 locked = is_vmflag_set(addr, LOCKED); 171 if (!locked) 172 goto out; 173 174 smaps = seek_to_smaps_entry(addr); 175 if (!smaps) { 176 printf("Unable to parse /proc/self/smaps\n"); 177 goto out; 178 } 179 180 while (getline(&line, &size, smaps) > 0) { 181 if (!strstr(line, SIZE)) { 182 free(line); 183 line = NULL; 184 size = 0; 185 continue; 186 } 187 188 value = line + strlen(SIZE); 189 if (sscanf(value, "%lu kB", &vma_size) < 1) { 190 printf("Unable to parse smaps entry for Size\n"); 191 goto out; 192 } 193 break; 194 } 195 196 while (getline(&line, &size, smaps) > 0) { 197 if (!strstr(line, RSS)) { 198 free(line); 199 line = NULL; 200 size = 0; 201 continue; 202 } 203 204 value = line + strlen(RSS); 205 if (sscanf(value, "%lu kB", &vma_rss) < 1) { 206 printf("Unable to parse smaps entry for Rss\n"); 207 goto out; 208 } 209 break; 210 } 211 212 ret = locked && (vma_rss < vma_size); 213out: 214 free(line); 215 if (smaps) 216 fclose(smaps); 217 return ret; 218} 219 220#define PRESENT_BIT 0x8000000000000000ULL 221#define PFN_MASK 0x007FFFFFFFFFFFFFULL 222#define UNEVICTABLE_BIT (1UL << 18) 223 224static int lock_check(char *map) 225{ 226 unsigned long page_size = getpagesize(); 227 uint64_t page1_flags, page2_flags; 228 229 page1_flags = get_pageflags((unsigned long)map); 230 page2_flags = get_pageflags((unsigned long)map + page_size); 231 232 /* Both pages should be present */ 233 if (((page1_flags & PRESENT_BIT) == 0) || 234 ((page2_flags & PRESENT_BIT) == 0)) { 235 printf("Failed to make both pages present\n"); 236 return 1; 237 } 238 239 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 240 page2_flags = get_kpageflags(page2_flags & PFN_MASK); 241 242 /* Both pages should be unevictable */ 243 if (((page1_flags & UNEVICTABLE_BIT) == 0) || 244 ((page2_flags & UNEVICTABLE_BIT) == 0)) { 245 printf("Failed to make both pages unevictable\n"); 246 return 1; 247 } 248 249 if (!is_vmflag_set((unsigned long)map, LOCKED)) { 250 printf("VMA flag %s is missing on page 1\n", LOCKED); 251 return 1; 252 } 253 254 if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) { 255 printf("VMA flag %s is missing on page 2\n", LOCKED); 256 return 1; 257 } 258 259 return 0; 260} 261 262static int unlock_lock_check(char *map) 263{ 264 unsigned long page_size = getpagesize(); 265 uint64_t page1_flags, page2_flags; 266 267 page1_flags = get_pageflags((unsigned long)map); 268 page2_flags = get_pageflags((unsigned long)map + page_size); 269 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 270 page2_flags = get_kpageflags(page2_flags & PFN_MASK); 271 272 if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) { 273 printf("A page is still marked unevictable after unlock\n"); 274 return 1; 275 } 276 277 if (is_vmflag_set((unsigned long)map, LOCKED)) { 278 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED); 279 return 1; 280 } 281 282 if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) { 283 printf("VMA flag %s is present on page 2 after unlock\n", LOCKED); 284 return 1; 285 } 286 287 return 0; 288} 289 290static int test_mlock_lock() 291{ 292 char *map; 293 int ret = 1; 294 unsigned long page_size = getpagesize(); 295 296 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 297 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 298 if (map == MAP_FAILED) { 299 perror("test_mlock_locked mmap"); 300 goto out; 301 } 302 303 if (mlock2_(map, 2 * page_size, 0)) { 304 if (errno == ENOSYS) { 305 printf("Cannot call new mlock family, skipping test\n"); 306 _exit(0); 307 } 308 perror("mlock2(0)"); 309 goto unmap; 310 } 311 312 if (lock_check(map)) 313 goto unmap; 314 315 /* Now unlock and recheck attributes */ 316 if (munlock(map, 2 * page_size)) { 317 perror("munlock()"); 318 goto unmap; 319 } 320 321 ret = unlock_lock_check(map); 322 323unmap: 324 munmap(map, 2 * page_size); 325out: 326 return ret; 327} 328 329static int onfault_check(char *map) 330{ 331 unsigned long page_size = getpagesize(); 332 uint64_t page1_flags, page2_flags; 333 334 page1_flags = get_pageflags((unsigned long)map); 335 page2_flags = get_pageflags((unsigned long)map + page_size); 336 337 /* Neither page should be present */ 338 if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) { 339 printf("Pages were made present by MLOCK_ONFAULT\n"); 340 return 1; 341 } 342 343 *map = 'a'; 344 page1_flags = get_pageflags((unsigned long)map); 345 page2_flags = get_pageflags((unsigned long)map + page_size); 346 347 /* Only page 1 should be present */ 348 if ((page1_flags & PRESENT_BIT) == 0) { 349 printf("Page 1 is not present after fault\n"); 350 return 1; 351 } else if (page2_flags & PRESENT_BIT) { 352 printf("Page 2 was made present\n"); 353 return 1; 354 } 355 356 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 357 358 /* Page 1 should be unevictable */ 359 if ((page1_flags & UNEVICTABLE_BIT) == 0) { 360 printf("Failed to make faulted page unevictable\n"); 361 return 1; 362 } 363 364 if (!is_vma_lock_on_fault((unsigned long)map)) { 365 printf("VMA is not marked for lock on fault\n"); 366 return 1; 367 } 368 369 if (!is_vma_lock_on_fault((unsigned long)map + page_size)) { 370 printf("VMA is not marked for lock on fault\n"); 371 return 1; 372 } 373 374 return 0; 375} 376 377static int unlock_onfault_check(char *map) 378{ 379 unsigned long page_size = getpagesize(); 380 uint64_t page1_flags; 381 382 page1_flags = get_pageflags((unsigned long)map); 383 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 384 385 if (page1_flags & UNEVICTABLE_BIT) { 386 printf("Page 1 is still marked unevictable after unlock\n"); 387 return 1; 388 } 389 390 if (is_vma_lock_on_fault((unsigned long)map) || 391 is_vma_lock_on_fault((unsigned long)map + page_size)) { 392 printf("VMA is still lock on fault after unlock\n"); 393 return 1; 394 } 395 396 return 0; 397} 398 399static int test_mlock_onfault() 400{ 401 char *map; 402 int ret = 1; 403 unsigned long page_size = getpagesize(); 404 405 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 406 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 407 if (map == MAP_FAILED) { 408 perror("test_mlock_locked mmap"); 409 goto out; 410 } 411 412 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) { 413 if (errno == ENOSYS) { 414 printf("Cannot call new mlock family, skipping test\n"); 415 _exit(0); 416 } 417 perror("mlock2(MLOCK_ONFAULT)"); 418 goto unmap; 419 } 420 421 if (onfault_check(map)) 422 goto unmap; 423 424 /* Now unlock and recheck attributes */ 425 if (munlock(map, 2 * page_size)) { 426 if (errno == ENOSYS) { 427 printf("Cannot call new mlock family, skipping test\n"); 428 _exit(0); 429 } 430 perror("munlock()"); 431 goto unmap; 432 } 433 434 ret = unlock_onfault_check(map); 435unmap: 436 munmap(map, 2 * page_size); 437out: 438 return ret; 439} 440 441static int test_lock_onfault_of_present() 442{ 443 char *map; 444 int ret = 1; 445 unsigned long page_size = getpagesize(); 446 uint64_t page1_flags, page2_flags; 447 448 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 449 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 450 if (map == MAP_FAILED) { 451 perror("test_mlock_locked mmap"); 452 goto out; 453 } 454 455 *map = 'a'; 456 457 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) { 458 if (errno == ENOSYS) { 459 printf("Cannot call new mlock family, skipping test\n"); 460 _exit(0); 461 } 462 perror("mlock2(MLOCK_ONFAULT)"); 463 goto unmap; 464 } 465 466 page1_flags = get_pageflags((unsigned long)map); 467 page2_flags = get_pageflags((unsigned long)map + page_size); 468 page1_flags = get_kpageflags(page1_flags & PFN_MASK); 469 page2_flags = get_kpageflags(page2_flags & PFN_MASK); 470 471 /* Page 1 should be unevictable */ 472 if ((page1_flags & UNEVICTABLE_BIT) == 0) { 473 printf("Failed to make present page unevictable\n"); 474 goto unmap; 475 } 476 477 if (!is_vma_lock_on_fault((unsigned long)map) || 478 !is_vma_lock_on_fault((unsigned long)map + page_size)) { 479 printf("VMA with present pages is not marked lock on fault\n"); 480 goto unmap; 481 } 482 ret = 0; 483unmap: 484 munmap(map, 2 * page_size); 485out: 486 return ret; 487} 488 489static int test_munlockall() 490{ 491 char *map; 492 int ret = 1; 493 unsigned long page_size = getpagesize(); 494 495 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 496 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 497 498 if (map == MAP_FAILED) { 499 perror("test_munlockall mmap"); 500 goto out; 501 } 502 503 if (mlockall(MCL_CURRENT)) { 504 perror("mlockall(MCL_CURRENT)"); 505 goto out; 506 } 507 508 if (lock_check(map)) 509 goto unmap; 510 511 if (munlockall()) { 512 perror("munlockall()"); 513 goto unmap; 514 } 515 516 if (unlock_lock_check(map)) 517 goto unmap; 518 519 munmap(map, 2 * page_size); 520 521 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 522 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 523 524 if (map == MAP_FAILED) { 525 perror("test_munlockall second mmap"); 526 goto out; 527 } 528 529 if (mlockall(MCL_CURRENT | MCL_ONFAULT)) { 530 perror("mlockall(MCL_CURRENT | MCL_ONFAULT)"); 531 goto unmap; 532 } 533 534 if (onfault_check(map)) 535 goto unmap; 536 537 if (munlockall()) { 538 perror("munlockall()"); 539 goto unmap; 540 } 541 542 if (unlock_onfault_check(map)) 543 goto unmap; 544 545 if (mlockall(MCL_CURRENT | MCL_FUTURE)) { 546 perror("mlockall(MCL_CURRENT | MCL_FUTURE)"); 547 goto out; 548 } 549 550 if (lock_check(map)) 551 goto unmap; 552 553 if (munlockall()) { 554 perror("munlockall()"); 555 goto unmap; 556 } 557 558 ret = unlock_lock_check(map); 559 560unmap: 561 munmap(map, 2 * page_size); 562out: 563 munlockall(); 564 return ret; 565} 566 567static int test_vma_management(bool call_mlock) 568{ 569 int ret = 1; 570 void *map; 571 unsigned long page_size = getpagesize(); 572 struct vm_boundaries page1; 573 struct vm_boundaries page2; 574 struct vm_boundaries page3; 575 576 map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, 577 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 578 if (map == MAP_FAILED) { 579 perror("mmap()"); 580 return ret; 581 } 582 583 if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) { 584 if (errno == ENOSYS) { 585 printf("Cannot call new mlock family, skipping test\n"); 586 _exit(0); 587 } 588 perror("mlock(ONFAULT)\n"); 589 goto out; 590 } 591 592 if (get_vm_area((unsigned long)map, &page1) || 593 get_vm_area((unsigned long)map + page_size, &page2) || 594 get_vm_area((unsigned long)map + page_size * 2, &page3)) { 595 printf("couldn't find mapping in /proc/self/maps\n"); 596 goto out; 597 } 598 599 /* 600 * Before we unlock a portion, we need to that all three pages are in 601 * the same VMA. If they are not we abort this test (Note that this is 602 * not a failure) 603 */ 604 if (page1.start != page2.start || page2.start != page3.start) { 605 printf("VMAs are not merged to start, aborting test\n"); 606 ret = 0; 607 goto out; 608 } 609 610 if (munlock(map + page_size, page_size)) { 611 perror("munlock()"); 612 goto out; 613 } 614 615 if (get_vm_area((unsigned long)map, &page1) || 616 get_vm_area((unsigned long)map + page_size, &page2) || 617 get_vm_area((unsigned long)map + page_size * 2, &page3)) { 618 printf("couldn't find mapping in /proc/self/maps\n"); 619 goto out; 620 } 621 622 /* All three VMAs should be different */ 623 if (page1.start == page2.start || page2.start == page3.start) { 624 printf("failed to split VMA for munlock\n"); 625 goto out; 626 } 627 628 /* Now unlock the first and third page and check the VMAs again */ 629 if (munlock(map, page_size * 3)) { 630 perror("munlock()"); 631 goto out; 632 } 633 634 if (get_vm_area((unsigned long)map, &page1) || 635 get_vm_area((unsigned long)map + page_size, &page2) || 636 get_vm_area((unsigned long)map + page_size * 2, &page3)) { 637 printf("couldn't find mapping in /proc/self/maps\n"); 638 goto out; 639 } 640 641 /* Now all three VMAs should be the same */ 642 if (page1.start != page2.start || page2.start != page3.start) { 643 printf("failed to merge VMAs after munlock\n"); 644 goto out; 645 } 646 647 ret = 0; 648out: 649 munmap(map, 3 * page_size); 650 return ret; 651} 652 653static int test_mlockall(int (test_function)(bool call_mlock)) 654{ 655 int ret = 1; 656 657 if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) { 658 perror("mlockall"); 659 return ret; 660 } 661 662 ret = test_function(false); 663 munlockall(); 664 return ret; 665} 666 667int main(int argc, char **argv) 668{ 669 int ret = 0; 670 ret += test_mlock_lock(); 671 ret += test_mlock_onfault(); 672 ret += test_munlockall(); 673 ret += test_lock_onfault_of_present(); 674 ret += test_vma_management(true); 675 ret += test_mlockall(test_vma_management); 676 return ret; 677}