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  * 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