/* * Copyright (c) 2018 Google, Inc. * * SPDX-License-Identifier: GPL-2.0-or-later * * A test schedboost cgroup is created and a task is put inside it. The * utilization of that task is monitored and verified while changing the boost * of the test schedboost cgroup to different values. */ #define _GNU_SOURCE #include <errno.h> #include <pthread.h> #include <sched.h> #include <semaphore.h> #include <time.h> #include "tst_test.h" #include "tst_safe_file_ops.h" #include "tst_safe_pthread.h" #include "tst_safe_macros.h" #include "trace_parse.h" #include "util.h" #define TRACE_EVENTS "sugov_util_update sched_switch" static sem_t test_sem; static sem_t result_sem; static int test_cpu; static int task_pid; static unsigned int test_index = 0; static int test_boost[] = { 0, 25, 50, 75, 100 }; #define NUM_TESTS (sizeof(test_boost)/sizeof(int)) static int test_utils[NUM_TESTS]; #define STUNE_TEST_PATH "/dev/stune/test" #define STUNE_ROOT_TASKS "/dev/stune/tasks" #define BUSY_USEC 1000 #define SLEEP_USEC 19000 /* Run each test for one second. */ #define TEST_LENGTH_USEC USEC_PER_SEC static void do_work(void) { struct timespec ts; unsigned long long now_usec, end_usec; /* Calculate overall end time. */ if (clock_gettime(CLOCK_MONOTONIC, &ts)) { printf("clock_gettime() reported an error\n"); return; } now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000; end_usec = now_usec + TEST_LENGTH_USEC; while (now_usec < end_usec) { burn(BUSY_USEC, 0); usleep(SLEEP_USEC); if (clock_gettime(CLOCK_MONOTONIC, &ts)) { printf("clock_gettime() reported an error\n"); return; } now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000; } } static void *test_fn(void *arg LTP_ATTRIBUTE_UNUSED) { unsigned int tests_done = 0; affine(test_cpu); task_pid = gettid(); SAFE_FILE_PRINTF(STUNE_TEST_PATH "/tasks", "%d", task_pid); while(tests_done < NUM_TESTS) { sem_wait(&test_sem); do_work(); sem_post(&result_sem); tests_done++; } SAFE_FILE_PRINTF(STUNE_ROOT_TASKS, "%d", task_pid); return NULL; } static int parse_results(void) { int i; int max_util_seen = 0; for (i = 0; i < num_trace_records; i++) if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) { struct trace_sugov_util_update *t = trace[i].event_data; if (t->cpu == test_cpu && t->util > max_util_seen) max_util_seen = t->util; } test_utils[test_index] = max_util_seen; printf("Max util seen for boost %d: %d\n", test_boost[test_index], max_util_seen); return 0; } static void run_test(void) { SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.boost", "%d", test_boost[test_index]); SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n"); SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1"); sem_post(&test_sem); sem_wait(&result_sem); SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); LOAD_TRACE(); parse_results(); test_index++; } #define TEST_MARGIN 50 static void check_results(void) { unsigned int i; for (i = 0; i < NUM_TESTS; i++) { int target_util = test_boost[i] * 10; if (test_utils[i] < target_util - TEST_MARGIN || test_utils[i] > target_util + TEST_MARGIN) tst_res(TFAIL, "Test %i (boost %d) failed with " "util %d (allowed %d - %d).\n", i, test_boost[i], test_utils[i], target_util - TEST_MARGIN, target_util + TEST_MARGIN); } tst_res(TPASS, "Boosted utilizations within expected range."); } static void run(void) { pthread_t test_thread; sem_init(&test_sem, 0, 0); sem_init(&result_sem, 0, 0); test_cpu = tst_ncpus() - 1; printf("Running %ld tests for %d sec\n", NUM_TESTS, TEST_LENGTH_USEC / USEC_PER_SEC); printf("CPU hog will be bound to CPU %d.\n", test_cpu); /* * If this fails due to ENOSPC the definition of BOOSTGROUPS_COUNT in * kernel/sched/tune.c needs to be increased. */ SAFE_MKDIR(STUNE_TEST_PATH, 0777); SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.colocate", "0"); SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.prefer_idle", "0"); SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.sched_boost_enabled", "1"); SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.sched_boost_no_override", "0"); SAFE_PTHREAD_CREATE(&test_thread, NULL, test_fn, NULL); SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384"); SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS); while (test_index < NUM_TESTS) run_test(); SAFE_PTHREAD_JOIN(test_thread, NULL); SAFE_RMDIR(STUNE_TEST_PATH); check_results(); } static struct tst_test test = { .test_all = run, .cleanup = trace_cleanup, };