1 /* 2 * Copyright (c) 2018 Google, Inc. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 * 6 * Six RT FIFO tasks are created and affined to the same CPU. They execute 7 * with a particular pattern of overlapping eligibility to run. The resulting 8 * execution pattern is checked to see that the tasks execute as expected given 9 * their priorities. 10 */ 11 12 #define _GNU_SOURCE 13 #include <errno.h> 14 #include <pthread.h> 15 #include <sched.h> 16 #include <semaphore.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_wakeup sched_switch sched_process_exit" 27 28 static int rt_task_tids[6]; 29 30 /* 31 * Create two of each RT FIFO task at each priority level. Ensure that 32 * - higher priority RT tasks preempt lower priority RT tasks 33 * - newly woken RT tasks at the same priority level do not preempt currently 34 * running RT tasks 35 * 36 * Affine all tasks to CPU 0. 37 * Have rt_low_fn 1 run first. It wakes up rt_low_fn 2, which should not run 38 * until rt_low_fn 1 sleeps/exits. 39 * rt_low_fn2 wakes rt_med_fn1. rt_med_fn1 should run immediately, then sleep, 40 * allowing rt_low_fn2 to complete. 41 * rt_med_fn1 wakes rt_med_fn2, which should not run until rt_med_fn 2 42 * sleeps/exits... (etc) 43 */ 44 static sem_t sem_high_b; 45 static sem_t sem_high_a; 46 static sem_t sem_med_b; 47 static sem_t sem_med_a; 48 static sem_t sem_low_b; 49 static sem_t sem_low_a; 50 51 enum { 52 RT_LOW_FN_A_TID = 0, 53 RT_LOW_FN_B_TID, 54 RT_MED_FN_A_TID, 55 RT_MED_FN_B_TID, 56 RT_HIGH_FN_A_TID, 57 RT_HIGH_FN_B_TID, 58 }; 59 60 struct expected_event { 61 int event_type; 62 /* 63 * If sched_wakeup, pid being woken. 64 * If sched_switch, pid being switched to. 65 */ 66 int event_data; 67 }; 68 static struct expected_event events[] = { 69 /* rt_low_fn_a wakes up rt_low_fn_b: 70 * sched_wakeup(rt_low_fn_b) */ 71 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 72 .event_data = RT_LOW_FN_B_TID}, 73 /* TODO: Expect an event for the exit of rt_low_fn_a. */ 74 /* 3ms goes by, then rt_low_fn_a exits and rt_low_fn_b starts running */ 75 { .event_type = TRACE_RECORD_SCHED_SWITCH, 76 .event_data = RT_LOW_FN_B_TID}, 77 /* rt_low_fn_b wakes rt_med_fn_a which runs immediately */ 78 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 79 .event_data = RT_MED_FN_A_TID}, 80 { .event_type = TRACE_RECORD_SCHED_SWITCH, 81 .event_data = RT_MED_FN_A_TID}, 82 /* rt_med_fn_a sleeps, allowing rt_low_fn_b time to exit */ 83 { .event_type = TRACE_RECORD_SCHED_SWITCH, 84 .event_data = RT_LOW_FN_B_TID}, 85 /* TODO: Expect an event for the exit of rt_low_fn_b. */ 86 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 87 .event_data = RT_MED_FN_A_TID}, 88 { .event_type = TRACE_RECORD_SCHED_SWITCH, 89 .event_data = RT_MED_FN_A_TID}, 90 /* rt_med_fn_a wakes rt_med_fn_b */ 91 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 92 .event_data = RT_MED_FN_B_TID}, 93 /* 3ms goes by, then rt_med_fn_a exits and rt_med_fn_b starts running */ 94 /* TODO: Expect an event for the exit of rt_med_fn_a */ 95 { .event_type = TRACE_RECORD_SCHED_SWITCH, 96 .event_data = RT_MED_FN_B_TID}, 97 /* rt_med_fn_b wakes up rt_high_fn_a which runs immediately */ 98 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 99 .event_data = RT_HIGH_FN_A_TID}, 100 { .event_type = TRACE_RECORD_SCHED_SWITCH, 101 .event_data = RT_HIGH_FN_A_TID}, 102 /* rt_high_fn_a sleeps, allowing rt_med_fn_b time to exit */ 103 { .event_type = TRACE_RECORD_SCHED_SWITCH, 104 .event_data = RT_MED_FN_B_TID}, 105 /* TODO: Expect an event for the exit of rt_med_fn_b */ 106 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 107 .event_data = RT_HIGH_FN_A_TID}, 108 { .event_type = TRACE_RECORD_SCHED_SWITCH, 109 .event_data = RT_HIGH_FN_A_TID}, 110 /* rt_high_fn_a wakes up rt_high_fn_b */ 111 { .event_type = TRACE_RECORD_SCHED_WAKEUP, 112 .event_data = RT_HIGH_FN_B_TID}, 113 /* 3ms goes by, then rt_high_fn_a exits and rt_high_fn_b starts running */ 114 /* TODO: Expect an event for the exit of rt_high_fn_a */ 115 { .event_type = TRACE_RECORD_SCHED_SWITCH, 116 .event_data = RT_HIGH_FN_B_TID}, 117 }; 118 119 static void *rt_high_fn_b(void *arg LTP_ATTRIBUTE_UNUSED) 120 { 121 rt_task_tids[RT_HIGH_FN_B_TID] = gettid(); 122 affine(0); 123 124 /* Wait for rt_high_fn_a to wake us up. */ 125 sem_wait(&sem_high_b); 126 /* Run after rt_high_fn_a exits. */ 127 128 return NULL; 129 } 130 131 static void *rt_high_fn_a(void *arg LTP_ATTRIBUTE_UNUSED) 132 { 133 rt_task_tids[RT_HIGH_FN_A_TID] = gettid(); 134 affine(0); 135 136 /* Wait for rt_med_fn_b to wake us up. */ 137 sem_wait(&sem_high_a); 138 139 /* Sleep, allowing rt_med_fn_b a chance to exit. */ 140 usleep(1000); 141 142 /* Wake up rt_high_fn_b. We should continue to run though. */ 143 sem_post(&sem_high_b); 144 145 /* Busy wait for just a bit. */ 146 burn(3000, 0); 147 148 return NULL; 149 } 150 151 static void *rt_med_fn_b(void *arg LTP_ATTRIBUTE_UNUSED) 152 { 153 rt_task_tids[RT_MED_FN_B_TID] = gettid(); 154 affine(0); 155 156 /* Wait for rt_med_fn_a to wake us up. */ 157 sem_wait(&sem_med_b); 158 /* Run after rt_med_fn_a exits. */ 159 160 /* This will wake up rt_high_fn_a which will run immediately, preempting 161 * us. */ 162 sem_post(&sem_high_a); 163 164 return NULL; 165 } 166 167 static void *rt_med_fn_a(void *arg LTP_ATTRIBUTE_UNUSED) 168 { 169 rt_task_tids[RT_MED_FN_A_TID] = gettid(); 170 affine(0); 171 172 /* Wait for rt_low_fn_b to wake us up. */ 173 sem_wait(&sem_med_a); 174 175 /* Sleep, allowing rt_low_fn_b a chance to exit. */ 176 usleep(3000); 177 178 /* Wake up rt_med_fn_b. We should continue to run though. */ 179 sem_post(&sem_med_b); 180 181 /* Busy wait for just a bit. */ 182 burn(3000, 0); 183 184 return NULL; 185 } 186 187 static void *rt_low_fn_b(void *arg LTP_ATTRIBUTE_UNUSED) 188 { 189 rt_task_tids[RT_LOW_FN_B_TID] = gettid(); 190 affine(0); 191 192 /* Wait for rt_low_fn_a to wake us up. */ 193 sem_wait(&sem_low_b); 194 /* Run after rt_low_fn_a exits. */ 195 196 /* This will wake up rt_med_fn_a which will run immediately, preempting 197 * us. */ 198 sem_post(&sem_med_a); 199 200 /* So the previous sem_post isn't actually causing a sched_switch 201 * to med_a immediately - this is running a bit longer and exiting. 202 * Delay here. */ 203 burn(1000, 0); 204 205 return NULL; 206 } 207 208 /* Put real task tids into the expected events. */ 209 static void fixup_expected_events(void) 210 { 211 int i; 212 int size = sizeof(events)/sizeof(struct expected_event); 213 214 for (i = 0; i < size; i++) 215 events[i].event_data = rt_task_tids[events[i].event_data]; 216 } 217 218 static void *rt_low_fn_a(void *arg LTP_ATTRIBUTE_UNUSED) 219 { 220 rt_task_tids[RT_LOW_FN_A_TID] = gettid(); 221 affine(0); 222 223 /* Give all other tasks a chance to set their tids and block. */ 224 usleep(3000); 225 226 fixup_expected_events(); 227 228 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "TEST START"); 229 230 /* Wake up rt_low_fn_b. We should continue to run though. */ 231 sem_post(&sem_low_b); 232 233 /* Busy wait for just a bit. */ 234 burn(3000, 0); 235 236 return NULL; 237 } 238 239 /* Returns whether the given tid is a tid of one of the RT tasks in this 240 * testcase. */ 241 static int rt_tid(int tid) 242 { 243 int i; 244 for (i = 0; i < 6; i++) 245 if (rt_task_tids[i] == tid) 246 return 1; 247 return 0; 248 } 249 250 static int parse_results(void) 251 { 252 int i; 253 int test_start = 0; 254 int event_idx = 0; 255 int events_size = sizeof(events)/sizeof(struct expected_event); 256 257 for (i = 0; i < num_trace_records; i++) { 258 if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE && 259 !strcmp(trace[i].event_data, "TEST START")) 260 test_start = 1; 261 262 if (!test_start) 263 continue; 264 265 if (trace[i].event_type != TRACE_RECORD_SCHED_WAKEUP && 266 trace[i].event_type != TRACE_RECORD_SCHED_SWITCH) 267 continue; 268 269 if (trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) { 270 struct trace_sched_switch *t = trace[i].event_data; 271 if (!rt_tid(t->next_pid)) 272 continue; 273 if (events[event_idx].event_type != 274 TRACE_RECORD_SCHED_SWITCH || 275 events[event_idx].event_data != 276 t->next_pid) { 277 printf("Test case failed, expecting event " 278 "index %d type %d for tid %d, " 279 "got sched switch to tid %d\n", 280 event_idx, 281 events[event_idx].event_type, 282 events[event_idx].event_data, 283 t->next_pid); 284 return -1; 285 } 286 event_idx++; 287 } 288 289 if (trace[i].event_type == TRACE_RECORD_SCHED_WAKEUP) { 290 struct trace_sched_wakeup *t = trace[i].event_data; 291 if (!rt_tid(t->pid)) 292 continue; 293 if (events[event_idx].event_type != 294 TRACE_RECORD_SCHED_WAKEUP || 295 events[event_idx].event_data != 296 t->pid) { 297 printf("Test case failed, expecting event " 298 "index %d type %d for tid %d, " 299 "got sched wakeup to tid %d\n", 300 event_idx, 301 events[event_idx].event_type, 302 events[event_idx].event_data, 303 t->pid); 304 return -1; 305 } 306 event_idx++; 307 } 308 309 if (event_idx == events_size) 310 break; 311 } 312 313 if (event_idx != events_size) { 314 printf("Test case failed, " 315 "did not complete all expected events.\n"); 316 printf("Next expected event: event type %d for tid %d\n", 317 events[event_idx].event_type, 318 events[event_idx].event_data); 319 return -1; 320 } 321 322 return 0; 323 } 324 325 static void create_rt_thread(int prio, void *fn, pthread_t *rt_thread) 326 { 327 pthread_attr_t rt_thread_attrs; 328 struct sched_param rt_thread_sched_params; 329 330 ERROR_CHECK(pthread_attr_init(&rt_thread_attrs)); 331 ERROR_CHECK(pthread_attr_setinheritsched(&rt_thread_attrs, 332 PTHREAD_EXPLICIT_SCHED)); 333 ERROR_CHECK(pthread_attr_setschedpolicy(&rt_thread_attrs, 334 SCHED_FIFO)); 335 rt_thread_sched_params.sched_priority = prio; 336 ERROR_CHECK(pthread_attr_setschedparam(&rt_thread_attrs, 337 &rt_thread_sched_params)); 338 339 SAFE_PTHREAD_CREATE(rt_thread, &rt_thread_attrs, fn, NULL); 340 } 341 342 static void run(void) 343 { 344 pthread_t rt_low_a, rt_low_b; 345 pthread_t rt_med_a, rt_med_b; 346 pthread_t rt_high_a, rt_high_b; 347 348 sem_init(&sem_high_b, 0, 0); 349 sem_init(&sem_high_a, 0, 0); 350 sem_init(&sem_med_b, 0, 0); 351 sem_init(&sem_med_a, 0, 0); 352 sem_init(&sem_low_b, 0, 0); 353 sem_init(&sem_low_a, 0, 0); 354 355 /* configure and enable tracing */ 356 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 357 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384"); 358 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS); 359 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n"); 360 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1"); 361 362 create_rt_thread(70, rt_low_fn_a, &rt_low_a); 363 create_rt_thread(70, rt_low_fn_b, &rt_low_b); 364 create_rt_thread(75, rt_med_fn_a, &rt_med_a); 365 create_rt_thread(75, rt_med_fn_b, &rt_med_b); 366 create_rt_thread(80, rt_high_fn_a, &rt_high_a); 367 create_rt_thread(80, rt_high_fn_b, &rt_high_b); 368 369 SAFE_PTHREAD_JOIN(rt_low_a, NULL); 370 SAFE_PTHREAD_JOIN(rt_low_b, NULL); 371 SAFE_PTHREAD_JOIN(rt_med_a, NULL); 372 SAFE_PTHREAD_JOIN(rt_med_b, NULL); 373 SAFE_PTHREAD_JOIN(rt_high_a, NULL); 374 SAFE_PTHREAD_JOIN(rt_high_b, NULL); 375 376 /* disable tracing */ 377 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0"); 378 LOAD_TRACE(); 379 380 if (parse_results()) 381 tst_res(TFAIL, "RT FIFO tasks did not execute in the expected " 382 "pattern.\n"); 383 else 384 tst_res(TPASS, "RT FIFO tasks executed in the expected " 385 "pattern.\n"); 386 } 387 388 static struct tst_test test = { 389 .test_all = run, 390 .cleanup = trace_cleanup, 391 }; 392