1 /*
2 * Copyright (c) 2018 Google, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * A DL task runs. Its execution pattern is checked to see that the constraints
7 * it has been given (runtime, period, deadline) are satisfied.
8 */
9
10 #define _GNU_SOURCE
11 #include <errno.h>
12 #include <pthread.h>
13 #include <sched.h>
14 #include <semaphore.h>
15 #include <time.h>
16
17 #include "tst_test.h"
18 #include "tst_safe_file_ops.h"
19 #include "tst_safe_pthread.h"
20
21 #include "lapi/syscalls.h"
22 #include "lapi/sched.h"
23 #include "trace_parse.h"
24 #include "util.h"
25
26 #define TRACE_EVENTS "sched_switch"
27
28 static int dl_task_tid;
29
30 /*
31 * It is not possible to restrict CPU affinity on SCHED_DEADLINE tasks, so we do
32 * not force the CFS and DL tasks to be on the same CPU in this test. CPUsets
33 * can be used to do this, perhaps implement that later.
34 */
35
dl_fn(void * arg LTP_ATTRIBUTE_UNUSED)36 static void *dl_fn(void *arg LTP_ATTRIBUTE_UNUSED)
37 {
38 struct sched_attr attr;
39 struct timespec ts;
40 uint64_t now_usec, end_usec;
41
42 attr.size = sizeof(attr);
43 attr.sched_flags = 0;
44 attr.sched_nice = 0;
45 attr.sched_priority = 0;
46
47 attr.sched_policy = SCHED_DEADLINE;
48 attr.sched_runtime = 5000000;
49 attr.sched_period = 20000000;
50 attr.sched_deadline = 10000000;
51
52 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "DL START");
53 ERROR_CHECK(sched_setattr(0, &attr, 0));
54
55 dl_task_tid = gettid();
56
57 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
58 printf("clock_gettime() reported an error\n");
59 return NULL;
60 }
61 now_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000);
62 end_usec = now_usec + 3 * USEC_PER_SEC;
63 while (now_usec < end_usec) {
64 /* Run for 5ms */
65 burn(5000, 0);
66
67 /* Wait until next 20ms boundary. sched_yield() for DL tasks
68 * throttles the task until its next period. */
69 sched_yield();
70 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
71 printf("clock_gettime() reported an error\n");
72 return NULL;
73 }
74 now_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000);
75 }
76
77 return NULL;
78 }
79
parse_results(void)80 static int parse_results(void)
81 {
82 int i;
83 unsigned long long next_period_ts_us = 0;
84 unsigned long long next_deadline_ts_us = 0;
85 unsigned long long start_ts_us = 0;
86 unsigned long long period_exec_time_us = 0;
87 int periods_parsed = 0;
88
89 for (i = 0; i < num_trace_records; i++) {
90 if (trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) {
91 struct trace_sched_switch *t = trace[i].event_data;
92 if (t->prev_pid == dl_task_tid) {
93 unsigned long long end_ts_us;
94 if (!start_ts_us) {
95 printf("Trace parse error, "
96 "start_ts_us = 0\n");
97 return -1;
98 }
99 if (TS_TO_USEC(trace[i].ts) >
100 next_period_ts_us) {
101 printf("Task ran past end of "
102 "period!\n");
103 return -1;
104 }
105 end_ts_us = TS_TO_USEC(trace[i].ts);
106 if (end_ts_us > next_deadline_ts_us)
107 end_ts_us = next_deadline_ts_us;
108 if (start_ts_us > next_deadline_ts_us)
109 start_ts_us = next_deadline_ts_us;
110 period_exec_time_us +=
111 (end_ts_us - start_ts_us);
112 start_ts_us = 0;
113 }
114 }
115 if (next_period_ts_us &&
116 TS_TO_USEC(trace[i].ts) > next_period_ts_us) {
117 if (start_ts_us) {
118 printf("Task was running across period boundary!\n");
119 return -1;
120 }
121 if (period_exec_time_us < 5000) {
122 printf("Missed deadline at %llu!\n",
123 next_deadline_ts_us);
124 return -1;
125 }
126 periods_parsed++;
127 next_deadline_ts_us += 20000;
128 next_period_ts_us += 20000;
129 }
130 if (trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) {
131 struct trace_sched_switch *t = trace[i].event_data;
132 if (t->next_pid == dl_task_tid) {
133
134 if (start_ts_us) {
135 printf("Trace parse error, "
136 "start_ts_us != 0\n");
137 return -1;
138 }
139 start_ts_us = TS_TO_USEC(trace[i].ts);
140 if (!next_period_ts_us) {
141 /*
142 * Initialize period and deadline the
143 * first time the DL task runs.
144 */
145 next_period_ts_us = start_ts_us + 20000;
146 next_deadline_ts_us = start_ts_us +
147 10000;
148 }
149 }
150 }
151 }
152 if (periods_parsed >= 149)
153 printf("%d periods parsed successfully.\n", periods_parsed);
154 else
155 printf("Only %d periods parsed successfully.\n",
156 periods_parsed);
157 return 0;
158 }
159
run(void)160 static void run(void)
161 {
162 pthread_t dl_thread;
163 pthread_attr_t dl_thread_attrs;
164 struct sched_param dl_thread_sched_params;
165
166 ERROR_CHECK(pthread_attr_init(&dl_thread_attrs));
167 ERROR_CHECK(pthread_attr_setinheritsched(&dl_thread_attrs,
168 PTHREAD_EXPLICIT_SCHED));
169 ERROR_CHECK(pthread_attr_setschedpolicy(&dl_thread_attrs,
170 SCHED_FIFO));
171 dl_thread_sched_params.sched_priority = 80;
172 ERROR_CHECK(pthread_attr_setschedparam(&dl_thread_attrs,
173 &dl_thread_sched_params));
174
175 /* configure and enable tracing */
176 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
177 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
178 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
179 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
180 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
181
182 SAFE_PTHREAD_CREATE(&dl_thread, NULL, dl_fn, NULL);
183 SAFE_PTHREAD_JOIN(dl_thread, NULL);
184
185 /* disable tracing */
186 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
187 LOAD_TRACE();
188
189 if (parse_results())
190 tst_res(TFAIL, "DL task did not execute as expected.\n");
191 else
192 tst_res(TPASS, "DL task ran as expected.\n");
193 }
194
195 static struct tst_test test = {
196 .test_all = run,
197 .cleanup = trace_cleanup,
198 };
199