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

selftests/cgroup: fix cpu.max tests

Current cpu.max tests (both the normal one and the nested one) are broken.

They setup cpu.max with 1000 us quota and the default period (100,000 us).
A cpu hog is run for a duration of 1s as per wall clock time. This corresponds
to 10 periods, hence an expected usage of 10,000 us. We want the measured
usage (as per cpu.stat) to be close to 10,000 us.

Previously, this approximate equality test was done by
`!values_close(usage_usec, expected_usage_usec, 95)`: if the absolute
difference between usage_usec and expected_usage_usec is greater than 95% of
their sum, then we pass. And expected_usage_usec was set to 1,000,000 us.
Mathematically, this translates to the following being true for pass:

|usage - expected_usage| > (usage + expected_usage)*0.95

If usage > expected_usage:
usage - expected_usage > (usage + expected_usage)*0.95
0.05*usage > 1.95*expected_usage
usage > 39*expected_usage = 39s

If usage < expected_usage:
expected_usage - usage > (usage + expected_usage)*0.95
0.05*expected_usage > 1.95*usage
usage < 0.0256*expected_usage = 25,600 us

Combined,

Pass if usage < 25,600 us or > 39 s,

which makes no sense given that all we need is for usage_usec to be close to
10,000 us.

Fix this by explicitly calcuating the expected usage duration based on the
configured quota, default period, and the duration, and compare usage_usec
and expected_usage_usec using values_close() with a 10% error margin.

Also, use snprintf to get the quota string to write to cpu.max instead of
hardcoding the quota, ensuring a single source of truth.

Remove the check comparing user_usec and expected_usage_usec, since on running
this test modified with printfs, it's seen that user_usec and usage_usec can
regularly exceed the theoretical expected_usage_usec:

$ sudo ./test_cpu
user: 10485, usage: 10485, expected: 10000
ok 1 test_cpucg_max
user: 11127, usage: 11127, expected: 10000
ok 2 test_cpucg_max_nested
$ sudo ./test_cpu
user: 10286, usage: 10286, expected: 10000
ok 1 test_cpucg_max
user: 10404, usage: 11271, expected: 10000
ok 2 test_cpucg_max_nested

Hence, a values_close() check of usage_usec and expected_usage_usec is
sufficient.

Fixes: a79906570f9646ae17 ("cgroup: Add test_cpucg_max_nested() testcase")
Fixes: 889ab8113ef1386c57 ("cgroup: Add test_cpucg_max() testcase")
Acked-by: Michal Koutný <mkoutny@suse.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

Shashank Balaji and committed by
Tejun Heo
954bacce dfe25fba

