Commit 3c879a1b authored by David Vernet's avatar David Vernet Committed by Tejun Heo

cgroup: Add test_cpucg_stats() testcase to cgroup cpu selftests

test_cpu.c includes testcases that validate the cgroup cpu controller.
This patch adds a new testcase called test_cpucg_stats() that verifies the
expected behavior of the cpu.stat interface. In doing so, we define a
new hog_cpus_timed() function which takes a cpu_hog_func_param struct
that configures how many CPUs it uses, and how long it runs. Future
patches will also spawn threads that hog CPUs, so this function will
eventually serve those use-cases as well.
Signed-off-by: default avatarDavid Vernet <void@manifault.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent 820a4f88
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
#define MB(x) (x << 20) #define MB(x) (x << 20)
#define USEC_PER_SEC 1000000L
#define NSEC_PER_SEC 1000000000L
/* /*
* Checks if two given values differ by less than err% of their sum. * Checks if two given values differ by less than err% of their sum.
*/ */
......
...@@ -2,11 +2,19 @@ ...@@ -2,11 +2,19 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <linux/limits.h> #include <linux/limits.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include "../kselftest.h" #include "../kselftest.h"
#include "cgroup_util.h" #include "cgroup_util.h"
struct cpu_hog_func_param {
int nprocs;
struct timespec ts;
};
/* /*
* This test creates two nested cgroups with and without enabling * This test creates two nested cgroups with and without enabling
* the cpu controller. * the cpu controller.
...@@ -70,12 +78,132 @@ static int test_cpucg_subtree_control(const char *root) ...@@ -70,12 +78,132 @@ static int test_cpucg_subtree_control(const char *root)
return ret; return ret;
} }
static void *hog_cpu_thread_func(void *arg)
{
while (1)
;
return NULL;
}
static struct timespec
timespec_sub(const struct timespec *lhs, const struct timespec *rhs)
{
struct timespec zero = {
.tv_sec = 0,
.tv_nsec = 0,
};
struct timespec ret;
if (lhs->tv_sec < rhs->tv_sec)
return zero;
ret.tv_sec = lhs->tv_sec - rhs->tv_sec;
if (lhs->tv_nsec < rhs->tv_nsec) {
if (ret.tv_sec == 0)
return zero;
ret.tv_sec--;
ret.tv_nsec = NSEC_PER_SEC - rhs->tv_nsec + lhs->tv_nsec;
} else
ret.tv_nsec = lhs->tv_nsec - rhs->tv_nsec;
return ret;
}
static int hog_cpus_timed(const char *cgroup, void *arg)
{
const struct cpu_hog_func_param *param =
(struct cpu_hog_func_param *)arg;
struct timespec ts_run = param->ts;
struct timespec ts_remaining = ts_run;
int i, ret;
for (i = 0; i < param->nprocs; i++) {
pthread_t tid;
ret = pthread_create(&tid, NULL, &hog_cpu_thread_func, NULL);
if (ret != 0)
return ret;
}
while (ts_remaining.tv_sec > 0 || ts_remaining.tv_nsec > 0) {
struct timespec ts_total;
ret = nanosleep(&ts_remaining, NULL);
if (ret && errno != EINTR)
return ret;
ret = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts_total);
if (ret != 0)
return ret;
ts_remaining = timespec_sub(&ts_run, &ts_total);
}
return 0;
}
/*
* Creates a cpu cgroup, burns a CPU for a few quanta, and verifies that
* cpu.stat shows the expected output.
*/
static int test_cpucg_stats(const char *root)
{
int ret = KSFT_FAIL;
long usage_usec, user_usec, system_usec;
long usage_seconds = 2;
long expected_usage_usec = usage_seconds * USEC_PER_SEC;
char *cpucg;
cpucg = cg_name(root, "cpucg_test");
if (!cpucg)
goto cleanup;
if (cg_create(cpucg))
goto cleanup;
usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec");
user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec");
system_usec = cg_read_key_long(cpucg, "cpu.stat", "system_usec");
if (usage_usec != 0 || user_usec != 0 || system_usec != 0)
goto cleanup;
struct cpu_hog_func_param param = {
.nprocs = 1,
.ts = {
.tv_sec = usage_seconds,
.tv_nsec = 0,
},
};
if (cg_run(cpucg, hog_cpus_timed, (void *)&param))
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)
goto cleanup;
if (!values_close(usage_usec, expected_usage_usec, 1))
goto cleanup;
ret = KSFT_PASS;
cleanup:
cg_destroy(cpucg);
free(cpucg);
return ret;
}
#define T(x) { x, #x } #define T(x) { x, #x }
struct cpucg_test { struct cpucg_test {
int (*fn)(const char *root); int (*fn)(const char *root);
const char *name; const char *name;
} tests[] = { } tests[] = {
T(test_cpucg_subtree_control), T(test_cpucg_subtree_control),
T(test_cpucg_stats),
}; };
#undef T #undef T
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment