The cpu.max test (both the normal one and the nested one) setup cpu.max with 1000 us runtime 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. Enforce this bound correctly.
Previously, this approximate equality test was done by `!values_close(usage_usec, duration_usec, 95)`: if the absolute difference between usage_usec and duration_usec is greater than 95% of their sum, then we pass. This is problematic for two reasons:
1. Semantics: When one sees `values_close` they expect the error percentage to be some small number, not 95. The intent behind using `values_close` is lost by using a high error percent such as 95. The intent it's actually going for is "values far".
2. Bound too wide: The condition translates to the following expression:
|usage_usec - duration_usec| > (usage_usec + duration_usec)*0.95
0.05*duration_usec > 1.95*usage_usec (usage < duration)
usage_usec < 0.0257*duration_usec = 25,641 us
So, this condition passes as long as usage_usec is lower than 25,641 us, while all we want is for it to be close to 10,000 us.
To address these issues, the condition is changed to `labs(usage_usec - expected_usage_usec) < 2000` meaning pass. Now the meaning is much clearer. `labs` is used instead of `values_close` because we don't expect the error in usage_usec compared to expected_usage_usec to scale with either of the terms. The error is because of the cpu hog process running for slightly longer than the duration. So, using a proportional error estimate, such as `values_close`, does not make sense. The maximum tolerable error is set to 2000 us because on running this test 10 times, the maximum `usage_usec` observed was 11,513 us, which corresponds to an error of 1513 us.
user_usec is removed because it will always be less than usage_usec. usage_usec is what really represents the throttling.
Signed-off-by: Shashank Balaji shashank.mahadasyam@sony.com --- tools/testing/selftests/cgroup/test_cpu.c | 34 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_cpu.c b/tools/testing/selftests/cgroup/test_cpu.c index 26b0df338505526cc0c5de8f4179b8ec9bad43d7..fcef90d2948e1344b7741214a0cdd10609069624 100644 --- a/tools/testing/selftests/cgroup/test_cpu.c +++ b/tools/testing/selftests/cgroup/test_cpu.c @@ -645,9 +645,8 @@ test_cpucg_nested_weight_underprovisioned(const char *root) static int test_cpucg_max(const char *root) { int ret = KSFT_FAIL; - long usage_usec, user_usec; + long usage_usec, expected_usage_usec; long duration_seconds = 1; - long duration_usec = duration_seconds * USEC_PER_SEC; char *cpucg;
cpucg = cg_name(root, "cpucg_test"); @@ -672,14 +671,18 @@ static int test_cpucg_max(const char *root) goto cleanup;
usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec"); - user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec"); - if (user_usec <= 0) + if (usage_usec <= 0) goto cleanup;
- if (user_usec >= duration_usec) - goto cleanup; + /* + * Since the cpu hog is set to run as per wall clock time, it's expected to + * run for 10 periods (duration_usec/default_period_usec), and in each + * period, it's throttled to run for 1000 usec. So its expected usage is + * 1000 * 10 = 10000 usec. + */ + expected_usage_usec = 10000;
- if (values_close(usage_usec, duration_usec, 95)) + if (labs(usage_usec - expected_usage_usec) > 2000) goto cleanup;
ret = KSFT_PASS; @@ -698,9 +701,8 @@ static int test_cpucg_max(const char *root) static int test_cpucg_max_nested(const char *root) { int ret = KSFT_FAIL; - long usage_usec, user_usec; + long usage_usec, expected_usage_usec; long duration_seconds = 1; - long duration_usec = duration_seconds * USEC_PER_SEC; char *parent, *child;
parent = cg_name(root, "cpucg_parent"); @@ -732,14 +734,18 @@ static int test_cpucg_max_nested(const char *root) goto cleanup;
usage_usec = cg_read_key_long(child, "cpu.stat", "usage_usec"); - user_usec = cg_read_key_long(child, "cpu.stat", "user_usec"); - if (user_usec <= 0) + if (usage_usec <= 0) goto cleanup;
- if (user_usec >= duration_usec) - goto cleanup; + /* + * Since the cpu hog is set to run as per wall clock time, it's expected to + * run for 10 periods (duration_usec/default_period_usec), and in each + * period, it's throttled to run for 1000 usec. So its expected usage is + * 1000 * 10 = 10000 usec. + */ + expected_usage_usec = 10000;
- if (values_close(usage_usec, duration_usec, 95)) + if (labs(usage_usec - expected_usage_usec) > 2000) goto cleanup;
ret = KSFT_PASS;