+43 -20
+43 -20
tools/testing/selftests/cgroup/test_cpu.c
··· 2 2 3 3 #define _GNU_SOURCE 4 4 #include <linux/limits.h> 5 + #include <sys/param.h> 5 6 #include <sys/sysinfo.h> 6 7 #include <sys/wait.h> 7 8 #include <errno.h> ··· 646 645 static int test_cpucg_max(const char *root) 647 646 { 648 647 int ret = KSFT_FAIL; 649 - long usage_usec, user_usec; 650 - long usage_seconds = 1; 651 - long expected_usage_usec = usage_seconds * USEC_PER_SEC; 648 + long quota_usec = 1000; 649 + long default_period_usec = 100000; /* cpu.max's default period */ 650 + long duration_seconds = 1; 651 + 652 + long duration_usec = duration_seconds * USEC_PER_SEC; 653 + long usage_usec, n_periods, remainder_usec, expected_usage_usec; 652 654 char *cpucg; 655 + char quota_buf[32]; 656 + 657 + snprintf(quota_buf, sizeof(quota_buf), "%ld", quota_usec); 653 658 654 659 cpucg = cg_name(root, "cpucg_test"); 655 660 if (!cpucg) ··· 664 657 if (cg_create(cpucg)) 665 658 goto cleanup; 666 659 667 - if (cg_write(cpucg, "cpu.max", "1000")) 660 + if (cg_write(cpucg, "cpu.max", quota_buf)) 668 661 goto cleanup; 669 662 670 663 struct cpu_hog_func_param param = { 671 664 .nprocs = 1, 672 665 .ts = { 673 - .tv_sec = usage_seconds, 666 + .tv_sec = duration_seconds, 674 667 .tv_nsec = 0, 675 668 }, 676 669 .clock_type = CPU_HOG_CLOCK_WALL, ··· 679 672 goto cleanup; 680 673 681 674 usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec"); 682 - user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec"); 683 - if (user_usec <= 0) 675 + if (usage_usec <= 0) 684 676 goto cleanup; 685 677 686 - if (user_usec >= expected_usage_usec) 687 - goto cleanup; 678 + /* 679 + * The following calculation applies only since 680 + * the cpu hog is set to run as per wall-clock time 681 + */ 682 + n_periods = duration_usec / default_period_usec; 683 + remainder_usec = duration_usec - n_periods * default_period_usec; 684 + expected_usage_usec 685 + = n_periods * quota_usec + MIN(remainder_usec, quota_usec); 688 686 689 - if (values_close(usage_usec, expected_usage_usec, 95)) 687 + if (!values_close(usage_usec, expected_usage_usec, 10)) 690 688 goto cleanup; 691 689 692 690 ret = KSFT_PASS; ··· 710 698 static int test_cpucg_max_nested(const char *root) 711 699 { 712 700 int ret = KSFT_FAIL; 713 - long usage_usec, user_usec; 714 - long usage_seconds = 1; 715 - long expected_usage_usec = usage_seconds * USEC_PER_SEC; 701 + long quota_usec = 1000; 702 + long default_period_usec = 100000; /* cpu.max's default period */ 703 + long duration_seconds = 1; 704 + 705 + long duration_usec = duration_seconds * USEC_PER_SEC; 706 + long usage_usec, n_periods, remainder_usec, expected_usage_usec; 716 707 char *parent, *child; 708 + char quota_buf[32]; 709 + 710 + snprintf(quota_buf, sizeof(quota_buf), "%ld", quota_usec); 717 711 718 712 parent = cg_name(root, "cpucg_parent"); 719 713 child = cg_name(parent, "cpucg_child"); ··· 735 717 if (cg_create(child)) 736 718 goto cleanup; 737 719 738 - if (cg_write(parent, "cpu.max", "1000")) 720 + if (cg_write(parent, "cpu.max", quota_buf)) 739 721 goto cleanup; 740 722 741 723 struct cpu_hog_func_param param = { 742 724 .nprocs = 1, 743 725 .ts = { 744 - .tv_sec = usage_seconds, 726 + .tv_sec = duration_seconds, 745 727 .tv_nsec = 0, 746 728 }, 747 729 .clock_type = CPU_HOG_CLOCK_WALL, ··· 750 732 goto cleanup; 751 733 752 734 usage_usec = cg_read_key_long(child, "cpu.stat", "usage_usec"); 753 - user_usec = cg_read_key_long(child, "cpu.stat", "user_usec"); 754 - if (user_usec <= 0) 735 + if (usage_usec <= 0) 755 736 goto cleanup; 756 737 757 - if (user_usec >= expected_usage_usec) 758 - goto cleanup; 738 + /* 739 + * The following calculation applies only since 740 + * the cpu hog is set to run as per wall-clock time 741 + */ 742 + n_periods = duration_usec / default_period_usec; 743 + remainder_usec = duration_usec - n_periods * default_period_usec; 744 + expected_usage_usec 745 + = n_periods * quota_usec + MIN(remainder_usec, quota_usec); 759 746 760 - if (values_close(usage_usec, expected_usage_usec, 95)) 747 + if (!values_close(usage_usec, expected_usage_usec, 10)) 761 748 goto cleanup; 762 749 763 750 ret = KSFT_PASS;