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 v5.3-rc3 850 lines 15 kB view raw
1/* SPDX-License-Identifier: GPL-2.0 */ 2#include <stdbool.h> 3#include <linux/limits.h> 4#include <sys/ptrace.h> 5#include <sys/types.h> 6#include <sys/mman.h> 7#include <unistd.h> 8#include <stdio.h> 9#include <errno.h> 10#include <poll.h> 11#include <stdlib.h> 12#include <sys/inotify.h> 13#include <string.h> 14#include <sys/wait.h> 15 16#include "../kselftest.h" 17#include "cgroup_util.h" 18 19#define DEBUG 20#ifdef DEBUG 21#define debug(args...) fprintf(stderr, args) 22#else 23#define debug(args...) 24#endif 25 26/* 27 * Check if the cgroup is frozen by looking at the cgroup.events::frozen value. 28 */ 29static int cg_check_frozen(const char *cgroup, bool frozen) 30{ 31 if (frozen) { 32 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) { 33 debug("Cgroup %s isn't frozen\n", cgroup); 34 return -1; 35 } 36 } else { 37 /* 38 * Check the cgroup.events::frozen value. 39 */ 40 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) { 41 debug("Cgroup %s is frozen\n", cgroup); 42 return -1; 43 } 44 } 45 46 return 0; 47} 48 49/* 50 * Freeze the given cgroup. 51 */ 52static int cg_freeze_nowait(const char *cgroup, bool freeze) 53{ 54 return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0"); 55} 56 57/* 58 * Prepare for waiting on cgroup.events file. 59 */ 60static int cg_prepare_for_wait(const char *cgroup) 61{ 62 int fd, ret = -1; 63 64 fd = inotify_init1(0); 65 if (fd == -1) { 66 debug("Error: inotify_init1() failed\n"); 67 return fd; 68 } 69 70 ret = inotify_add_watch(fd, cg_control(cgroup, "cgroup.events"), 71 IN_MODIFY); 72 if (ret == -1) { 73 debug("Error: inotify_add_watch() failed\n"); 74 close(fd); 75 } 76 77 return fd; 78} 79 80/* 81 * Wait for an event. If there are no events for 10 seconds, 82 * treat this an error. 83 */ 84static int cg_wait_for(int fd) 85{ 86 int ret = -1; 87 struct pollfd fds = { 88 .fd = fd, 89 .events = POLLIN, 90 }; 91 92 while (true) { 93 ret = poll(&fds, 1, 10000); 94 95 if (ret == -1) { 96 if (errno == EINTR) 97 continue; 98 debug("Error: poll() failed\n"); 99 break; 100 } 101 102 if (ret > 0 && fds.revents & POLLIN) { 103 ret = 0; 104 break; 105 } 106 } 107 108 return ret; 109} 110 111/* 112 * Attach a task to the given cgroup and wait for a cgroup frozen event. 113 * All transient events (e.g. populated) are ignored. 114 */ 115static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid, 116 bool frozen) 117{ 118 int fd, ret = -1; 119 int attempts; 120 121 fd = cg_prepare_for_wait(cgroup); 122 if (fd < 0) 123 return fd; 124 125 ret = cg_enter(cgroup, pid); 126 if (ret) 127 goto out; 128 129 for (attempts = 0; attempts < 10; attempts++) { 130 ret = cg_wait_for(fd); 131 if (ret) 132 break; 133 134 ret = cg_check_frozen(cgroup, frozen); 135 if (ret) 136 continue; 137 } 138 139out: 140 close(fd); 141 return ret; 142} 143 144/* 145 * Freeze the given cgroup and wait for the inotify signal. 146 * If there are no events in 10 seconds, treat this as an error. 147 * Then check that the cgroup is in the desired state. 148 */ 149static int cg_freeze_wait(const char *cgroup, bool freeze) 150{ 151 int fd, ret = -1; 152 153 fd = cg_prepare_for_wait(cgroup); 154 if (fd < 0) 155 return fd; 156 157 ret = cg_freeze_nowait(cgroup, freeze); 158 if (ret) { 159 debug("Error: cg_freeze_nowait() failed\n"); 160 goto out; 161 } 162 163 ret = cg_wait_for(fd); 164 if (ret) 165 goto out; 166 167 ret = cg_check_frozen(cgroup, freeze); 168out: 169 close(fd); 170 return ret; 171} 172 173/* 174 * A simple process running in a sleep loop until being 175 * re-parented. 176 */ 177static int child_fn(const char *cgroup, void *arg) 178{ 179 int ppid = getppid(); 180 181 while (getppid() == ppid) 182 usleep(1000); 183 184 return getppid() == ppid; 185} 186 187/* 188 * A simple test for the cgroup freezer: populated the cgroup with 100 189 * running processes and freeze it. Then unfreeze it. Then it kills all 190 * processes and destroys the cgroup. 191 */ 192static int test_cgfreezer_simple(const char *root) 193{ 194 int ret = KSFT_FAIL; 195 char *cgroup = NULL; 196 int i; 197 198 cgroup = cg_name(root, "cg_test_simple"); 199 if (!cgroup) 200 goto cleanup; 201 202 if (cg_create(cgroup)) 203 goto cleanup; 204 205 for (i = 0; i < 100; i++) 206 cg_run_nowait(cgroup, child_fn, NULL); 207 208 if (cg_wait_for_proc_count(cgroup, 100)) 209 goto cleanup; 210 211 if (cg_check_frozen(cgroup, false)) 212 goto cleanup; 213 214 if (cg_freeze_wait(cgroup, true)) 215 goto cleanup; 216 217 if (cg_freeze_wait(cgroup, false)) 218 goto cleanup; 219 220 ret = KSFT_PASS; 221 222cleanup: 223 if (cgroup) 224 cg_destroy(cgroup); 225 free(cgroup); 226 return ret; 227} 228 229/* 230 * The test creates the following hierarchy: 231 * A 232 * / / \ \ 233 * B E I K 234 * /\ | 235 * C D F 236 * | 237 * G 238 * | 239 * H 240 * 241 * with a process in C, H and 3 processes in K. 242 * Then it tries to freeze and unfreeze the whole tree. 243 */ 244static int test_cgfreezer_tree(const char *root) 245{ 246 char *cgroup[10] = {0}; 247 int ret = KSFT_FAIL; 248 int i; 249 250 cgroup[0] = cg_name(root, "cg_test_tree_A"); 251 if (!cgroup[0]) 252 goto cleanup; 253 254 cgroup[1] = cg_name(cgroup[0], "B"); 255 if (!cgroup[1]) 256 goto cleanup; 257 258 cgroup[2] = cg_name(cgroup[1], "C"); 259 if (!cgroup[2]) 260 goto cleanup; 261 262 cgroup[3] = cg_name(cgroup[1], "D"); 263 if (!cgroup[3]) 264 goto cleanup; 265 266 cgroup[4] = cg_name(cgroup[0], "E"); 267 if (!cgroup[4]) 268 goto cleanup; 269 270 cgroup[5] = cg_name(cgroup[4], "F"); 271 if (!cgroup[5]) 272 goto cleanup; 273 274 cgroup[6] = cg_name(cgroup[5], "G"); 275 if (!cgroup[6]) 276 goto cleanup; 277 278 cgroup[7] = cg_name(cgroup[6], "H"); 279 if (!cgroup[7]) 280 goto cleanup; 281 282 cgroup[8] = cg_name(cgroup[0], "I"); 283 if (!cgroup[8]) 284 goto cleanup; 285 286 cgroup[9] = cg_name(cgroup[0], "K"); 287 if (!cgroup[9]) 288 goto cleanup; 289 290 for (i = 0; i < 10; i++) 291 if (cg_create(cgroup[i])) 292 goto cleanup; 293 294 cg_run_nowait(cgroup[2], child_fn, NULL); 295 cg_run_nowait(cgroup[7], child_fn, NULL); 296 cg_run_nowait(cgroup[9], child_fn, NULL); 297 cg_run_nowait(cgroup[9], child_fn, NULL); 298 cg_run_nowait(cgroup[9], child_fn, NULL); 299 300 /* 301 * Wait until all child processes will enter 302 * corresponding cgroups. 303 */ 304 305 if (cg_wait_for_proc_count(cgroup[2], 1) || 306 cg_wait_for_proc_count(cgroup[7], 1) || 307 cg_wait_for_proc_count(cgroup[9], 3)) 308 goto cleanup; 309 310 /* 311 * Freeze B. 312 */ 313 if (cg_freeze_wait(cgroup[1], true)) 314 goto cleanup; 315 316 /* 317 * Freeze F. 318 */ 319 if (cg_freeze_wait(cgroup[5], true)) 320 goto cleanup; 321 322 /* 323 * Freeze G. 324 */ 325 if (cg_freeze_wait(cgroup[6], true)) 326 goto cleanup; 327 328 /* 329 * Check that A and E are not frozen. 330 */ 331 if (cg_check_frozen(cgroup[0], false)) 332 goto cleanup; 333 334 if (cg_check_frozen(cgroup[4], false)) 335 goto cleanup; 336 337 /* 338 * Freeze A. Check that A, B and E are frozen. 339 */ 340 if (cg_freeze_wait(cgroup[0], true)) 341 goto cleanup; 342 343 if (cg_check_frozen(cgroup[1], true)) 344 goto cleanup; 345 346 if (cg_check_frozen(cgroup[4], true)) 347 goto cleanup; 348 349 /* 350 * Unfreeze B, F and G 351 */ 352 if (cg_freeze_nowait(cgroup[1], false)) 353 goto cleanup; 354 355 if (cg_freeze_nowait(cgroup[5], false)) 356 goto cleanup; 357 358 if (cg_freeze_nowait(cgroup[6], false)) 359 goto cleanup; 360 361 /* 362 * Check that C and H are still frozen. 363 */ 364 if (cg_check_frozen(cgroup[2], true)) 365 goto cleanup; 366 367 if (cg_check_frozen(cgroup[7], true)) 368 goto cleanup; 369 370 /* 371 * Unfreeze A. Check that A, C and K are not frozen. 372 */ 373 if (cg_freeze_wait(cgroup[0], false)) 374 goto cleanup; 375 376 if (cg_check_frozen(cgroup[2], false)) 377 goto cleanup; 378 379 if (cg_check_frozen(cgroup[9], false)) 380 goto cleanup; 381 382 ret = KSFT_PASS; 383 384cleanup: 385 for (i = 9; i >= 0 && cgroup[i]; i--) { 386 cg_destroy(cgroup[i]); 387 free(cgroup[i]); 388 } 389 390 return ret; 391} 392 393/* 394 * A fork bomb emulator. 395 */ 396static int forkbomb_fn(const char *cgroup, void *arg) 397{ 398 int ppid; 399 400 fork(); 401 fork(); 402 403 ppid = getppid(); 404 405 while (getppid() == ppid) 406 usleep(1000); 407 408 return getppid() == ppid; 409} 410 411/* 412 * The test runs a fork bomb in a cgroup and tries to freeze it. 413 * Then it kills all processes and checks that cgroup isn't populated 414 * anymore. 415 */ 416static int test_cgfreezer_forkbomb(const char *root) 417{ 418 int ret = KSFT_FAIL; 419 char *cgroup = NULL; 420 421 cgroup = cg_name(root, "cg_forkbomb_test"); 422 if (!cgroup) 423 goto cleanup; 424 425 if (cg_create(cgroup)) 426 goto cleanup; 427 428 cg_run_nowait(cgroup, forkbomb_fn, NULL); 429 430 usleep(100000); 431 432 if (cg_freeze_wait(cgroup, true)) 433 goto cleanup; 434 435 if (cg_killall(cgroup)) 436 goto cleanup; 437 438 if (cg_wait_for_proc_count(cgroup, 0)) 439 goto cleanup; 440 441 ret = KSFT_PASS; 442 443cleanup: 444 if (cgroup) 445 cg_destroy(cgroup); 446 free(cgroup); 447 return ret; 448} 449 450/* 451 * The test creates two nested cgroups, freezes the parent 452 * and removes the child. Then it checks that the parent cgroup 453 * remains frozen and it's possible to create a new child 454 * without unfreezing. The new child is frozen too. 455 */ 456static int test_cgfreezer_rmdir(const char *root) 457{ 458 int ret = KSFT_FAIL; 459 char *parent, *child = NULL; 460 461 parent = cg_name(root, "cg_test_rmdir_A"); 462 if (!parent) 463 goto cleanup; 464 465 child = cg_name(parent, "cg_test_rmdir_B"); 466 if (!child) 467 goto cleanup; 468 469 if (cg_create(parent)) 470 goto cleanup; 471 472 if (cg_create(child)) 473 goto cleanup; 474 475 if (cg_freeze_wait(parent, true)) 476 goto cleanup; 477 478 if (cg_destroy(child)) 479 goto cleanup; 480 481 if (cg_check_frozen(parent, true)) 482 goto cleanup; 483 484 if (cg_create(child)) 485 goto cleanup; 486 487 if (cg_check_frozen(child, true)) 488 goto cleanup; 489 490 ret = KSFT_PASS; 491 492cleanup: 493 if (child) 494 cg_destroy(child); 495 free(child); 496 if (parent) 497 cg_destroy(parent); 498 free(parent); 499 return ret; 500} 501 502/* 503 * The test creates two cgroups: A and B, runs a process in A 504 * and performs several migrations: 505 * 1) A (running) -> B (frozen) 506 * 2) B (frozen) -> A (running) 507 * 3) A (frozen) -> B (frozen) 508 * 509 * On each step it checks the actual state of both cgroups. 510 */ 511static int test_cgfreezer_migrate(const char *root) 512{ 513 int ret = KSFT_FAIL; 514 char *cgroup[2] = {0}; 515 int pid; 516 517 cgroup[0] = cg_name(root, "cg_test_migrate_A"); 518 if (!cgroup[0]) 519 goto cleanup; 520 521 cgroup[1] = cg_name(root, "cg_test_migrate_B"); 522 if (!cgroup[1]) 523 goto cleanup; 524 525 if (cg_create(cgroup[0])) 526 goto cleanup; 527 528 if (cg_create(cgroup[1])) 529 goto cleanup; 530 531 pid = cg_run_nowait(cgroup[0], child_fn, NULL); 532 if (pid < 0) 533 goto cleanup; 534 535 if (cg_wait_for_proc_count(cgroup[0], 1)) 536 goto cleanup; 537 538 /* 539 * Migrate from A (running) to B (frozen) 540 */ 541 if (cg_freeze_wait(cgroup[1], true)) 542 goto cleanup; 543 544 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true)) 545 goto cleanup; 546 547 if (cg_check_frozen(cgroup[0], false)) 548 goto cleanup; 549 550 /* 551 * Migrate from B (frozen) to A (running) 552 */ 553 if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false)) 554 goto cleanup; 555 556 if (cg_check_frozen(cgroup[1], true)) 557 goto cleanup; 558 559 /* 560 * Migrate from A (frozen) to B (frozen) 561 */ 562 if (cg_freeze_wait(cgroup[0], true)) 563 goto cleanup; 564 565 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true)) 566 goto cleanup; 567 568 if (cg_check_frozen(cgroup[0], true)) 569 goto cleanup; 570 571 ret = KSFT_PASS; 572 573cleanup: 574 if (cgroup[0]) 575 cg_destroy(cgroup[0]); 576 free(cgroup[0]); 577 if (cgroup[1]) 578 cg_destroy(cgroup[1]); 579 free(cgroup[1]); 580 return ret; 581} 582 583/* 584 * The test checks that ptrace works with a tracing process in a frozen cgroup. 585 */ 586static int test_cgfreezer_ptrace(const char *root) 587{ 588 int ret = KSFT_FAIL; 589 char *cgroup = NULL; 590 siginfo_t siginfo; 591 int pid; 592 593 cgroup = cg_name(root, "cg_test_ptrace"); 594 if (!cgroup) 595 goto cleanup; 596 597 if (cg_create(cgroup)) 598 goto cleanup; 599 600 pid = cg_run_nowait(cgroup, child_fn, NULL); 601 if (pid < 0) 602 goto cleanup; 603 604 if (cg_wait_for_proc_count(cgroup, 1)) 605 goto cleanup; 606 607 if (cg_freeze_wait(cgroup, true)) 608 goto cleanup; 609 610 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL)) 611 goto cleanup; 612 613 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL)) 614 goto cleanup; 615 616 waitpid(pid, NULL, 0); 617 618 /* 619 * Cgroup has to remain frozen, however the test task 620 * is in traced state. 621 */ 622 if (cg_check_frozen(cgroup, true)) 623 goto cleanup; 624 625 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo)) 626 goto cleanup; 627 628 if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) 629 goto cleanup; 630 631 if (cg_check_frozen(cgroup, true)) 632 goto cleanup; 633 634 ret = KSFT_PASS; 635 636cleanup: 637 if (cgroup) 638 cg_destroy(cgroup); 639 free(cgroup); 640 return ret; 641} 642 643/* 644 * Check if the process is stopped. 645 */ 646static int proc_check_stopped(int pid) 647{ 648 char buf[PAGE_SIZE]; 649 int len; 650 651 len = proc_read_text(pid, "stat", buf, sizeof(buf)); 652 if (len == -1) { 653 debug("Can't get %d stat\n", pid); 654 return -1; 655 } 656 657 if (strstr(buf, "(test_freezer) T ") == NULL) { 658 debug("Process %d in the unexpected state: %s\n", pid, buf); 659 return -1; 660 } 661 662 return 0; 663} 664 665/* 666 * Test that it's possible to freeze a cgroup with a stopped process. 667 */ 668static int test_cgfreezer_stopped(const char *root) 669{ 670 int pid, ret = KSFT_FAIL; 671 char *cgroup = NULL; 672 673 cgroup = cg_name(root, "cg_test_stopped"); 674 if (!cgroup) 675 goto cleanup; 676 677 if (cg_create(cgroup)) 678 goto cleanup; 679 680 pid = cg_run_nowait(cgroup, child_fn, NULL); 681 682 if (cg_wait_for_proc_count(cgroup, 1)) 683 goto cleanup; 684 685 if (kill(pid, SIGSTOP)) 686 goto cleanup; 687 688 if (cg_check_frozen(cgroup, false)) 689 goto cleanup; 690 691 if (cg_freeze_wait(cgroup, true)) 692 goto cleanup; 693 694 if (cg_freeze_wait(cgroup, false)) 695 goto cleanup; 696 697 if (proc_check_stopped(pid)) 698 goto cleanup; 699 700 ret = KSFT_PASS; 701 702cleanup: 703 if (cgroup) 704 cg_destroy(cgroup); 705 free(cgroup); 706 return ret; 707} 708 709/* 710 * Test that it's possible to freeze a cgroup with a ptraced process. 711 */ 712static int test_cgfreezer_ptraced(const char *root) 713{ 714 int pid, ret = KSFT_FAIL; 715 char *cgroup = NULL; 716 siginfo_t siginfo; 717 718 cgroup = cg_name(root, "cg_test_ptraced"); 719 if (!cgroup) 720 goto cleanup; 721 722 if (cg_create(cgroup)) 723 goto cleanup; 724 725 pid = cg_run_nowait(cgroup, child_fn, NULL); 726 727 if (cg_wait_for_proc_count(cgroup, 1)) 728 goto cleanup; 729 730 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL)) 731 goto cleanup; 732 733 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL)) 734 goto cleanup; 735 736 waitpid(pid, NULL, 0); 737 738 if (cg_check_frozen(cgroup, false)) 739 goto cleanup; 740 741 if (cg_freeze_wait(cgroup, true)) 742 goto cleanup; 743 744 /* 745 * cg_check_frozen(cgroup, true) will fail here, 746 * because the task in in the TRACEd state. 747 */ 748 if (cg_freeze_wait(cgroup, false)) 749 goto cleanup; 750 751 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo)) 752 goto cleanup; 753 754 if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) 755 goto cleanup; 756 757 ret = KSFT_PASS; 758 759cleanup: 760 if (cgroup) 761 cg_destroy(cgroup); 762 free(cgroup); 763 return ret; 764} 765 766static int vfork_fn(const char *cgroup, void *arg) 767{ 768 int pid = vfork(); 769 770 if (pid == 0) 771 while (true) 772 sleep(1); 773 774 return pid; 775} 776 777/* 778 * Test that it's possible to freeze a cgroup with a process, 779 * which called vfork() and is waiting for a child. 780 */ 781static int test_cgfreezer_vfork(const char *root) 782{ 783 int ret = KSFT_FAIL; 784 char *cgroup = NULL; 785 786 cgroup = cg_name(root, "cg_test_vfork"); 787 if (!cgroup) 788 goto cleanup; 789 790 if (cg_create(cgroup)) 791 goto cleanup; 792 793 cg_run_nowait(cgroup, vfork_fn, NULL); 794 795 if (cg_wait_for_proc_count(cgroup, 2)) 796 goto cleanup; 797 798 if (cg_freeze_wait(cgroup, true)) 799 goto cleanup; 800 801 ret = KSFT_PASS; 802 803cleanup: 804 if (cgroup) 805 cg_destroy(cgroup); 806 free(cgroup); 807 return ret; 808} 809 810#define T(x) { x, #x } 811struct cgfreezer_test { 812 int (*fn)(const char *root); 813 const char *name; 814} tests[] = { 815 T(test_cgfreezer_simple), 816 T(test_cgfreezer_tree), 817 T(test_cgfreezer_forkbomb), 818 T(test_cgfreezer_rmdir), 819 T(test_cgfreezer_migrate), 820 T(test_cgfreezer_ptrace), 821 T(test_cgfreezer_stopped), 822 T(test_cgfreezer_ptraced), 823 T(test_cgfreezer_vfork), 824}; 825#undef T 826 827int main(int argc, char *argv[]) 828{ 829 char root[PATH_MAX]; 830 int i, ret = EXIT_SUCCESS; 831 832 if (cg_find_unified_root(root, sizeof(root))) 833 ksft_exit_skip("cgroup v2 isn't mounted\n"); 834 for (i = 0; i < ARRAY_SIZE(tests); i++) { 835 switch (tests[i].fn(root)) { 836 case KSFT_PASS: 837 ksft_test_result_pass("%s\n", tests[i].name); 838 break; 839 case KSFT_SKIP: 840 ksft_test_result_skip("%s\n", tests[i].name); 841 break; 842 default: 843 ret = EXIT_FAILURE; 844 ksft_test_result_fail("%s\n", tests[i].name); 845 break; 846 } 847 } 848 849 return ret; 850}