• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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