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