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 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 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 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 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 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 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