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 32
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
rt_b_fn(void * arg LTP_ATTRIBUTE_UNUSED)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 
rt_c_fn(void * arg LTP_ATTRIBUTE_UNUSED)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 
rt_a_fn(void * arg LTP_ATTRIBUTE_UNUSED)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 	tracefs_write("trace_marker", "TEST START");
62 	sem_post(&sem);
63 	sem_post(&sem);
64 	burn(BUSY_WAIT_USECS, 0);
65 	return NULL;
66 }
67 
parse_results(void)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 
create_rt_thread(int prio,void * fn,pthread_t * rt_thread)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
run(void)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 	tracefs_write("tracing_on", "0");
151 	tracefs_write("buffer_size_kb", "16384");
152 	tracefs_write("set_event", TRACE_EVENTS);
153 	tracefs_write("trace", "\n");
154 	tracefs_write("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 	tracefs_write("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 	.setup = trace_setup,
179 	.cleanup = trace_cleanup,
180 };
181