1 /* SPDX-License-Identifier: GPL-2.0 */ 2 3 #include <linux/limits.h> 4 #include <sys/types.h> 5 #include <sys/mman.h> 6 #include <sys/wait.h> 7 #include <unistd.h> 8 #include <fcntl.h> 9 #include <stdio.h> 10 #include <errno.h> 11 #include <signal.h> 12 #include <string.h> 13 #include <pthread.h> 14 15 #include "../kselftest.h" 16 #include "cgroup_util.h" 17 18 static int touch_anon(char *buf, size_t size) 19 { 20 int fd; 21 char *pos = buf; 22 23 fd = open("/dev/urandom", O_RDONLY); 24 if (fd < 0) 25 return -1; 26 27 while (size > 0) { 28 ssize_t ret = read(fd, pos, size); 29 30 if (ret < 0) { 31 if (errno != EINTR) { 32 close(fd); 33 return -1; 34 } 35 } else { 36 pos += ret; 37 size -= ret; 38 } 39 } 40 close(fd); 41 42 return 0; 43 } 44 45 static int alloc_and_touch_anon_noexit(const char *cgroup, void *arg) 46 { 47 int ppid = getppid(); 48 size_t size = (size_t)arg; 49 void *buf; 50 51 buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 52 0, 0); 53 if (buf == MAP_FAILED) 54 return -1; 55 56 if (touch_anon((char *)buf, size)) { 57 munmap(buf, size); 58 return -1; 59 } 60 61 while (getppid() == ppid) 62 sleep(1); 63 64 munmap(buf, size); 65 return 0; 66 } 67 68 /* 69 * Create a child process that allocates and touches 100MB, then waits to be 70 * killed. Wait until the child is attached to the cgroup, kill all processes 71 * in that cgroup and wait until "cgroup.procs" is empty. At this point try to 72 * destroy the empty cgroup. The test helps detect race conditions between 73 * dying processes leaving the cgroup and cgroup destruction path. 74 */ 75 static int test_cgcore_destroy(const char *root) 76 { 77 int ret = KSFT_FAIL; 78 char *cg_test = NULL; 79 int child_pid; 80 char buf[PAGE_SIZE]; 81 82 cg_test = cg_name(root, "cg_test"); 83 84 if (!cg_test) 85 goto cleanup; 86 87 for (int i = 0; i < 10; i++) { 88 if (cg_create(cg_test)) 89 goto cleanup; 90 91 child_pid = cg_run_nowait(cg_test, alloc_and_touch_anon_noexit, 92 (void *) MB(100)); 93 94 if (child_pid < 0) 95 goto cleanup; 96 97 /* wait for the child to enter cgroup */ 98 if (cg_wait_for_proc_count(cg_test, 1)) 99 goto cleanup; 100 101 if (cg_killall(cg_test)) 102 goto cleanup; 103 104 /* wait for cgroup to be empty */ 105 while (1) { 106 if (cg_read(cg_test, "cgroup.procs", buf, sizeof(buf))) 107 goto cleanup; 108 if (buf[0] == '\0') 109 break; 110 usleep(1000); 111 } 112 113 if (rmdir(cg_test)) 114 goto cleanup; 115 116 if (waitpid(child_pid, NULL, 0) < 0) 117 goto cleanup; 118 } 119 ret = KSFT_PASS; 120 cleanup: 121 if (cg_test) 122 cg_destroy(cg_test); 123 free(cg_test); 124 return ret; 125 } 126 127 /* 128 * A(0) - B(0) - C(1) 129 * \ D(0) 130 * 131 * A, B and C's "populated" fields would be 1 while D's 0. 132 * test that after the one process in C is moved to root, 133 * A,B and C's "populated" fields would flip to "0" and file 134 * modified events will be generated on the 135 * "cgroup.events" files of both cgroups. 136 */ 137 static int test_cgcore_populated(const char *root) 138 { 139 int ret = KSFT_FAIL; 140 char *cg_test_a = NULL, *cg_test_b = NULL; 141 char *cg_test_c = NULL, *cg_test_d = NULL; 142 143 cg_test_a = cg_name(root, "cg_test_a"); 144 cg_test_b = cg_name(root, "cg_test_a/cg_test_b"); 145 cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c"); 146 cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d"); 147 148 if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d) 149 goto cleanup; 150 151 if (cg_create(cg_test_a)) 152 goto cleanup; 153 154 if (cg_create(cg_test_b)) 155 goto cleanup; 156 157 if (cg_create(cg_test_c)) 158 goto cleanup; 159 160 if (cg_create(cg_test_d)) 161 goto cleanup; 162 163 if (cg_enter_current(cg_test_c)) 164 goto cleanup; 165 166 if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n")) 167 goto cleanup; 168 169 if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n")) 170 goto cleanup; 171 172 if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n")) 173 goto cleanup; 174 175 if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) 176 goto cleanup; 177 178 if (cg_enter_current(root)) 179 goto cleanup; 180 181 if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n")) 182 goto cleanup; 183 184 if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n")) 185 goto cleanup; 186 187 if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n")) 188 goto cleanup; 189 190 if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) 191 goto cleanup; 192 193 ret = KSFT_PASS; 194 195 cleanup: 196 if (cg_test_d) 197 cg_destroy(cg_test_d); 198 if (cg_test_c) 199 cg_destroy(cg_test_c); 200 if (cg_test_b) 201 cg_destroy(cg_test_b); 202 if (cg_test_a) 203 cg_destroy(cg_test_a); 204 free(cg_test_d); 205 free(cg_test_c); 206 free(cg_test_b); 207 free(cg_test_a); 208 return ret; 209 } 210 211 /* 212 * A (domain threaded) - B (threaded) - C (domain) 213 * 214 * test that C can't be used until it is turned into a 215 * threaded cgroup. "cgroup.type" file will report "domain (invalid)" in 216 * these cases. Operations which fail due to invalid topology use 217 * EOPNOTSUPP as the errno. 218 */ 219 static int test_cgcore_invalid_domain(const char *root) 220 { 221 int ret = KSFT_FAIL; 222 char *grandparent = NULL, *parent = NULL, *child = NULL; 223 224 grandparent = cg_name(root, "cg_test_grandparent"); 225 parent = cg_name(root, "cg_test_grandparent/cg_test_parent"); 226 child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child"); 227 if (!parent || !child || !grandparent) 228 goto cleanup; 229 230 if (cg_create(grandparent)) 231 goto cleanup; 232 233 if (cg_create(parent)) 234 goto cleanup; 235 236 if (cg_create(child)) 237 goto cleanup; 238 239 if (cg_write(parent, "cgroup.type", "threaded")) 240 goto cleanup; 241 242 if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n")) 243 goto cleanup; 244 245 if (!cg_enter_current(child)) 246 goto cleanup; 247 248 if (errno != EOPNOTSUPP) 249 goto cleanup; 250 251 ret = KSFT_PASS; 252 253 cleanup: 254 cg_enter_current(root); 255 if (child) 256 cg_destroy(child); 257 if (parent) 258 cg_destroy(parent); 259 if (grandparent) 260 cg_destroy(grandparent); 261 free(child); 262 free(parent); 263 free(grandparent); 264 return ret; 265 } 266 267 /* 268 * Test that when a child becomes threaded 269 * the parent type becomes domain threaded. 270 */ 271 static int test_cgcore_parent_becomes_threaded(const char *root) 272 { 273 int ret = KSFT_FAIL; 274 char *parent = NULL, *child = NULL; 275 276 parent = cg_name(root, "cg_test_parent"); 277 child = cg_name(root, "cg_test_parent/cg_test_child"); 278 if (!parent || !child) 279 goto cleanup; 280 281 if (cg_create(parent)) 282 goto cleanup; 283 284 if (cg_create(child)) 285 goto cleanup; 286 287 if (cg_write(child, "cgroup.type", "threaded")) 288 goto cleanup; 289 290 if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n")) 291 goto cleanup; 292 293 ret = KSFT_PASS; 294 295 cleanup: 296 if (child) 297 cg_destroy(child); 298 if (parent) 299 cg_destroy(parent); 300 free(child); 301 free(parent); 302 return ret; 303 304 } 305 306 /* 307 * Test that there's no internal process constrain on threaded cgroups. 308 * You can add threads/processes on a parent with a controller enabled. 309 */ 310 static int test_cgcore_no_internal_process_constraint_on_threads(const char *root) 311 { 312 int ret = KSFT_FAIL; 313 char *parent = NULL, *child = NULL; 314 315 if (cg_read_strstr(root, "cgroup.controllers", "cpu") || 316 cg_write(root, "cgroup.subtree_control", "+cpu")) { 317 ret = KSFT_SKIP; 318 goto cleanup; 319 } 320 321 parent = cg_name(root, "cg_test_parent"); 322 child = cg_name(root, "cg_test_parent/cg_test_child"); 323 if (!parent || !child) 324 goto cleanup; 325 326 if (cg_create(parent)) 327 goto cleanup; 328 329 if (cg_create(child)) 330 goto cleanup; 331 332 if (cg_write(parent, "cgroup.type", "threaded")) 333 goto cleanup; 334 335 if (cg_write(child, "cgroup.type", "threaded")) 336 goto cleanup; 337 338 if (cg_write(parent, "cgroup.subtree_control", "+cpu")) 339 goto cleanup; 340 341 if (cg_enter_current(parent)) 342 goto cleanup; 343 344 ret = KSFT_PASS; 345 346 cleanup: 347 cg_enter_current(root); 348 cg_enter_current(root); 349 if (child) 350 cg_destroy(child); 351 if (parent) 352 cg_destroy(parent); 353 free(child); 354 free(parent); 355 return ret; 356 } 357 358 /* 359 * Test that you can't enable a controller on a child if it's not enabled 360 * on the parent. 361 */ 362 static int test_cgcore_top_down_constraint_enable(const char *root) 363 { 364 int ret = KSFT_FAIL; 365 char *parent = NULL, *child = NULL; 366 367 parent = cg_name(root, "cg_test_parent"); 368 child = cg_name(root, "cg_test_parent/cg_test_child"); 369 if (!parent || !child) 370 goto cleanup; 371 372 if (cg_create(parent)) 373 goto cleanup; 374 375 if (cg_create(child)) 376 goto cleanup; 377 378 if (!cg_write(child, "cgroup.subtree_control", "+memory")) 379 goto cleanup; 380 381 ret = KSFT_PASS; 382 383 cleanup: 384 if (child) 385 cg_destroy(child); 386 if (parent) 387 cg_destroy(parent); 388 free(child); 389 free(parent); 390 return ret; 391 } 392 393 /* 394 * Test that you can't disable a controller on a parent 395 * if it's enabled in a child. 396 */ 397 static int test_cgcore_top_down_constraint_disable(const char *root) 398 { 399 int ret = KSFT_FAIL; 400 char *parent = NULL, *child = NULL; 401 402 parent = cg_name(root, "cg_test_parent"); 403 child = cg_name(root, "cg_test_parent/cg_test_child"); 404 if (!parent || !child) 405 goto cleanup; 406 407 if (cg_create(parent)) 408 goto cleanup; 409 410 if (cg_create(child)) 411 goto cleanup; 412 413 if (cg_write(parent, "cgroup.subtree_control", "+memory")) 414 goto cleanup; 415 416 if (cg_write(child, "cgroup.subtree_control", "+memory")) 417 goto cleanup; 418 419 if (!cg_write(parent, "cgroup.subtree_control", "-memory")) 420 goto cleanup; 421 422 ret = KSFT_PASS; 423 424 cleanup: 425 if (child) 426 cg_destroy(child); 427 if (parent) 428 cg_destroy(parent); 429 free(child); 430 free(parent); 431 return ret; 432 } 433 434 /* 435 * Test internal process constraint. 436 * You can't add a pid to a domain parent if a controller is enabled. 437 */ 438 static int test_cgcore_internal_process_constraint(const char *root) 439 { 440 int ret = KSFT_FAIL; 441 char *parent = NULL, *child = NULL; 442 443 parent = cg_name(root, "cg_test_parent"); 444 child = cg_name(root, "cg_test_parent/cg_test_child"); 445 if (!parent || !child) 446 goto cleanup; 447 448 if (cg_create(parent)) 449 goto cleanup; 450 451 if (cg_create(child)) 452 goto cleanup; 453 454 if (cg_write(parent, "cgroup.subtree_control", "+memory")) 455 goto cleanup; 456 457 if (!cg_enter_current(parent)) 458 goto cleanup; 459 460 ret = KSFT_PASS; 461 462 cleanup: 463 if (child) 464 cg_destroy(child); 465 if (parent) 466 cg_destroy(parent); 467 free(child); 468 free(parent); 469 return ret; 470 } 471 472 static void *dummy_thread_fn(void *arg) 473 { 474 return (void *)(size_t)pause(); 475 } 476 477 /* 478 * Test threadgroup migration. 479 * All threads of a process are migrated together. 480 */ 481 static int test_cgcore_proc_migration(const char *root) 482 { 483 int ret = KSFT_FAIL; 484 int t, c_threads = 0, n_threads = 13; 485 char *src = NULL, *dst = NULL; 486 pthread_t threads[n_threads]; 487 488 src = cg_name(root, "cg_src"); 489 dst = cg_name(root, "cg_dst"); 490 if (!src || !dst) 491 goto cleanup; 492 493 if (cg_create(src)) 494 goto cleanup; 495 if (cg_create(dst)) 496 goto cleanup; 497 498 if (cg_enter_current(src)) 499 goto cleanup; 500 501 for (c_threads = 0; c_threads < n_threads; ++c_threads) { 502 if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL)) 503 goto cleanup; 504 } 505 506 cg_enter_current(dst); 507 if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1) 508 goto cleanup; 509 510 ret = KSFT_PASS; 511 512 cleanup: 513 for (t = 0; t < c_threads; ++t) { 514 pthread_cancel(threads[t]); 515 } 516 517 for (t = 0; t < c_threads; ++t) { 518 pthread_join(threads[t], NULL); 519 } 520 521 cg_enter_current(root); 522 523 if (dst) 524 cg_destroy(dst); 525 if (src) 526 cg_destroy(src); 527 free(dst); 528 free(src); 529 return ret; 530 } 531 532 static void *migrating_thread_fn(void *arg) 533 { 534 int g, i, n_iterations = 1000; 535 char **grps = arg; 536 char lines[3][PATH_MAX]; 537 538 for (g = 1; g < 3; ++g) 539 snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0])); 540 541 for (i = 0; i < n_iterations; ++i) { 542 cg_enter_current_thread(grps[(i % 2) + 1]); 543 544 if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1])) 545 return (void *)-1; 546 } 547 return NULL; 548 } 549 550 /* 551 * Test single thread migration. 552 * Threaded cgroups allow successful migration of a thread. 553 */ 554 static int test_cgcore_thread_migration(const char *root) 555 { 556 int ret = KSFT_FAIL; 557 char *dom = NULL; 558 char line[PATH_MAX]; 559 char *grps[3] = { (char *)root, NULL, NULL }; 560 pthread_t thr; 561 void *retval; 562 563 dom = cg_name(root, "cg_dom"); 564 grps[1] = cg_name(root, "cg_dom/cg_src"); 565 grps[2] = cg_name(root, "cg_dom/cg_dst"); 566 if (!grps[1] || !grps[2] || !dom) 567 goto cleanup; 568 569 if (cg_create(dom)) 570 goto cleanup; 571 if (cg_create(grps[1])) 572 goto cleanup; 573 if (cg_create(grps[2])) 574 goto cleanup; 575 576 if (cg_write(grps[1], "cgroup.type", "threaded")) 577 goto cleanup; 578 if (cg_write(grps[2], "cgroup.type", "threaded")) 579 goto cleanup; 580 581 if (cg_enter_current(grps[1])) 582 goto cleanup; 583 584 if (pthread_create(&thr, NULL, migrating_thread_fn, grps)) 585 goto cleanup; 586 587 if (pthread_join(thr, &retval)) 588 goto cleanup; 589 590 if (retval) 591 goto cleanup; 592 593 snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0])); 594 if (proc_read_strstr(0, 1, "cgroup", line)) 595 goto cleanup; 596 597 ret = KSFT_PASS; 598 599 cleanup: 600 cg_enter_current(root); 601 if (grps[2]) 602 cg_destroy(grps[2]); 603 if (grps[1]) 604 cg_destroy(grps[1]); 605 if (dom) 606 cg_destroy(dom); 607 free(grps[2]); 608 free(grps[1]); 609 free(dom); 610 return ret; 611 } 612 613 #define T(x) { x, #x } 614 struct corecg_test { 615 int (*fn)(const char *root); 616 const char *name; 617 } tests[] = { 618 T(test_cgcore_internal_process_constraint), 619 T(test_cgcore_top_down_constraint_enable), 620 T(test_cgcore_top_down_constraint_disable), 621 T(test_cgcore_no_internal_process_constraint_on_threads), 622 T(test_cgcore_parent_becomes_threaded), 623 T(test_cgcore_invalid_domain), 624 T(test_cgcore_populated), 625 T(test_cgcore_proc_migration), 626 T(test_cgcore_thread_migration), 627 T(test_cgcore_destroy), 628 }; 629 #undef T 630 631 int main(int argc, char *argv[]) 632 { 633 char root[PATH_MAX]; 634 int i, ret = EXIT_SUCCESS; 635 636 if (cg_find_unified_root(root, sizeof(root))) 637 ksft_exit_skip("cgroup v2 isn't mounted\n"); 638 639 if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 640 if (cg_write(root, "cgroup.subtree_control", "+memory")) 641 ksft_exit_skip("Failed to set memory controller\n"); 642 643 for (i = 0; i < ARRAY_SIZE(tests); i++) { 644 switch (tests[i].fn(root)) { 645 case KSFT_PASS: 646 ksft_test_result_pass("%s\n", tests[i].name); 647 break; 648 case KSFT_SKIP: 649 ksft_test_result_skip("%s\n", tests[i].name); 650 break; 651 default: 652 ret = EXIT_FAILURE; 653 ksft_test_result_fail("%s\n", tests[i].name); 654 break; 655 } 656 } 657 658 return ret; 659 } 660