1 /* SPDX-License-Identifier: GPL-2.0 */ 2 3 #define _GNU_SOURCE 4 #include <linux/limits.h> 5 #include <linux/sched.h> 6 #include <sys/types.h> 7 #include <sys/mman.h> 8 #include <sys/mount.h> 9 #include <sys/stat.h> 10 #include <sys/wait.h> 11 #include <unistd.h> 12 #include <fcntl.h> 13 #include <sched.h> 14 #include <stdio.h> 15 #include <errno.h> 16 #include <signal.h> 17 #include <string.h> 18 #include <pthread.h> 19 20 #include "kselftest.h" 21 #include "cgroup_util.h" 22 23 static bool nsdelegate; 24 #ifndef CLONE_NEWCGROUP 25 #define CLONE_NEWCGROUP 0 26 #endif 27 28 static int touch_anon(char *buf, size_t size) 29 { 30 int fd; 31 char *pos = buf; 32 33 fd = open("/dev/urandom", O_RDONLY); 34 if (fd < 0) 35 return -1; 36 37 while (size > 0) { 38 ssize_t ret = read(fd, pos, size); 39 40 if (ret < 0) { 41 if (errno != EINTR) { 42 close(fd); 43 return -1; 44 } 45 } else { 46 pos += ret; 47 size -= ret; 48 } 49 } 50 close(fd); 51 52 return 0; 53 } 54 55 static int alloc_and_touch_anon_noexit(const char *cgroup, void *arg) 56 { 57 int ppid = getppid(); 58 size_t size = (size_t)arg; 59 void *buf; 60 61 buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 62 0, 0); 63 if (buf == MAP_FAILED) 64 return -1; 65 66 if (touch_anon((char *)buf, size)) { 67 munmap(buf, size); 68 return -1; 69 } 70 71 while (getppid() == ppid) 72 sleep(1); 73 74 munmap(buf, size); 75 return 0; 76 } 77 78 /* 79 * Create a child process that allocates and touches 100MB, then waits to be 80 * killed. Wait until the child is attached to the cgroup, kill all processes 81 * in that cgroup and wait until "cgroup.procs" is empty. At this point try to 82 * destroy the empty cgroup. The test helps detect race conditions between 83 * dying processes leaving the cgroup and cgroup destruction path. 84 */ 85 static int test_cgcore_destroy(const char *root) 86 { 87 int ret = KSFT_FAIL; 88 char *cg_test = NULL; 89 int child_pid; 90 char buf[BUF_SIZE]; 91 92 cg_test = cg_name(root, "cg_test"); 93 94 if (!cg_test) 95 goto cleanup; 96 97 for (int i = 0; i < 10; i++) { 98 if (cg_create(cg_test)) 99 goto cleanup; 100 101 child_pid = cg_run_nowait(cg_test, alloc_and_touch_anon_noexit, 102 (void *) MB(100)); 103 104 if (child_pid < 0) 105 goto cleanup; 106 107 /* wait for the child to enter cgroup */ 108 if (cg_wait_for_proc_count(cg_test, 1)) 109 goto cleanup; 110 111 if (cg_killall(cg_test)) 112 goto cleanup; 113 114 /* wait for cgroup to be empty */ 115 while (1) { 116 if (cg_read(cg_test, "cgroup.procs", buf, sizeof(buf))) 117 goto cleanup; 118 if (buf[0] == '\0') 119 break; 120 usleep(1000); 121 } 122 123 if (rmdir(cg_test)) 124 goto cleanup; 125 126 if (waitpid(child_pid, NULL, 0) < 0) 127 goto cleanup; 128 } 129 ret = KSFT_PASS; 130 cleanup: 131 if (cg_test) 132 cg_destroy(cg_test); 133 free(cg_test); 134 return ret; 135 } 136 137 /* 138 * A(0) - B(0) - C(1) 139 * \ D(0) 140 * 141 * A, B and C's "populated" fields would be 1 while D's 0. 142 * test that after the one process in C is moved to root, 143 * A,B and C's "populated" fields would flip to "0" and file 144 * modified events will be generated on the 145 * "cgroup.events" files of both cgroups. 146 */ 147 static int test_cgcore_populated(const char *root) 148 { 149 int ret = KSFT_FAIL; 150 int err; 151 char *cg_test_a = NULL, *cg_test_b = NULL; 152 char *cg_test_c = NULL, *cg_test_d = NULL; 153 int cgroup_fd = -EBADF; 154 pid_t pid; 155 156 if (cg_test_v1_named) 157 return KSFT_SKIP; 158 159 cg_test_a = cg_name(root, "cg_test_a"); 160 cg_test_b = cg_name(root, "cg_test_a/cg_test_b"); 161 cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c"); 162 cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d"); 163 164 if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d) 165 goto cleanup; 166 167 if (cg_create(cg_test_a)) 168 goto cleanup; 169 170 if (cg_create(cg_test_b)) 171 goto cleanup; 172 173 if (cg_create(cg_test_c)) 174 goto cleanup; 175 176 if (cg_create(cg_test_d)) 177 goto cleanup; 178 179 if (cg_enter_current(cg_test_c)) 180 goto cleanup; 181 182 if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n")) 183 goto cleanup; 184 185 if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n")) 186 goto cleanup; 187 188 if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n")) 189 goto cleanup; 190 191 if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) 192 goto cleanup; 193 194 if (cg_enter_current(root)) 195 goto cleanup; 196 197 if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n")) 198 goto cleanup; 199 200 if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n")) 201 goto cleanup; 202 203 if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n")) 204 goto cleanup; 205 206 if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) 207 goto cleanup; 208 209 /* Test that we can directly clone into a new cgroup. */ 210 cgroup_fd = dirfd_open_opath(cg_test_d); 211 if (cgroup_fd < 0) 212 goto cleanup; 213 214 pid = clone_into_cgroup(cgroup_fd); 215 if (pid < 0) { 216 if (errno == ENOSYS) 217 goto cleanup_pass; 218 goto cleanup; 219 } 220 221 if (pid == 0) { 222 if (raise(SIGSTOP)) 223 exit(EXIT_FAILURE); 224 exit(EXIT_SUCCESS); 225 } 226 227 err = cg_read_strcmp(cg_test_d, "cgroup.events", "populated 1\n"); 228 229 (void)clone_reap(pid, WSTOPPED); 230 (void)kill(pid, SIGCONT); 231 (void)clone_reap(pid, WEXITED); 232 233 if (err) 234 goto cleanup; 235 236 if (cg_read_strcmp_wait(cg_test_d, "cgroup.events", 237 "populated 0\n")) 238 goto cleanup; 239 240 /* Remove cgroup. */ 241 if (cg_test_d) { 242 cg_destroy(cg_test_d); 243 free(cg_test_d); 244 cg_test_d = NULL; 245 } 246 247 pid = clone_into_cgroup(cgroup_fd); 248 if (pid < 0) 249 goto cleanup_pass; 250 if (pid == 0) 251 exit(EXIT_SUCCESS); 252 (void)clone_reap(pid, WEXITED); 253 goto cleanup; 254 255 cleanup_pass: 256 ret = KSFT_PASS; 257 258 cleanup: 259 if (cg_test_d) 260 cg_destroy(cg_test_d); 261 if (cg_test_c) 262 cg_destroy(cg_test_c); 263 if (cg_test_b) 264 cg_destroy(cg_test_b); 265 if (cg_test_a) 266 cg_destroy(cg_test_a); 267 free(cg_test_d); 268 free(cg_test_c); 269 free(cg_test_b); 270 free(cg_test_a); 271 if (cgroup_fd >= 0) 272 close(cgroup_fd); 273 return ret; 274 } 275 276 /* 277 * A (domain threaded) - B (threaded) - C (domain) 278 * 279 * test that C can't be used until it is turned into a 280 * threaded cgroup. "cgroup.type" file will report "domain (invalid)" in 281 * these cases. Operations which fail due to invalid topology use 282 * EOPNOTSUPP as the errno. 283 */ 284 static int test_cgcore_invalid_domain(const char *root) 285 { 286 int ret = KSFT_FAIL; 287 char *grandparent = NULL, *parent = NULL, *child = NULL; 288 289 if (cg_test_v1_named) 290 return KSFT_SKIP; 291 292 grandparent = cg_name(root, "cg_test_grandparent"); 293 parent = cg_name(root, "cg_test_grandparent/cg_test_parent"); 294 child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child"); 295 if (!parent || !child || !grandparent) 296 goto cleanup; 297 298 if (cg_create(grandparent)) 299 goto cleanup; 300 301 if (cg_create(parent)) 302 goto cleanup; 303 304 if (cg_create(child)) 305 goto cleanup; 306 307 if (cg_write(parent, "cgroup.type", "threaded")) 308 goto cleanup; 309 310 if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n")) 311 goto cleanup; 312 313 if (!cg_enter_current(child)) 314 goto cleanup; 315 316 if (errno != EOPNOTSUPP) 317 goto cleanup; 318 319 if (!clone_into_cgroup_run_wait(child)) 320 goto cleanup; 321 322 if (errno == ENOSYS) 323 goto cleanup_pass; 324 325 if (errno != EOPNOTSUPP) 326 goto cleanup; 327 328 cleanup_pass: 329 ret = KSFT_PASS; 330 331 cleanup: 332 cg_enter_current(root); 333 if (child) 334 cg_destroy(child); 335 if (parent) 336 cg_destroy(parent); 337 if (grandparent) 338 cg_destroy(grandparent); 339 free(child); 340 free(parent); 341 free(grandparent); 342 return ret; 343 } 344 345 /* 346 * Test that when a child becomes threaded 347 * the parent type becomes domain threaded. 348 */ 349 static int test_cgcore_parent_becomes_threaded(const char *root) 350 { 351 int ret = KSFT_FAIL; 352 char *parent = NULL, *child = NULL; 353 354 if (cg_test_v1_named) 355 return KSFT_SKIP; 356 357 parent = cg_name(root, "cg_test_parent"); 358 child = cg_name(root, "cg_test_parent/cg_test_child"); 359 if (!parent || !child) 360 goto cleanup; 361 362 if (cg_create(parent)) 363 goto cleanup; 364 365 if (cg_create(child)) 366 goto cleanup; 367 368 if (cg_write(child, "cgroup.type", "threaded")) 369 goto cleanup; 370 371 if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n")) 372 goto cleanup; 373 374 ret = KSFT_PASS; 375 376 cleanup: 377 if (child) 378 cg_destroy(child); 379 if (parent) 380 cg_destroy(parent); 381 free(child); 382 free(parent); 383 return ret; 384 385 } 386 387 /* 388 * Test that there's no internal process constrain on threaded cgroups. 389 * You can add threads/processes on a parent with a controller enabled. 390 */ 391 static int test_cgcore_no_internal_process_constraint_on_threads(const char *root) 392 { 393 int ret = KSFT_FAIL; 394 char *parent = NULL, *child = NULL; 395 396 if (cg_test_v1_named || 397 cg_read_strstr(root, "cgroup.controllers", "cpu") || 398 cg_write(root, "cgroup.subtree_control", "+cpu")) { 399 ret = KSFT_SKIP; 400 goto cleanup; 401 } 402 403 parent = cg_name(root, "cg_test_parent"); 404 child = cg_name(root, "cg_test_parent/cg_test_child"); 405 if (!parent || !child) 406 goto cleanup; 407 408 if (cg_create(parent)) 409 goto cleanup; 410 411 if (cg_create(child)) 412 goto cleanup; 413 414 if (cg_write(parent, "cgroup.type", "threaded")) 415 goto cleanup; 416 417 if (cg_write(child, "cgroup.type", "threaded")) 418 goto cleanup; 419 420 if (cg_write(parent, "cgroup.subtree_control", "+cpu")) 421 goto cleanup; 422 423 if (cg_enter_current(parent)) 424 goto cleanup; 425 426 ret = KSFT_PASS; 427 428 cleanup: 429 cg_enter_current(root); 430 if (child) 431 cg_destroy(child); 432 if (parent) 433 cg_destroy(parent); 434 free(child); 435 free(parent); 436 return ret; 437 } 438 439 /* 440 * Test that you can't enable a controller on a child if it's not enabled 441 * on the parent. 442 */ 443 static int test_cgcore_top_down_constraint_enable(const char *root) 444 { 445 int ret = KSFT_FAIL; 446 char *parent = NULL, *child = NULL; 447 448 if (cg_test_v1_named) 449 return KSFT_SKIP; 450 451 parent = cg_name(root, "cg_test_parent"); 452 child = cg_name(root, "cg_test_parent/cg_test_child"); 453 if (!parent || !child) 454 goto cleanup; 455 456 if (cg_create(parent)) 457 goto cleanup; 458 459 if (cg_create(child)) 460 goto cleanup; 461 462 if (!cg_write(child, "cgroup.subtree_control", "+memory")) 463 goto cleanup; 464 465 ret = KSFT_PASS; 466 467 cleanup: 468 if (child) 469 cg_destroy(child); 470 if (parent) 471 cg_destroy(parent); 472 free(child); 473 free(parent); 474 return ret; 475 } 476 477 /* 478 * Test that you can't disable a controller on a parent 479 * if it's enabled in a child. 480 */ 481 static int test_cgcore_top_down_constraint_disable(const char *root) 482 { 483 int ret = KSFT_FAIL; 484 char *parent = NULL, *child = NULL; 485 486 if (cg_test_v1_named) 487 return KSFT_SKIP; 488 489 parent = cg_name(root, "cg_test_parent"); 490 child = cg_name(root, "cg_test_parent/cg_test_child"); 491 if (!parent || !child) 492 goto cleanup; 493 494 if (cg_create(parent)) 495 goto cleanup; 496 497 if (cg_create(child)) 498 goto cleanup; 499 500 if (cg_write(parent, "cgroup.subtree_control", "+memory")) 501 goto cleanup; 502 503 if (cg_write(child, "cgroup.subtree_control", "+memory")) 504 goto cleanup; 505 506 if (!cg_write(parent, "cgroup.subtree_control", "-memory")) 507 goto cleanup; 508 509 ret = KSFT_PASS; 510 511 cleanup: 512 if (child) 513 cg_destroy(child); 514 if (parent) 515 cg_destroy(parent); 516 free(child); 517 free(parent); 518 return ret; 519 } 520 521 /* 522 * Test internal process constraint. 523 * You can't add a pid to a domain parent if a controller is enabled. 524 */ 525 static int test_cgcore_internal_process_constraint(const char *root) 526 { 527 int ret = KSFT_FAIL; 528 char *parent = NULL, *child = NULL; 529 530 if (cg_test_v1_named) 531 return KSFT_SKIP; 532 533 parent = cg_name(root, "cg_test_parent"); 534 child = cg_name(root, "cg_test_parent/cg_test_child"); 535 if (!parent || !child) 536 goto cleanup; 537 538 if (cg_create(parent)) 539 goto cleanup; 540 541 if (cg_create(child)) 542 goto cleanup; 543 544 if (cg_write(parent, "cgroup.subtree_control", "+memory")) 545 goto cleanup; 546 547 if (!cg_enter_current(parent)) 548 goto cleanup; 549 550 if (!clone_into_cgroup_run_wait(parent)) 551 goto cleanup; 552 553 ret = KSFT_PASS; 554 555 cleanup: 556 if (child) 557 cg_destroy(child); 558 if (parent) 559 cg_destroy(parent); 560 free(child); 561 free(parent); 562 return ret; 563 } 564 565 static void *dummy_thread_fn(void *arg) 566 { 567 return (void *)(size_t)pause(); 568 } 569 570 /* 571 * Test threadgroup migration. 572 * All threads of a process are migrated together. 573 */ 574 static int test_cgcore_proc_migration(const char *root) 575 { 576 int ret = KSFT_FAIL; 577 int t, c_threads = 0, n_threads = 13; 578 char *src = NULL, *dst = NULL; 579 pthread_t threads[n_threads]; 580 581 src = cg_name(root, "cg_src"); 582 dst = cg_name(root, "cg_dst"); 583 if (!src || !dst) 584 goto cleanup; 585 586 if (cg_create(src)) 587 goto cleanup; 588 if (cg_create(dst)) 589 goto cleanup; 590 591 if (cg_enter_current(src)) 592 goto cleanup; 593 594 for (c_threads = 0; c_threads < n_threads; ++c_threads) { 595 if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL)) 596 goto cleanup; 597 } 598 599 cg_enter_current(dst); 600 if (cg_read_lc(dst, CG_THREADS_FILE) != n_threads + 1) 601 goto cleanup; 602 603 ret = KSFT_PASS; 604 605 cleanup: 606 for (t = 0; t < c_threads; ++t) { 607 pthread_cancel(threads[t]); 608 } 609 610 for (t = 0; t < c_threads; ++t) { 611 pthread_join(threads[t], NULL); 612 } 613 614 cg_enter_current(root); 615 616 if (dst) 617 cg_destroy(dst); 618 if (src) 619 cg_destroy(src); 620 free(dst); 621 free(src); 622 return ret; 623 } 624 625 static void *migrating_thread_fn(void *arg) 626 { 627 int g, i, n_iterations = 1000; 628 char **grps = arg; 629 char lines[3][PATH_MAX]; 630 631 for (g = 1; g < 3; ++g) 632 snprintf(lines[g], sizeof(lines[g]), CG_PATH_FORMAT, grps[g] + strlen(grps[0])); 633 634 for (i = 0; i < n_iterations; ++i) { 635 cg_enter_current_thread(grps[(i % 2) + 1]); 636 637 if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1])) 638 return (void *)-1; 639 } 640 return NULL; 641 } 642 643 /* 644 * Test single thread migration. 645 * Threaded cgroups allow successful migration of a thread. 646 */ 647 static int test_cgcore_thread_migration(const char *root) 648 { 649 int ret = KSFT_FAIL; 650 char *dom = NULL; 651 char line[PATH_MAX]; 652 char *grps[3] = { (char *)root, NULL, NULL }; 653 pthread_t thr; 654 void *retval; 655 656 dom = cg_name(root, "cg_dom"); 657 grps[1] = cg_name(root, "cg_dom/cg_src"); 658 grps[2] = cg_name(root, "cg_dom/cg_dst"); 659 if (!grps[1] || !grps[2] || !dom) 660 goto cleanup; 661 662 if (cg_create(dom)) 663 goto cleanup; 664 if (cg_create(grps[1])) 665 goto cleanup; 666 if (cg_create(grps[2])) 667 goto cleanup; 668 669 if (!cg_test_v1_named) { 670 if (cg_write(grps[1], "cgroup.type", "threaded")) 671 goto cleanup; 672 if (cg_write(grps[2], "cgroup.type", "threaded")) 673 goto cleanup; 674 } 675 676 if (cg_enter_current(grps[1])) 677 goto cleanup; 678 679 if (pthread_create(&thr, NULL, migrating_thread_fn, grps)) 680 goto cleanup; 681 682 if (pthread_join(thr, &retval)) 683 goto cleanup; 684 685 if (retval) 686 goto cleanup; 687 688 snprintf(line, sizeof(line), CG_PATH_FORMAT, grps[1] + strlen(grps[0])); 689 if (proc_read_strstr(0, 1, "cgroup", line)) 690 goto cleanup; 691 692 ret = KSFT_PASS; 693 694 cleanup: 695 cg_enter_current(root); 696 if (grps[2]) 697 cg_destroy(grps[2]); 698 if (grps[1]) 699 cg_destroy(grps[1]); 700 if (dom) 701 cg_destroy(dom); 702 free(grps[2]); 703 free(grps[1]); 704 free(dom); 705 return ret; 706 } 707 708 /* 709 * cgroup migration permission check should be performed based on the 710 * credentials at the time of open instead of write. 711 */ 712 static int test_cgcore_lesser_euid_open(const char *root) 713 { 714 const uid_t test_euid = TEST_UID; 715 int ret = KSFT_FAIL; 716 char *cg_test_a = NULL, *cg_test_b = NULL; 717 char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; 718 int cg_test_b_procs_fd = -1; 719 uid_t saved_uid; 720 721 cg_test_a = cg_name(root, "cg_test_a"); 722 cg_test_b = cg_name(root, "cg_test_b"); 723 724 if (!cg_test_a || !cg_test_b) 725 goto cleanup; 726 727 cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs"); 728 cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs"); 729 730 if (!cg_test_a_procs || !cg_test_b_procs) 731 goto cleanup; 732 733 if (cg_create(cg_test_a) || cg_create(cg_test_b)) 734 goto cleanup; 735 736 if (cg_enter_current(cg_test_a)) 737 goto cleanup; 738 739 if (chown(cg_test_a_procs, test_euid, -1) || 740 chown(cg_test_b_procs, test_euid, -1)) 741 goto cleanup; 742 743 saved_uid = geteuid(); 744 if (seteuid(test_euid)) 745 goto cleanup; 746 747 cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR); 748 749 if (seteuid(saved_uid)) 750 goto cleanup; 751 752 if (cg_test_b_procs_fd < 0) 753 goto cleanup; 754 755 if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES) 756 goto cleanup; 757 758 ret = KSFT_PASS; 759 760 cleanup: 761 cg_enter_current(root); 762 if (cg_test_b_procs_fd >= 0) 763 close(cg_test_b_procs_fd); 764 if (cg_test_b) 765 cg_destroy(cg_test_b); 766 if (cg_test_a) 767 cg_destroy(cg_test_a); 768 free(cg_test_b_procs); 769 free(cg_test_a_procs); 770 free(cg_test_b); 771 free(cg_test_a); 772 return ret; 773 } 774 775 struct lesser_ns_open_thread_arg { 776 const char *path; 777 int fd; 778 int err; 779 }; 780 781 static int lesser_ns_open_thread_fn(void *arg) 782 { 783 struct lesser_ns_open_thread_arg *targ = arg; 784 785 targ->fd = open(targ->path, O_RDWR); 786 targ->err = errno; 787 return 0; 788 } 789 790 /* 791 * cgroup migration permission check should be performed based on the cgroup 792 * namespace at the time of open instead of write. 793 */ 794 static int test_cgcore_lesser_ns_open(const char *root) 795 { 796 static char stack[65536]; 797 int ret = KSFT_FAIL; 798 char *cg_test_a = NULL, *cg_test_b = NULL; 799 char *cg_test_b_procs = NULL; 800 int cg_test_b_procs_fd = -1; 801 struct lesser_ns_open_thread_arg targ = { .fd = -1 }; 802 pid_t pid; 803 int status; 804 805 if (!nsdelegate) 806 return KSFT_SKIP; 807 808 cg_test_a = cg_name(root, "cg_test_a"); 809 cg_test_b = cg_name(root, "cg_test_b"); 810 811 if (!cg_test_a || !cg_test_b) 812 goto cleanup; 813 814 cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs"); 815 816 if (!cg_test_b_procs) 817 goto cleanup; 818 819 if (cg_create(cg_test_a) || cg_create(cg_test_b)) 820 goto cleanup; 821 822 if (cg_enter_current(cg_test_b)) 823 goto cleanup; 824 825 targ.path = cg_test_b_procs; 826 pid = clone(lesser_ns_open_thread_fn, stack + sizeof(stack), 827 CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD, 828 &targ); 829 if (pid < 0) 830 goto cleanup; 831 832 if (waitpid(pid, &status, 0) < 0) 833 goto cleanup; 834 835 if (!WIFEXITED(status)) 836 goto cleanup; 837 838 cg_test_b_procs_fd = targ.fd; 839 if (cg_test_b_procs_fd < 0) 840 goto cleanup; 841 842 if (cg_enter_current(cg_test_a)) 843 goto cleanup; 844 845 if ((status = write(cg_test_b_procs_fd, "0", 1)) >= 0 || errno != ENOENT) 846 goto cleanup; 847 848 ret = KSFT_PASS; 849 850 cleanup: 851 cg_enter_current(root); 852 if (cg_test_b_procs_fd >= 0) 853 close(cg_test_b_procs_fd); 854 if (cg_test_b) 855 cg_destroy(cg_test_b); 856 if (cg_test_a) 857 cg_destroy(cg_test_a); 858 free(cg_test_b_procs); 859 free(cg_test_b); 860 free(cg_test_a); 861 return ret; 862 } 863 864 static int setup_named_v1_root(char *root, size_t len, const char *name) 865 { 866 char options[PATH_MAX]; 867 int r; 868 869 r = snprintf(root, len, "/mnt/cg_selftest"); 870 if (r < 0) 871 return r; 872 873 r = snprintf(options, sizeof(options), "none,name=%s", name); 874 if (r < 0) 875 return r; 876 877 r = mkdir(root, 0755); 878 if (r < 0 && errno != EEXIST) 879 return r; 880 881 r = mount("none", root, "cgroup", 0, options); 882 if (r < 0) 883 return r; 884 885 return 0; 886 } 887 888 static void cleanup_named_v1_root(char *root) 889 { 890 if (!cg_test_v1_named) 891 return; 892 umount(root); 893 rmdir(root); 894 } 895 896 #define T(x) { x, #x } 897 struct corecg_test { 898 int (*fn)(const char *root); 899 const char *name; 900 } tests[] = { 901 T(test_cgcore_internal_process_constraint), 902 T(test_cgcore_top_down_constraint_enable), 903 T(test_cgcore_top_down_constraint_disable), 904 T(test_cgcore_no_internal_process_constraint_on_threads), 905 T(test_cgcore_parent_becomes_threaded), 906 T(test_cgcore_invalid_domain), 907 T(test_cgcore_populated), 908 T(test_cgcore_proc_migration), 909 T(test_cgcore_thread_migration), 910 T(test_cgcore_destroy), 911 T(test_cgcore_lesser_euid_open), 912 T(test_cgcore_lesser_ns_open), 913 }; 914 #undef T 915 916 int main(int argc, char *argv[]) 917 { 918 char root[PATH_MAX]; 919 int i; 920 921 ksft_print_header(); 922 if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) { 923 if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME)) 924 ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n"); 925 cg_test_v1_named = true; 926 goto post_v2_setup; 927 } 928 929 if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 930 if (cg_write(root, "cgroup.subtree_control", "+memory")) 931 ksft_exit_skip("Failed to set memory controller\n"); 932 933 post_v2_setup: 934 ksft_set_plan(ARRAY_SIZE(tests)); 935 for (i = 0; i < ARRAY_SIZE(tests); i++) { 936 switch (tests[i].fn(root)) { 937 case KSFT_PASS: 938 ksft_test_result_pass("%s\n", tests[i].name); 939 break; 940 case KSFT_SKIP: 941 ksft_test_result_skip("%s\n", tests[i].name); 942 break; 943 default: 944 ksft_test_result_fail("%s\n", tests[i].name); 945 break; 946 } 947 } 948 949 cleanup_named_v1_root(root); 950 ksft_finished(); 951 } 952