• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
43 
do_work(void)44 static void do_work(void)
45 {
46 	struct timespec ts;
47 	unsigned long long now_usec, end_usec;
48 
49 	/* Calculate overall end time. */
50 	if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
51 		printf("clock_gettime() reported an error\n");
52 		return;
53 	}
54 	now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000;
55 	end_usec = now_usec + TEST_LENGTH_USEC/2;
56 
57 	while (now_usec < end_usec) {
58 		burn(BUSY_USEC, 0);
59 		usleep(SLEEP_USEC);
60 		if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
61 			printf("clock_gettime() reported an error\n");
62 			return;
63 		}
64 		now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000;
65 	}
66 }
67 
test_fn(void * arg LTP_ATTRIBUTE_UNUSED)68 static void *test_fn(void *arg LTP_ATTRIBUTE_UNUSED)
69 {
70 	unsigned int tests_done = 0;
71 
72 	affine(test_cpu);
73 
74 	task_pid = gettid();
75 	SAFE_FILE_PRINTF(STUNE_TEST_PATH "/tasks", "%d", task_pid);
76 	while(tests_done < NUM_TESTS) {
77 		sem_wait(&test_sem);
78 		// give time for utilization to track real task usage
79 		do_work();
80 		// start measuring
81 		tracefs_write("tracing_on", "1");
82 		do_work();
83 		sem_post(&result_sem);
84 		tests_done++;
85 	}
86 	SAFE_FILE_PRINTF(STUNE_ROOT_TASKS, "%d", task_pid);
87 	return NULL;
88 }
89 
parse_results(void)90 static int parse_results(void)
91 {
92 	int i;
93 	int max_util_seen = 0;
94 
95 	for (i = 0; i < num_trace_records; i++)
96 		if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) {
97 			struct trace_sugov_util_update *t =
98 				trace[i].event_data;
99 			if (t->cpu == test_cpu && t->util > max_util_seen)
100 				max_util_seen = t->util;
101 		}
102 
103 	test_utils[test_index] = max_util_seen;
104 	printf("Max util seen for boost %d: %d\n", test_boost[test_index],
105 	       max_util_seen);
106 	return 0;
107 }
108 
run_test(void)109 static void run_test(void)
110 {
111 	SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.boost",
112 			 "%d", test_boost[test_index]);
113 	tracefs_write("trace", "\n");
114 	sem_post(&test_sem);
115 	sem_wait(&result_sem);
116 	tracefs_write("tracing_on", "0");
117 	LOAD_TRACE();
118 	parse_results();
119 	test_index++;
120 }
121 
122 #define TEST_MARGIN 50
check_results(void)123 static void check_results(void)
124 {
125 	unsigned int i;
126 	for (i = 0; i < NUM_TESTS; i++) {
127 		int target_util = test_boost[i] * 10;
128 		if (test_utils[i] < target_util - TEST_MARGIN ||
129 		    test_utils[i] > target_util + TEST_MARGIN)
130 			tst_res(TFAIL, "Test %i (boost %d) failed with "
131 				"util %d (allowed %d - %d).\n",
132 				i, test_boost[i], test_utils[i],
133 				target_util - TEST_MARGIN,
134 				target_util + TEST_MARGIN);
135 		else
136 			tst_res(TPASS, "Test %i (boost %d) passed with "
137 				"util %d (allowed %d - %d).\n",
138 				i, test_boost[i], test_utils[i],
139 				target_util - TEST_MARGIN,
140 				target_util + TEST_MARGIN);
141 	}
142 }
143 
run(void)144 static void run(void)
145 {
146 	pthread_t test_thread;
147 
148 	sem_init(&test_sem, 0, 0);
149 	sem_init(&result_sem, 0, 0);
150 
151 	if (access("/dev/stune", R_OK))
152 		tst_brk(TCONF, "schedtune not detected");
153 
154 	test_cpu = tst_ncpus() - 1;
155 	printf("Running %ld tests for %d sec\n", NUM_TESTS,
156 	       TEST_LENGTH_USEC / USEC_PER_SEC);
157 	printf("CPU hog will be bound to CPU %d.\n", test_cpu);
158 
159 	/*
160 	 * If this fails due to ENOSPC the definition of BOOSTGROUPS_COUNT in
161 	 * kernel/sched/tune.c needs to be increased.
162 	 */
163 	SAFE_MKDIR(STUNE_TEST_PATH, 0777);
164 	if (access(STUNE_TEST_PATH "/schedtune.colocate", W_OK) != -1) {
165 		SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.colocate",
166 				 "0");
167 	}
168 	if (access(STUNE_TEST_PATH "/schedtune.prefer_idle", W_OK) != -1) {
169 		SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.prefer_idle",
170 				 "0");
171 	}
172 	if (access(STUNE_TEST_PATH "/schedtune.sched_boost_enabled", W_OK) !=
173 	    -1) {
174 		SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.sched_boost_enabled",
175 				 "1");
176 	}
177 	if (access(STUNE_TEST_PATH "/schedtune.sched_boost_no_override",
178 		   W_OK) != -1) {
179 		SAFE_FILE_PRINTF(STUNE_TEST_PATH "/schedtune.sched_boost_no_override",
180 				 "0");
181 	}
182 
183 	SAFE_PTHREAD_CREATE(&test_thread, NULL, test_fn, NULL);
184 
185 	tracefs_write("tracing_on", "0");
186 	tracefs_write("buffer_size_kb", "16384");
187 	tracefs_write("set_event", TRACE_EVENTS);
188 
189 	while (test_index < NUM_TESTS)
190 		run_test();
191 
192 	SAFE_PTHREAD_JOIN(test_thread, NULL);
193 	SAFE_RMDIR(STUNE_TEST_PATH);
194 	check_results();
195 }
196 
197 static struct tst_test test = {
198 	.test_all = run,
199 	.setup = trace_setup,
200 	.cleanup = trace_cleanup,
201 };
202