1 /* 2 * Copyright (c) 2018 Google, Inc. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * Three RT RR tasks are created and affined to the same CPU. They execute as 7 * CPU hogs. Their runtime is checked to see that they share the CPU as 8 * expected. 9 */ 10 11 #define _GNU_SOURCE 12 #include <errno.h> 13 #include <pthread.h> 14 #include <sched.h> 15 #include <semaphore.h> 16 #include <time.h> 17 18 #include "tst_test.h" 19 #include "tst_safe_file_ops.h" 20 #include "tst_safe_pthread.h" 21 22 #include "trace_parse.h" 23 #include "util.h" 24 25 #define TRACE_EVENTS "sched_wakeup sched_switch sched_process_exit" 26 27 #define EXEC_MIN_PCT 33 28 #define EXEC_MAX_PCT 34 29 30 static sem_t sem; 31 32 static int rt_a_tid; 33 static int rt_b_tid; 34 static int rt_c_tid; 35 36 #define BUSY_WAIT_USECS 10000000 37 static void *rt_b_fn(void *arg LTP_ATTRIBUTE_UNUSED) 38 { 39 rt_b_tid = gettid(); 40 affine(0); 41 sem_wait(&sem); 42 burn(BUSY_WAIT_USECS, 0); 43 return NULL; 44 } 45 46 static void *rt_c_fn(void *arg LTP_ATTRIBUTE_UNUSED) 47 { 48 rt_c_tid = gettid(); 49 affine(0); 50 sem_wait(&sem); 51 burn(BUSY_WAIT_USECS, 0); 52 return NULL; 53 } 54 55 static void *rt_a_fn(void *arg LTP_ATTRIBUTE_UNUSED) 56 { 57 rt_a_tid = gettid(); 58 affine(0); 59 /* Give all other tasks a chance to affine and block. */ 60 usleep(3000); 61 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "TEST START"); 62 sem_post(&sem); 63 sem_post(&sem); 64 burn(BUSY_WAIT_USECS, 0); 65 return NULL; 66 } 67 68 static int parse_results(void) 69 { 70 int i, pct, rv; 71 int test_start = 0; 72 unsigned long long exec_start_us = 0; 73 unsigned long a_exec_us = 0; 74 unsigned long b_exec_us = 0; 75 unsigned long c_exec_us = 0; 76 unsigned long total; 77 78 for (i = 0; i < num_trace_records; i++) { 79 struct trace_sched_switch *t = trace[i].event_data; 80 unsigned long long segment_us; 81 82 if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE && 83 !strcmp(trace[i].event_data, "TEST START")) { 84 /* We need to include this segment in A's exec time. */ 85 exec_start_us = TS_TO_USEC(trace[i].ts); 86 test_start = 1; 87 } 88 89 if (!test_start) 90 continue; 91 92 if (trace[i].event_type != TRACE_RECORD_SCHED_SWITCH) 93 continue; 94 95 segment_us = TS_TO_USEC(trace[i].ts) - exec_start_us; 96 if (t->prev_pid == rt_a_tid) 97 a_exec_us += segment_us; 98 else if (t->prev_pid == rt_b_tid) 99 b_exec_us += segment_us; 100 else if (t->prev_pid == rt_c_tid) 101 c_exec_us += segment_us; 102 if (t->next_pid == rt_a_tid || 103 t->next_pid == rt_b_tid || 104 t->next_pid == rt_c_tid) 105 exec_start_us = TS_TO_USEC(trace[i].ts); 106 } 107 108 rv = 0; 109 total = a_exec_us + b_exec_us + c_exec_us; 110 pct = (a_exec_us * 100) / total; 111 rv |= (pct < EXEC_MIN_PCT || pct > EXEC_MAX_PCT); 112 printf("a exec time: %ld usec (%d%%)\n", a_exec_us, pct); 113 pct = (b_exec_us * 100) / total; 114 rv |= (pct < EXEC_MIN_PCT || pct > EXEC_MAX_PCT); 115 printf("b exec time: %ld usec (%d%%)\n", b_exec_us, pct); 116 pct = (c_exec_us * 100) / total; 117 rv |= (pct < EXEC_MIN_PCT || pct > EXEC_MAX_PCT); 118 printf("c exec time: %ld usec (%d%%)\n", c_exec_us, pct); 119 120 return rv; 121 } 122 123 static void create_rt_thread(int prio, void *fn, pthread_t *rt_thread) 124 { 125 pthread_attr_t rt_thread_attrs; 126 struct sched_param rt_thread_sched_params; 127 128 ERROR_CHECK(pthread_attr_init(&rt_thread_attrs)); 129 ERROR_CHECK(pthread_attr_setinheritsched(&rt_thread_attrs, 130 PTHREAD_EXPLICIT_SCHED)); 131 ERROR_CHECK(pthread_attr_setschedpolicy(&rt_thread_attrs, 132 SCHED_RR)); 133 rt_thread_sched_params.sched_priority = prio; 134 ERROR_CHECK(pthread_attr_setschedparam(&rt_thread_attrs, 135 &rt_thread_sched_params)); 136 137 SAFE_PTHREAD_CREATE(rt_thread, &rt_thread_attrs, fn, NULL); 138 } 139 140 #define NUM_TASKS 3 141 static void run(void) 142 { 143 pthread_t rt_a, rt_b, rt_c; 144 145 sem_init(&sem, 0, 0); 146 147 printf("Running %d RT RR tasks for 10 seconds...\n", NUM_TASKS); 148 149 /* configure and enable tracing */ 150 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 151 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384"); 152 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS); 153 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n"); 154 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1"); 155 156 create_rt_thread(70, rt_a_fn, &rt_a); 157 create_rt_thread(70, rt_b_fn, &rt_b); 158 create_rt_thread(70, rt_c_fn, &rt_c); 159 160 SAFE_PTHREAD_JOIN(rt_a, NULL); 161 SAFE_PTHREAD_JOIN(rt_b, NULL); 162 SAFE_PTHREAD_JOIN(rt_c, NULL); 163 164 /* disable tracing */ 165 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 166 LOAD_TRACE(); 167 168 if (parse_results()) 169 tst_res(TFAIL, "RT RR tasks did not receive the expected CPU " 170 "time (all between %d-%d %% CPU).\n", EXEC_MIN_PCT, 171 EXEC_MAX_PCT); 172 else 173 tst_res(TPASS, "RT RR tasks received the expected CPU time.\n"); 174 } 175 176 static struct tst_test test = { 177 .test_all = run, 178 .cleanup = trace_cleanup, 179 }; 180