1 /*
2 * Copyright (c) 2018 Google, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * This test attempts to verify that the schedutil governor does not take into
7 * account stale utilization from an idle CPU when calculating the frequency for
8 * a shared policy.
9 *
10 * This test is not yet complete and may never be. The CPU in question may
11 * receive spurious updates which push the stale deadline out, causing the test
12 * to fail.
13 */
14
15 #define _GNU_SOURCE
16 #include <errno.h>
17 #include <pthread.h>
18 #include <sched.h>
19 #include <time.h>
20 #include <semaphore.h>
21 #include <stdlib.h>
22
23 #include "tst_test.h"
24 #include "tst_safe_file_ops.h"
25 #include "tst_safe_pthread.h"
26
27 #include "trace_parse.h"
28 #include "util.h"
29
30 #define TRACE_EVENTS "sugov_next_freq sugov_util_update"
31
32 #define MAX_TEST_CPUS 32
33 static int policy_cpus[MAX_TEST_CPUS];
34 static int policy_num_cpus = 0;
35
36 static int test_cpu;
37 static sem_t sem;
38
39 /* sugov currently waits 1.125 * TICK_NSEC, which with HZ=300, is
40 * ~3.75ms for PELT
41 * On WALT, 1.125 * sched_ravg_window (20ms) is 22.5ms */
42 #define MAX_STALE_USEC 22500
43 /* The event task may not wake up right away due to timer slack. */
44 #define SLACK_USEC 10000
45
event_fn(void * arg LTP_ATTRIBUTE_UNUSED)46 static void *event_fn(void *arg LTP_ATTRIBUTE_UNUSED)
47 {
48 /*
49 * FIXME: Proper logic to identify a multi-CPU policy and select two
50 * CPUS from it is required here.
51 */
52 affine(test_cpu - 1);
53
54 sem_wait(&sem);
55
56 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker",
57 "event task sleep");
58 usleep(MAX_STALE_USEC);
59 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker",
60 "event task wake");
61 /*
62 * Waking up should be sufficient to get the cpufreq policy to
63 * re-evaluate.
64 */
65 return NULL;
66 }
67
68 #define BURN_MSEC 500
burn_fn(void * arg LTP_ATTRIBUTE_UNUSED)69 static void *burn_fn(void *arg LTP_ATTRIBUTE_UNUSED)
70 {
71 affine(test_cpu);
72
73 /*
74 * wait a bit to allow any hacks to boost frequency on migration
75 * to take effect
76 */
77 usleep(200);
78
79 /* Busy loop for BURN_MSEC to get the task demand to maximum. */
80 burn(BURN_MSEC * 1000, 0);
81
82 /*
83 * Sleep. The next sugov update after TICK_NSEC should not include
84 * this task's contribution.
85 */
86 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "sleeping");
87
88 /*
89 * Wake up task on another CPU in the same policy which will sleep
90 * for stale_ns, then wake up briefly to trigger a recalculation of the
91 * cpufreq policy.
92 */
93 sem_post(&sem);
94 sleep(2);
95
96 return NULL;
97 }
98
cpu_in_policy(int cpu)99 static int cpu_in_policy(int cpu)
100 {
101 int i;
102 for (i = 0; i < policy_num_cpus; i++)
103 if (cpu == policy_cpus[i])
104 return 1;
105 return 0;
106 }
107
parse_results(void)108 static int parse_results(void)
109 {
110 int i, sleep_idx;
111 int max_util_seen = 0;
112 unsigned int stale_usec;
113
114 /* Verify that utilization reached 1024 before sleep. */
115 for (i = 0; i < num_trace_records; i++) {
116 if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) {
117 struct trace_sugov_util_update *t =
118 trace[i].event_data;
119 if (t->cpu == test_cpu && t->util > max_util_seen)
120 max_util_seen = t->util;
121 }
122 if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
123 !strcmp(trace[i].event_data, "sleeping"))
124 break;
125 }
126 printf("Max util seen from CPU hog: %d\n", max_util_seen);
127 if (max_util_seen < 1000) {
128 printf("Trace parse error, utilization of CPU hog did "
129 "not reach 1000.\n");
130 return -1;
131 }
132 sleep_idx = i;
133 // print_trace_record(&trace[i]);
134 for (; i < num_trace_records; i++)
135 if (trace[i].event_type == TRACE_RECORD_SUGOV_NEXT_FREQ) {
136 struct trace_sugov_next_freq *t =
137 trace[i].event_data;
138 /* We should only see some minor utilization. */
139 if (cpu_in_policy(t->cpu) && t->util < 200)
140 break;
141 }
142 if (i == num_trace_records) {
143 printf("Trace parse error, util never went stale!\n");
144 return -1;
145 }
146 // print_trace_record(&trace[i]);
147 stale_usec = TS_TO_USEC(trace[i].ts) - TS_TO_USEC(trace[sleep_idx].ts);
148
149 printf("Stale vote shown to be cleared in %d usec.\n", stale_usec);
150 return (stale_usec > (MAX_STALE_USEC + SLACK_USEC));
151 }
152
153 #define POLICY_CPUS_BUFSIZE 1024
get_policy_cpus(void)154 static void get_policy_cpus(void)
155 {
156 int i=0, len, policy_cpus_fd;
157 char policy_cpus_fname[128];;
158 char *buf;
159
160 sprintf(policy_cpus_fname,
161 "/sys/devices/system/cpu/cpu%d/cpufreq/related_cpus",
162 test_cpu);
163 buf = SAFE_MALLOC(POLICY_CPUS_BUFSIZE);
164
165 policy_cpus_fd = open(policy_cpus_fname, O_RDONLY);
166 if (policy_cpus_fd < 0) {
167 printf("Failed to open policy cpus (errno %d)\n",
168 errno);
169 return;
170 }
171
172 len = read(policy_cpus_fd, buf, POLICY_CPUS_BUFSIZE -1);
173 /* At least one digit is expected. */
174 if (len < 2) {
175 printf("Read of policy cpus returned %d (errno %d)\n",
176 len, errno);
177 return;
178 }
179 close(policy_cpus_fd);
180 /* buf now has a list of CPUs, parse it */
181 while(buf[i] >= '0' && buf[i] <= '9') {
182 int j = i;
183 while (buf[j] >= '0' && buf[j] <= '9')
184 j++;
185 buf[j] = 0;
186 policy_cpus[policy_num_cpus++] = atoi(&buf[i]);
187 i = j + 1;
188 }
189 printf("Testing on CPU %d, all CPUs in that policy:\n",
190 test_cpu);
191 for (int i = 0; i < policy_num_cpus; i++)
192 printf(" %d", policy_cpus[i]);
193 printf("\n");
194 free(buf);
195 }
196
run(void)197 static void run(void)
198 {
199 pthread_t burn_thread, event_thread;
200
201 test_cpu = tst_ncpus() - 1;
202 printf("CPU hog will be bound to CPU %d.\n", test_cpu);
203 get_policy_cpus();
204
205 sem_init(&sem, 0, 0);
206
207 /* configure and enable tracing */
208 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
209 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
210 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
211 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
212 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
213
214 SAFE_PTHREAD_CREATE(&burn_thread, NULL, burn_fn, NULL);
215 SAFE_PTHREAD_CREATE(&event_thread, NULL, event_fn, NULL);
216
217 SAFE_PTHREAD_JOIN(burn_thread, NULL);
218 SAFE_PTHREAD_JOIN(event_thread, NULL);
219
220 /* disable tracing */
221 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
222 LOAD_TRACE();
223
224 if (parse_results())
225 tst_res(TFAIL, "Stale utilization not cleared within expected "
226 "time (%d usec).\n", MAX_STALE_USEC + SLACK_USEC);
227 else
228 tst_res(TPASS, "Stale utilization cleared within expected "
229 "time.\n");
230 }
231
232 static struct tst_test test = {
233 .test_all = run,
234 .cleanup = trace_cleanup,
235 };
236