1 /* 2 * Copyright (c) 2018 Google, Inc. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * Task starts out as a CPU hog and then becomes small. 7 * Task placement and downmigration latency are verified. 8 */ 9 10 #define _GNU_SOURCE 11 #include <errno.h> 12 #include <pthread.h> 13 #include <sched.h> 14 #include <time.h> 15 16 #include "tst_test.h" 17 #include "tst_safe_file_ops.h" 18 #include "tst_safe_pthread.h" 19 20 #include "trace_parse.h" 21 #include "util.h" 22 23 #define TRACE_EVENTS "sched_switch" 24 25 static int task_tid; 26 27 #define MAX_DOWNMIGRATE_LATENCY_US 100000 28 #define MAX_INCORRECT_CLUSTER_PCT 10 29 #define BURN_SEC 3 30 static void *task_fn(void *arg LTP_ATTRIBUTE_UNUSED) 31 { 32 task_tid = gettid(); 33 34 printf("Big task executing for %ds...\n", BURN_SEC); 35 burn(BURN_SEC * USEC_PER_SEC, 0); 36 37 printf("Changing to small task...\n"); 38 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "SMALL TASK"); 39 burn(BURN_SEC * USEC_PER_SEC, 1); 40 41 return NULL; 42 } 43 44 static int parse_results(void) 45 { 46 int i, pct, rv = 0; 47 unsigned long long exec_start_us = 0; 48 unsigned long long too_big_cpu_us = 0; 49 unsigned long long too_small_cpu_us = 0; 50 unsigned long long big_task_us = 0; 51 unsigned long long small_task_us = 0; 52 unsigned long long smalltask_ts_usec = 0; 53 unsigned long long downmigrate_ts_usec = 0; 54 unsigned long long downmigrate_latency_usec = 0; 55 cpu_set_t cpuset; 56 57 if (find_cpus_with_capacity(0, &cpuset)) { 58 printf("Failed to find the CPUs in the little cluster.\n"); 59 return -1; 60 } 61 62 for (i = 0; i < num_trace_records; i++) { 63 unsigned long long segment_us; 64 struct trace_sched_switch *t = trace[i].event_data; 65 66 if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE && 67 !strcmp(trace[i].event_data, "SMALL TASK")) { 68 smalltask_ts_usec = TS_TO_USEC(trace[i].ts); 69 continue; 70 } 71 72 if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH) 73 continue; 74 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 (smalltask_ts_usec && !downmigrate_ts_usec && 83 CPU_ISSET(trace[i].cpu, &cpuset)) 84 downmigrate_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 (!smalltask_ts_usec) 96 too_small_cpu_us += segment_us; 97 } else { 98 /* Task is running on big CPUs. */ 99 if (smalltask_ts_usec) { 100 /* 101 * Downmigration is accounted separately, so 102 * only record mis-scheduled time here if it 103 * happened after downmigration. 104 */ 105 if (downmigrate_ts_usec) 106 too_big_cpu_us += segment_us; 107 } 108 } 109 if (smalltask_ts_usec) 110 small_task_us += segment_us; 111 else 112 big_task_us += segment_us; 113 } 114 115 pct = (too_small_cpu_us * 100) / big_task_us; 116 rv |= (pct > MAX_INCORRECT_CLUSTER_PCT); 117 printf("Time incorrectly scheduled on small when task was big: " 118 "%lld usec (%d%% of big task CPU time)\n", too_small_cpu_us, 119 pct); 120 pct = (too_big_cpu_us * 100) / small_task_us; 121 rv |= (pct > MAX_INCORRECT_CLUSTER_PCT); 122 printf("Time incorrectly scheduled on big when task was small, after " 123 "downmigration: %lld usec (%d%% of small task CPU time)\n", 124 too_big_cpu_us, pct); 125 126 if (downmigrate_ts_usec) { 127 downmigrate_latency_usec = downmigrate_ts_usec - 128 smalltask_ts_usec; 129 printf("Downmigration latency: %lld usec\n", 130 downmigrate_latency_usec); 131 } else { 132 printf("Task never downmigrated!\n"); 133 downmigrate_latency_usec = UINT_MAX; 134 } 135 136 return (rv || downmigrate_latency_usec > MAX_DOWNMIGRATE_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 downmigration latency: %d usec", 146 MAX_DOWNMIGRATE_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/migration latency goals " 164 "not met.\n"); 165 else 166 tst_res(TPASS, "Task placement/migration latency goals " 167 "met.\n"); 168 } 169 170 static struct tst_test test = { 171 .test_all = run, 172 .cleanup = trace_cleanup, 173 }; 174