Home | History | Annotate | Download | only in eas
      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