xref: /aosp_15_r20/external/ltp/testcases/kernel/sched/eas/sugov_stale_util.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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 	tracefs_write("trace_marker", "event task sleep");
57 	usleep(MAX_STALE_USEC);
58 	tracefs_write("trace_marker", "event task wake");
59 	/*
60 	 * Waking up should be sufficient to get the cpufreq policy to
61 	 * re-evaluate.
62 	 */
63 	return NULL;
64 }
65 
66 #define BURN_MSEC 500
burn_fn(void * arg LTP_ATTRIBUTE_UNUSED)67 static void *burn_fn(void *arg LTP_ATTRIBUTE_UNUSED)
68 {
69 	affine(test_cpu);
70 
71 	/*
72 	 * wait a bit to allow any hacks to boost frequency on migration
73 	 * to take effect
74 	 */
75 	usleep(200);
76 
77 	/* Busy loop for BURN_MSEC to get the task demand to maximum. */
78 	burn(BURN_MSEC * 1000, 0);
79 
80 	/*
81 	 * Sleep. The next sugov update after TICK_NSEC should not include
82 	 * this task's contribution.
83 	 */
84 	tracefs_write("trace_marker", "sleeping");
85 
86 	/*
87 	 * Wake up task on another CPU in the same policy which will sleep
88 	 * for stale_ns, then wake up briefly to trigger a recalculation of the
89 	 * cpufreq policy.
90 	 */
91 	sem_post(&sem);
92 	sleep(2);
93 
94 	return NULL;
95 }
96 
cpu_in_policy(int cpu)97 static int cpu_in_policy(int cpu)
98 {
99 	int i;
100 	for (i = 0; i < policy_num_cpus; i++)
101 		if (cpu == policy_cpus[i])
102 			return 1;
103 	return 0;
104 }
105 
parse_results(void)106 static int parse_results(void)
107 {
108 	int i, sleep_idx;
109 	int max_util_seen = 0;
110 	unsigned int stale_usec;
111 
112 	/* Verify that utilization reached 1024 before sleep. */
113 	for (i = 0; i < num_trace_records; i++) {
114 		if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) {
115 			struct trace_sugov_util_update *t =
116 				trace[i].event_data;
117 			if (t->cpu == test_cpu && t->util > max_util_seen)
118 				max_util_seen = t->util;
119 		}
120 		if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
121 		    !strcmp(trace[i].event_data, "sleeping"))
122 			break;
123 	}
124 	printf("Max util seen from CPU hog: %d\n", max_util_seen);
125 	if (max_util_seen < 1000) {
126 		printf("Trace parse error, utilization of CPU hog did "
127 		       "not reach 1000.\n");
128 		return -1;
129 	}
130 	sleep_idx = i;
131 //	print_trace_record(&trace[i]);
132 	for (; i < num_trace_records; i++)
133 		if (trace[i].event_type == TRACE_RECORD_SUGOV_NEXT_FREQ) {
134 			struct trace_sugov_next_freq *t =
135 				trace[i].event_data;
136 			/* We should only see some minor utilization. */
137 			if (cpu_in_policy(t->cpu) && t->util < 200)
138 				break;
139 		}
140 	if (i == num_trace_records) {
141 		printf("Trace parse error, util never went stale!\n");
142 		return -1;
143 	}
144 //	print_trace_record(&trace[i]);
145 	stale_usec = TS_TO_USEC(trace[i].ts) - TS_TO_USEC(trace[sleep_idx].ts);
146 
147 	printf("Stale vote shown to be cleared in %d usec.\n", stale_usec);
148 	return (stale_usec > (MAX_STALE_USEC + SLACK_USEC));
149 }
150 
151 #define POLICY_CPUS_BUFSIZE 1024
get_policy_cpus(void)152 static void get_policy_cpus(void)
153 {
154 	int i=0, len, policy_cpus_fd;
155 	char policy_cpus_fname[128];;
156 	char *buf;
157 
158 	sprintf(policy_cpus_fname,
159 		"/sys/devices/system/cpu/cpu%d/cpufreq/related_cpus",
160 		test_cpu);
161 	buf = SAFE_MALLOC(POLICY_CPUS_BUFSIZE);
162 
163 	policy_cpus_fd = open(policy_cpus_fname, O_RDONLY);
164 	if (policy_cpus_fd < 0) {
165 		printf("Failed to open policy cpus (errno %d)\n",
166 		       errno);
167 		return;
168 	}
169 
170 	len = read(policy_cpus_fd, buf, POLICY_CPUS_BUFSIZE -1);
171 	/* At least one digit is expected. */
172 	if (len < 2) {
173 		printf("Read of policy cpus returned %d (errno %d)\n",
174 		       len, errno);
175 		return;
176 	}
177 	close(policy_cpus_fd);
178 	/* buf now has a list of CPUs, parse it */
179 	while(buf[i] >= '0' && buf[i] <= '9') {
180 		int j = i;
181 		while (buf[j] >= '0' && buf[j] <= '9')
182 			j++;
183 		buf[j] = 0;
184 		policy_cpus[policy_num_cpus++] = atoi(&buf[i]);
185 		i = j + 1;
186 	}
187 	printf("Testing on CPU %d, all CPUs in that policy:\n",
188 	       test_cpu);
189 	for (int i = 0; i < policy_num_cpus; i++)
190 		printf(" %d", policy_cpus[i]);
191 	printf("\n");
192 	free(buf);
193 }
194 
run(void)195 static void run(void)
196 {
197 	pthread_t burn_thread, event_thread;
198 
199 	test_cpu = tst_ncpus() - 1;
200 	printf("CPU hog will be bound to CPU %d.\n", test_cpu);
201 	get_policy_cpus();
202 
203 	sem_init(&sem, 0, 0);
204 
205 	/* configure and enable tracing */
206 	tracefs_write("tracing_on", "0");
207 	tracefs_write("buffer_size_kb", "16384");
208 	tracefs_write("set_event", TRACE_EVENTS);
209 	tracefs_write("trace", "\n");
210 	tracefs_write("tracing_on", "1");
211 
212 	SAFE_PTHREAD_CREATE(&burn_thread, NULL, burn_fn, NULL);
213 	SAFE_PTHREAD_CREATE(&event_thread, NULL, event_fn, NULL);
214 
215 	SAFE_PTHREAD_JOIN(burn_thread, NULL);
216 	SAFE_PTHREAD_JOIN(event_thread, NULL);
217 
218 	/* disable tracing */
219 	tracefs_write("tracing_on", "0");
220 	LOAD_TRACE();
221 
222 	if (parse_results())
223 		tst_res(TFAIL, "Stale utilization not cleared within expected "
224 			"time (%d usec).\n", MAX_STALE_USEC + SLACK_USEC);
225 	else
226 		tst_res(TPASS, "Stale utilization cleared within expected "
227 			"time.\n");
228 }
229 
230 static struct tst_test test = {
231 	.test_all = run,
232 	.setup = trace_setup,
233 	.cleanup = trace_cleanup,
234 };
235