1 /*
2 * Copyright (c) 2018 Google, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 *
6 * A DL task and a CFS task are created and affined to the same CPU. The CFS
7 * task is a CPU hog. The latency to switch to the DL task (which should preempt
8 * the CFS task immediately) is checked.
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 "lapi/syscalls.h"
23 #include "lapi/sched.h"
24 #include "trace_parse.h"
25 #include "util.h"
26
27 #define TRACE_EVENTS "sched_wakeup sched_switch"
28
29 #define MAX_EXEC_LATENCY_US 100
30
31 static int dl_task_tid;
32 static sem_t sem;
33
34 /*
35 * It is not possible to restrict CPU affinity on SCHED_DEADLINE tasks, so we do
36 * not force the CFS and DL tasks to be on the same CPU in this test. CPUsets
37 * can be used to do this, perhaps implement that later.
38 */
39
dl_fn(void * arg LTP_ATTRIBUTE_UNUSED)40 static void *dl_fn(void *arg LTP_ATTRIBUTE_UNUSED)
41 {
42 struct sched_attr attr;
43
44 attr.size = sizeof(attr);
45 attr.sched_flags = 0;
46 attr.sched_nice = 0;
47 attr.sched_priority = 0;
48
49 attr.sched_policy = SCHED_DEADLINE;
50 attr.sched_runtime = 10000000;
51 attr.sched_period = 30000000;
52 attr.sched_deadline = 30000000;
53
54 ERROR_CHECK(sched_setattr(0, &attr, 0));
55
56 dl_task_tid = gettid();
57 sem_wait(&sem);
58 return NULL;
59 }
60
cfs_fn(void * arg LTP_ATTRIBUTE_UNUSED)61 static void *cfs_fn(void *arg LTP_ATTRIBUTE_UNUSED)
62 {
63 usleep(5000);
64 SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "WAKING");
65 sem_post(&sem);
66
67 burn(USEC_PER_SEC, 0);
68 return NULL;
69 }
70
parse_results(void)71 static int parse_results(void)
72 {
73 int i;
74 unsigned long long dl_wakeup_ts_us = 0;
75 unsigned long long dl_exec_ts_us = 0;
76 unsigned long dl_exec_latency_us;
77
78 for (i = 0; i < num_trace_records; i++) {
79 if (trace[i].event_type == TRACE_RECORD_SCHED_WAKEUP) {
80 struct trace_sched_wakeup *t = trace[i].event_data;
81 if (t->pid != dl_task_tid)
82 continue;
83 dl_wakeup_ts_us = TS_TO_USEC(trace[i].ts);
84 continue;
85 }
86 if (dl_wakeup_ts_us &&
87 trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) {
88 struct trace_sched_switch *t = trace[i].event_data;
89 if (t->next_pid != dl_task_tid)
90 continue;
91 if (!dl_wakeup_ts_us) {
92 printf("DL task woke without being woken!\n");
93 return -1;
94 }
95 dl_exec_ts_us = TS_TO_USEC(trace[i].ts);
96 break;
97 }
98 }
99 if (!dl_wakeup_ts_us || !dl_exec_ts_us) {
100 printf("DL task either wasn't woken or didn't wake up.\n");
101 return -1;
102 }
103 dl_exec_latency_us = dl_exec_ts_us - dl_wakeup_ts_us;
104 printf("DL exec latency: %ld usec\n", dl_exec_latency_us);
105 return (dl_exec_latency_us > MAX_EXEC_LATENCY_US);
106 }
107
run(void)108 static void run(void)
109 {
110 pthread_t cfs_thread;
111 pthread_t dl_thread;
112 pthread_attr_t cfs_thread_attrs;
113 pthread_attr_t dl_thread_attrs;
114 struct sched_param cfs_thread_sched_params;
115 struct sched_param dl_thread_sched_params;
116
117 ERROR_CHECK(pthread_attr_init(&cfs_thread_attrs));
118 ERROR_CHECK(pthread_attr_setinheritsched(&cfs_thread_attrs,
119 PTHREAD_EXPLICIT_SCHED));
120 ERROR_CHECK(pthread_attr_setschedpolicy(&cfs_thread_attrs,
121 SCHED_OTHER));
122 cfs_thread_sched_params.sched_priority = 0;
123 ERROR_CHECK(pthread_attr_setschedparam(&cfs_thread_attrs,
124 &cfs_thread_sched_params));
125
126 ERROR_CHECK(pthread_attr_init(&dl_thread_attrs));
127 ERROR_CHECK(pthread_attr_setinheritsched(&dl_thread_attrs,
128 PTHREAD_EXPLICIT_SCHED));
129 ERROR_CHECK(pthread_attr_setschedpolicy(&dl_thread_attrs,
130 SCHED_FIFO));
131 dl_thread_sched_params.sched_priority = 80;
132 ERROR_CHECK(pthread_attr_setschedparam(&dl_thread_attrs,
133 &dl_thread_sched_params));
134
135 sem_init(&sem, 0, 0);
136
137 /* configure and enable tracing */
138 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
139 SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
140 SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
141 SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
142 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
143
144 SAFE_PTHREAD_CREATE(&cfs_thread, &cfs_thread_attrs, cfs_fn, NULL);
145 SAFE_PTHREAD_CREATE(&dl_thread, &dl_thread_attrs, dl_fn, NULL);
146 SAFE_PTHREAD_JOIN(cfs_thread, NULL);
147 SAFE_PTHREAD_JOIN(dl_thread, NULL);
148
149 /* disable tracing */
150 SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
151 LOAD_TRACE();
152
153 if (parse_results())
154 tst_res(TFAIL, "DL task did not execute within expected "
155 "latency of %d usec.\n", MAX_EXEC_LATENCY_US);
156 else
157 tst_res(TPASS, "DL task executed within expected latency "
158 "of %d usec.\n", MAX_EXEC_LATENCY_US);
159 }
160
161 static struct tst_test test = {
162 .test_all = run,
163 .cleanup = trace_cleanup,
164 };
165