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

cgroup: Adding test_cpucg_nested_weight_overprovisioned() testcase

The cgroup cpu controller tests in
tools/testing/selftests/cgroup/test_cpu.c have some testcases that validate
the expected behavior of setting cpu.weight on cgroups, and then hogging
CPUs. What is still missing from the suite is a testcase that validates
nested cgroups. This patch adds test_cpucg_nested_weight_overprovisioned(),
which validates that a parent's cpu.weight will override its children if
they overcommit a host, and properly protect any sibling groups of that
parent.

Signed-off-by: David Vernet <void@manifault.com>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

David Vernet and committed by
Tejun Heo
b76ee4f5 4ab93063

+122
+122
tools/testing/selftests/cgroup/test_cpu.c
··· 403 403 underprovision_validate); 404 404 } 405 405 406 + /* 407 + * First, this test creates the following hierarchy: 408 + * A 409 + * A/B cpu.weight = 1000 410 + * A/C cpu.weight = 1000 411 + * A/C/D cpu.weight = 5000 412 + * A/C/E cpu.weight = 5000 413 + * 414 + * A separate process is then created for each leaf, which spawn nproc threads 415 + * that burn a CPU for a few seconds. 416 + * 417 + * Once all of those processes have exited, we verify that each of the leaf 418 + * cgroups have roughly the same usage from cpu.stat. 419 + */ 420 + static int 421 + test_cpucg_nested_weight_overprovisioned(const char *root) 422 + { 423 + int ret = KSFT_FAIL, i; 424 + char *parent = NULL, *child = NULL; 425 + struct cpu_hogger leaf[3] = {NULL}; 426 + long nested_leaf_usage, child_usage; 427 + int nprocs = get_nprocs(); 428 + 429 + parent = cg_name(root, "cpucg_test"); 430 + child = cg_name(parent, "cpucg_child"); 431 + if (!parent || !child) 432 + goto cleanup; 433 + 434 + if (cg_create(parent)) 435 + goto cleanup; 436 + if (cg_write(parent, "cgroup.subtree_control", "+cpu")) 437 + goto cleanup; 438 + 439 + if (cg_create(child)) 440 + goto cleanup; 441 + if (cg_write(child, "cgroup.subtree_control", "+cpu")) 442 + goto cleanup; 443 + if (cg_write(child, "cpu.weight", "1000")) 444 + goto cleanup; 445 + 446 + for (i = 0; i < ARRAY_SIZE(leaf); i++) { 447 + const char *ancestor; 448 + long weight; 449 + 450 + if (i == 0) { 451 + ancestor = parent; 452 + weight = 1000; 453 + } else { 454 + ancestor = child; 455 + weight = 5000; 456 + } 457 + leaf[i].cgroup = cg_name_indexed(ancestor, "cpucg_leaf", i); 458 + if (!leaf[i].cgroup) 459 + goto cleanup; 460 + 461 + if (cg_create(leaf[i].cgroup)) 462 + goto cleanup; 463 + 464 + if (cg_write_numeric(leaf[i].cgroup, "cpu.weight", weight)) 465 + goto cleanup; 466 + } 467 + 468 + for (i = 0; i < ARRAY_SIZE(leaf); i++) { 469 + pid_t pid; 470 + struct cpu_hog_func_param param = { 471 + .nprocs = nprocs, 472 + .ts = { 473 + .tv_sec = 10, 474 + .tv_nsec = 0, 475 + }, 476 + .clock_type = CPU_HOG_CLOCK_WALL, 477 + }; 478 + 479 + pid = cg_run_nowait(leaf[i].cgroup, hog_cpus_timed, 480 + (void *)&param); 481 + if (pid <= 0) 482 + goto cleanup; 483 + leaf[i].pid = pid; 484 + } 485 + 486 + for (i = 0; i < ARRAY_SIZE(leaf); i++) { 487 + int retcode; 488 + 489 + waitpid(leaf[i].pid, &retcode, 0); 490 + if (!WIFEXITED(retcode)) 491 + goto cleanup; 492 + if (WEXITSTATUS(retcode)) 493 + goto cleanup; 494 + } 495 + 496 + for (i = 0; i < ARRAY_SIZE(leaf); i++) { 497 + leaf[i].usage = cg_read_key_long(leaf[i].cgroup, 498 + "cpu.stat", "usage_usec"); 499 + if (leaf[i].usage <= 0) 500 + goto cleanup; 501 + } 502 + 503 + nested_leaf_usage = leaf[1].usage + leaf[2].usage; 504 + if (!values_close(leaf[0].usage, nested_leaf_usage, 15)) 505 + goto cleanup; 506 + 507 + child_usage = cg_read_key_long(child, "cpu.stat", "usage_usec"); 508 + if (child_usage <= 0) 509 + goto cleanup; 510 + if (!values_close(child_usage, nested_leaf_usage, 1)) 511 + goto cleanup; 512 + 513 + ret = KSFT_PASS; 514 + cleanup: 515 + for (i = 0; i < ARRAY_SIZE(leaf); i++) { 516 + cg_destroy(leaf[i].cgroup); 517 + free(leaf[i].cgroup); 518 + } 519 + cg_destroy(child); 520 + free(child); 521 + cg_destroy(parent); 522 + free(parent); 523 + 524 + return ret; 525 + } 526 + 406 527 #define T(x) { x, #x } 407 528 struct cpucg_test { 408 529 int (*fn)(const char *root); ··· 533 412 T(test_cpucg_stats), 534 413 T(test_cpucg_weight_overprovisioned), 535 414 T(test_cpucg_weight_underprovisioned), 415 + T(test_cpucg_nested_weight_overprovisioned), 536 416 }; 537 417 #undef T 538 418