1 /* 2 * Copyright (c) 2018 Google, Inc. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * Some CFS tasks are started with different priorities. The tasks are CPU hogs 7 * and affined to the same CPU. Their runtime is checked to see that it 8 * corresponds to that which is expected given the task priorities. 9 */ 10 11 #define _GNU_SOURCE 12 #include <errno.h> 13 #include <pthread.h> 14 #include <sched.h> 15 #include <semaphore.h> 16 #include <stdlib.h> 17 #include <time.h> 18 19 #include "tst_test.h" 20 #include "tst_safe_file_ops.h" 21 #include "tst_safe_pthread.h" 22 23 #include "trace_parse.h" 24 #include "util.h" 25 26 #define TRACE_EVENTS "sched_switch" 27 28 static int cfs_task_tids[4]; 29 /* If testing a nice value of -1, task_fn's use of nice() must be amended to 30 * check for an error properly. */ 31 static int nice_vals[] = { -15, -5, 5, 15 }; 32 /* These come from sched_prio_to_weight in kernel/sched/core.c. */ 33 static int prio_to_weight[] = { 29154, 3121, 335, 36 }; 34 35 #define TEST_TASK_SECONDS 5 36 static void *task_fn(void *arg) 37 { 38 int idx = (int *)arg - cfs_task_tids; 39 40 cfs_task_tids[idx] = gettid(); 41 42 affine(0); 43 if (nice(nice_vals[idx]) != nice_vals[idx]) { 44 printf("Error calling nice(%d)\n", nice_vals[idx]); 45 return NULL; 46 } 47 burn(TEST_TASK_SECONDS * USEC_PER_SEC, 0); 48 return NULL; 49 } 50 51 #define LOWER_BOUND_PCT 80 52 #define UPPER_BOUND_PCT 105 53 #define LOWER_BOUND_US 20000 54 #define UPPER_BOUND_US 30000 55 56 int check_bounds(long long expected_us, long long runtime_us) 57 { 58 int rv = 0; 59 long long lower_bound, lower_bound_pct, lower_bound_us; 60 long long upper_bound, upper_bound_pct, upper_bound_us; 61 62 lower_bound_pct = (LOWER_BOUND_PCT / (100 * expected_us)); 63 lower_bound_us = (expected_us - LOWER_BOUND_US); 64 if (lower_bound_us > lower_bound_pct) 65 lower_bound = lower_bound_us; 66 else 67 lower_bound = lower_bound_pct; 68 upper_bound_pct = (UPPER_BOUND_PCT / (100 * expected_us)); 69 upper_bound_us = (expected_us + UPPER_BOUND_US); 70 if (upper_bound_us > upper_bound_pct) 71 upper_bound = upper_bound_us; 72 else 73 upper_bound = upper_bound_pct; 74 75 if (runtime_us < lower_bound) { 76 printf(" lower bound of %lld ms not met\n", 77 lower_bound/1000); 78 rv = 1; 79 } 80 if (runtime_us > upper_bound) { 81 printf(" upper bound of %lld ms exceeded\n", 82 upper_bound/1000); 83 rv = 1; 84 } 85 return rv; 86 } 87 88 static int parse_results(void) 89 { 90 int i, j, weight_sum; 91 int rv = 0; 92 unsigned long long start_ts_us[4] = { 0, 0, 0, 0 }; 93 long long runtime_us[4] = { 0, 0, 0, 0 }; 94 long long expected_us[4]; 95 96 for (i = 0; i < num_trace_records; i++) { 97 struct trace_sched_switch *t = trace[i].event_data; 98 99 if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH) 100 continue; 101 102 for (j = 0; j < 4; j++) { 103 if (t->prev_pid == cfs_task_tids[j]) { 104 if (!start_ts_us[j]) { 105 printf("Trace parse error, start_ts_us " 106 "unset at segment end!\n"); 107 return -1; 108 } 109 runtime_us[j] += TS_TO_USEC(trace[i].ts) - 110 start_ts_us[j]; 111 start_ts_us[j] = 0; 112 } 113 if (t->next_pid == cfs_task_tids[j]) { 114 if (start_ts_us[j]) { 115 printf("Trace parse error, start_ts_us " 116 "already set at segment " 117 "start!\n"); 118 return -1; 119 } 120 start_ts_us[j] = TS_TO_USEC(trace[i].ts); 121 } 122 } 123 } 124 125 /* 126 * Task prio to weight values are defined in the kernel at the end of 127 * kernel/sched/core.c (as of 4.19). 128 * -15: 29154 129 * -5: 3121 130 * 5: 335 131 * 15: 36 132 * Sum of weights: 29154 + 3121 + 335 + 36 = 32646 133 * Expected task runtime % (time with 5 second test): 134 * 29154/32646 = 89.3%, 4465ms 135 * 3121/32646 = 9.56%, 478ms 136 * 335/32646 = 1.02%, 51ms 137 * 36/32646 = 0.11%, 5.5ms 138 */ 139 140 weight_sum = 0; 141 for (i = 0; i < 4; i++) 142 weight_sum += prio_to_weight[i]; 143 for (i = 0; i < 4; i++) { 144 /* 145 * Expected task runtime: 146 * (prio_to_weight[i] / weight_sum) * TEST_TASK_SECONDS 147 */ 148 expected_us[i] = TEST_TASK_SECONDS * USEC_PER_SEC; 149 expected_us[i] *= prio_to_weight[i]; 150 expected_us[i] /= weight_sum; 151 } 152 153 printf("Task runtimes:\n"); 154 155 printf("Task a (nice -15): %8lld ms (expected %8lld ms)\n", 156 runtime_us[0] / 1000, expected_us[0] / 1000); 157 rv |= check_bounds(expected_us[0], runtime_us[0]); 158 159 printf("Task b (nice -5) : %8lld ms (expected %8lld ms)\n", 160 runtime_us[1] / 1000, expected_us[1] / 1000); 161 rv |= check_bounds(expected_us[1], runtime_us[1]); 162 163 printf("Task c (nice 5) : %8lld ms (expected %8lld ms)\n", 164 runtime_us[2] / 1000, expected_us[2] / 1000); 165 rv |= check_bounds(expected_us[2], runtime_us[2]); 166 167 printf("Task d (nice 15) : %8lld ms (expected %8lld ms)\n", 168 runtime_us[3] / 1000, expected_us[3] / 1000); 169 rv |= check_bounds(expected_us[3], runtime_us[3]); 170 171 return rv; 172 } 173 174 #define NUM_TASKS 4 175 static void run(void) 176 { 177 pthread_t tasks[NUM_TASKS]; 178 int i; 179 180 printf("Running %d CFS tasks concurrently for %d sec\n", 181 NUM_TASKS, TEST_TASK_SECONDS); 182 183 /* configure and enable tracing */ 184 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 185 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384"); 186 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS); 187 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n"); 188 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1"); 189 190 for (i = 0; i < NUM_TASKS; i++) 191 SAFE_PTHREAD_CREATE(&tasks[i], NULL, task_fn, 192 &cfs_task_tids[i]); 193 for (i = 0; i < NUM_TASKS; i++) 194 SAFE_PTHREAD_JOIN(tasks[i], NULL); 195 196 /* disable tracing */ 197 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 198 LOAD_TRACE(); 199 200 if (parse_results()) 201 tst_res(TFAIL, "Task runtimes not within allowed margins " 202 "of expected values.\n"); 203 else 204 tst_res(TPASS, "Task runtimes within allowed margins " 205 "of expected values.\n"); 206 } 207 208 static struct tst_test test = { 209 .test_all = run, 210 .cleanup = trace_cleanup, 211 }; 212