1 /* 2 * Copyright (c) 2018 Google, Inc. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * A task executes as small then as big. Upmigration latency and task placement 7 * are verified. 8 */ 9 10 #define _GNU_SOURCE 11 #include <errno.h> 12 #include <pthread.h> 13 #include <sched.h> 14 #include <sys/types.h> 15 #include <time.h> 16 17 #include "tst_test.h" 18 #include "tst_safe_file_ops.h" 19 #include "tst_safe_pthread.h" 20 21 #include "trace_parse.h" 22 #include "util.h" 23 24 #define TRACE_EVENTS "sched_switch" 25 26 static int task_tid; 27 28 #define MAX_UPMIGRATE_LATENCY_US 100000 29 #define MAX_INCORRECT_CLUSTER_PCT 10 30 #define BURN_SEC 3 31 static void *task_fn(void *arg LTP_ATTRIBUTE_UNUSED) 32 { 33 task_tid = gettid(); 34 35 printf("Small task executing for %ds...\n", BURN_SEC); 36 burn(BURN_SEC * USEC_PER_SEC, 1); 37 38 printf("Changing to big task...\n"); 39 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "CPU HOG"); 40 burn(BURN_SEC * USEC_PER_SEC, 0); 41 42 return NULL; 43 } 44 45 static int parse_results(void) 46 { 47 int i, pct, rv = 0; 48 unsigned long long exec_start_us = 0; 49 unsigned long long too_big_cpu_us = 0; 50 unsigned long long too_small_cpu_us = 0; 51 unsigned long long small_task_us = 0; 52 unsigned long long big_task_us = 0; 53 unsigned long long cpuhog_ts_usec = 0; 54 unsigned long long upmigrate_ts_usec = 0; 55 unsigned long long upmigrate_latency_usec = 0; 56 cpu_set_t cpuset; 57 58 if (find_cpus_with_capacity(0, &cpuset)) { 59 printf("Failed to find the CPUs in the little cluster.\n"); 60 return -1; 61 } 62 63 for (i = 0; i < num_trace_records; i++) { 64 unsigned long long segment_us; 65 struct trace_sched_switch *t = trace[i].event_data; 66 67 if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE && 68 !strcmp(trace[i].event_data, "CPU HOG")) { 69 cpuhog_ts_usec = TS_TO_USEC(trace[i].ts); 70 continue; 71 } 72 73 if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH) 74 continue; 75 if (t->next_pid == task_tid) { 76 /* Start of task execution segment. */ 77 if (exec_start_us) { 78 printf("Trace parse fail: double exec start\n"); 79 return -1; 80 } 81 exec_start_us = TS_TO_USEC(trace[i].ts); 82 if (cpuhog_ts_usec && !upmigrate_ts_usec && 83 !CPU_ISSET(trace[i].cpu, &cpuset)) 84 upmigrate_ts_usec = exec_start_us; 85 continue; 86 } 87 if (t->prev_pid != task_tid) 88 continue; 89 /* End of task execution segment. */ 90 segment_us = TS_TO_USEC(trace[i].ts); 91 segment_us -= exec_start_us; 92 exec_start_us = 0; 93 if (CPU_ISSET(trace[i].cpu, &cpuset)) { 94 /* Task is running on little CPUs. */ 95 if (cpuhog_ts_usec) { 96 /* 97 * Upmigration is accounted separately, so only 98 * record mis-scheduled time here if it happened 99 * after upmigration. 100 */ 101 if (upmigrate_ts_usec) 102 too_small_cpu_us += segment_us; 103 } 104 } else { 105 /* Task is running on big CPUs. */ 106 if (!cpuhog_ts_usec) 107 too_big_cpu_us += segment_us; 108 } 109 if (cpuhog_ts_usec) 110 big_task_us += segment_us; 111 else 112 small_task_us += segment_us; 113 } 114 115 pct = (too_big_cpu_us * 100) / small_task_us; 116 rv |= (pct > MAX_INCORRECT_CLUSTER_PCT); 117 printf("Time incorrectly scheduled on big when task was small: " 118 "%lld usec (%d%% of small task CPU time)\n", too_big_cpu_us, 119 pct); 120 pct = (too_small_cpu_us * 100) / big_task_us; 121 rv |= (pct > MAX_INCORRECT_CLUSTER_PCT); 122 printf("Time incorrectly scheduled on small when task was big, " 123 "after upmigration: " 124 "%lld usec (%d%% of big task CPU time)\n", too_small_cpu_us, 125 pct); 126 127 if (upmigrate_ts_usec) { 128 upmigrate_latency_usec = upmigrate_ts_usec - cpuhog_ts_usec; 129 printf("Upmigration latency: %lld usec\n", 130 upmigrate_latency_usec); 131 } else { 132 printf("Task never upmigrated!\n"); 133 upmigrate_latency_usec = UINT_MAX; 134 } 135 136 return (rv || upmigrate_latency_usec > MAX_UPMIGRATE_LATENCY_US); 137 } 138 139 static void run(void) 140 { 141 pthread_t task_thread; 142 143 tst_res(TINFO, "Maximum incorrect cluster time percentage: %d%%", 144 MAX_INCORRECT_CLUSTER_PCT); 145 tst_res(TINFO, "Maximum upmigration latency: %d usec", 146 MAX_UPMIGRATE_LATENCY_US); 147 148 /* configure and enable tracing */ 149 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 150 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384"); 151 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS); 152 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n"); 153 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1"); 154 155 SAFE_PTHREAD_CREATE(&task_thread, NULL, task_fn, NULL); 156 SAFE_PTHREAD_JOIN(task_thread, NULL); 157 158 /* disable tracing */ 159 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 160 LOAD_TRACE(); 161 162 if (parse_results()) 163 tst_res(TFAIL, "Task placement and migration latency goals " 164 "were not met.\n"); 165 else 166 tst_res(TPASS, "Task placement and migration latency goals " 167 "were met.\n"); 168 } 169 170 static struct tst_test test = { 171 .test_all = run, 172 .cleanup = trace_cleanup, 173 }; 174