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