1 /*
2 * Copyright (c) 2018 Google, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * A test schedboost cgroup is created and a task is put inside it. The
7 * utilization of that task is monitored and verified while changing the boost
8 * of the test schedboost cgroup to different values.
9 */
10
11 #define _GNU_SOURCE
12 #include <errno.h>
13 #include <pthread.h>
14 #include <sched.h>
15 #include <semaphore.h>
16 #include <time.h>
17
18 #include "tst_test.h"
19 #include "tst_safe_file_ops.h"
20 #include "tst_safe_pthread.h"
21 #include "tst_safe_macros.h"
22
23 #include "trace_parse.h"
24 #include "util.h"
25
26 #define TRACE_EVENTS "sugov_util_update sched_switch"
27
28 static sem_t test_sem;
29 static sem_t result_sem;
30 static int test_cpu;
31 static int task_pid;
32 static unsigned int test_index = 0;
33 static int test_boost[] = { 0, 25, 50, 75, 100 };
34 #define NUM_TESTS (sizeof(test_boost)/sizeof(int))
35 static int test_utils[NUM_TESTS];
36 #define STUNE_TEST_PATH "/dev/stune/test"
37 #define STUNE_ROOT_TASKS "/dev/stune/tasks"
38
39 #define BUSY_USEC 1000
40 #define SLEEP_USEC 19000
41 /* Run each test for one second. */
42 #define TEST_LENGTH_USEC USEC_PER_SEC
do_work(void)43 static void do_work(void)
44 {
45 struct timespec ts;
46 unsigned long long now_usec, end_usec;
47
48 /* Calculate overall end time. */
49 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
50 printf("clock_gettime() reported an error\n");
51 return;
52 }
53 now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000;
54 end_usec = now_usec + TEST_LENGTH_USEC;
55
56 while (now_usec < end_usec) {
57 burn(BUSY_USEC, 0);
58 usleep(SLEEP_USEC);
59 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
60 printf("clock_gettime() reported an error\n");
61 return;
62 }
63 now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000;
64 }
65 }
66
test_fn(void * arg LTP_ATTRIBUTE_UNUSED)67 static void *test_fn(void *arg LTP_ATTRIBUTE_UNUSED)
68 {
69 unsigned int tests_done = 0;
70
71 affine(test_cpu);
72
73 task_pid = gettid();
74 SAFE_FILE_PRINTF(STUNE_TEST_PATH "/tasks", "%d", task_pid);
75 while(tests_done < NUM_TESTS) {
76 sem_wait(&test_sem);
77 do_work();
78 sem_post(&result_sem);
79 tests_done++;
80 }
81 SAFE_FILE_PRINTF(STUNE_ROOT_TASKS, "%d", task_pid);
82 return NULL;
83 }
84
parse_results(void)85 static int parse_results(void)
86 {
87 int i;
88 int max_util_seen = 0;
89
90 for (i = 0; i < num_trace_records; i++)
91 if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) {
92 struct trace_sugov_util_update *t =
93 trace[i].event_data;
94 if (t->cpu == test_cpu && t->util > max_util_seen)
95 max_util_seen = t->util;
96 }
97
98 test_utils[test_index] = max_util_seen;
99 printf("Max util seen for boost %d: %d\n", test_boost[test_index],
100 max_util_seen);
101 return 0;
102 }
103
run_test(void)104 static void run_test(void)
105 {
106 SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.boost",
107 "%d", test_boost[test_index]);
108 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
109 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
110 sem_post(&test_sem);
111 sem_wait(&result_sem);
112 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
113 LOAD_TRACE();
114 parse_results();
115 test_index++;
116 }
117
118 #define TEST_MARGIN 50
check_results(void)119 static void check_results(void)
120 {
121 unsigned int i;
122 for (i = 0; i < NUM_TESTS; i++) {
123 int target_util = test_boost[i] * 10;
124 if (test_utils[i] < target_util - TEST_MARGIN ||
125 test_utils[i] > target_util + TEST_MARGIN)
126 tst_res(TFAIL, "Test %i (boost %d) failed with "
127 "util %d (allowed %d - %d).\n",
128 i, test_boost[i], test_utils[i],
129 target_util - TEST_MARGIN,
130 target_util + TEST_MARGIN);
131 }
132 tst_res(TPASS, "Boosted utilizations within expected range.");
133 }
134
run(void)135 static void run(void)
136 {
137 pthread_t test_thread;
138
139 sem_init(&test_sem, 0, 0);
140 sem_init(&result_sem, 0, 0);
141
142 test_cpu = tst_ncpus() - 1;
143 printf("Running %ld tests for %d sec\n", NUM_TESTS,
144 TEST_LENGTH_USEC / USEC_PER_SEC);
145 printf("CPU hog will be bound to CPU %d.\n", test_cpu);
146
147 /*
148 * If this fails due to ENOSPC the definition of BOOSTGROUPS_COUNT in
149 * kernel/sched/tune.c needs to be increased.
150 */
151 SAFE_MKDIR(STUNE_TEST_PATH, 0777);
152 SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.colocate",
153 "0");
154 SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.prefer_idle",
155 "0");
156 SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.sched_boost_enabled",
157 "1");
158 SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.sched_boost_no_override",
159 "0");
160
161 SAFE_PTHREAD_CREATE(&test_thread, NULL, test_fn, NULL);
162
163 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
164 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
165 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
166
167 while (test_index < NUM_TESTS)
168 run_test();
169
170 SAFE_PTHREAD_JOIN(test_thread, NULL);
171 SAFE_RMDIR(STUNE_TEST_PATH);
172 check_results();
173 }
174
175 static struct tst_test test = {
176 .test_all = run,
177 .cleanup = trace_cleanup,
178 };
179