1 /*
2  * Copyright (c) 2018 Google, Inc.
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  *
6  * A CFS task and an RT task are created and affined to the same CPU. The CFS
7  * task is a CPU hog. The latency for the RT task to execute after it has been
8  * woken is measured and verified.
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"
26 
27 #define MAX_EXEC_LATENCY_US 100
28 
29 static int rt_task_tid;
30 static sem_t sem;
31 
rt_fn(void * arg LTP_ATTRIBUTE_UNUSED)32 static void *rt_fn(void *arg LTP_ATTRIBUTE_UNUSED)
33 {
34 	rt_task_tid = gettid();
35 	affine(0);
36 	sem_wait(&sem);
37 	return NULL;
38 }
39 
40 #define BURN_MSEC 1000
cfs_fn(void * arg LTP_ATTRIBUTE_UNUSED)41 static void *cfs_fn(void *arg LTP_ATTRIBUTE_UNUSED)
42 {
43 	affine(0);
44 
45 	usleep(5000);
46 	SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "WAKING");
47 	sem_post(&sem);
48 
49 	burn(USEC_PER_SEC, 0);
50 	return NULL;
51 }
52 
parse_results(void)53 static int parse_results(void)
54 {
55 	int i;
56 	unsigned long long rt_wakeup_ts_us = 0;
57 	unsigned long long rt_exec_ts_us = 0;
58 	unsigned long rt_exec_latency_us;
59 
60 	for (i = 0; i < num_trace_records; i++) {
61 		if (trace[i].event_type == TRACE_RECORD_SCHED_WAKEUP) {
62 			struct trace_sched_wakeup *t = trace[i].event_data;
63 			if (t->pid != rt_task_tid)
64 				continue;
65 			rt_wakeup_ts_us = TS_TO_USEC(trace[i].ts);
66 			continue;
67 		}
68 		if (rt_wakeup_ts_us &&
69 		    trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) {
70 			struct trace_sched_switch *t = trace[i].event_data;
71 			if (t->next_pid != rt_task_tid)
72 				continue;
73 			if (!rt_wakeup_ts_us) {
74 				printf("RT task woke without being woken!\n");
75 				return -1;
76 			}
77 			rt_exec_ts_us = TS_TO_USEC(trace[i].ts);
78 			break;
79 		}
80 	}
81 	if (!rt_wakeup_ts_us || !rt_exec_ts_us) {
82 		printf("RT task either wasn't woken or didn't wake up.\n");
83 		return -1;
84 	}
85 	rt_exec_latency_us = rt_exec_ts_us - rt_wakeup_ts_us;
86 	printf("RT exec latency: %ld usec\n", rt_exec_latency_us);
87 	return (rt_exec_latency_us > MAX_EXEC_LATENCY_US);
88 }
89 
run(void)90 static void run(void)
91 {
92 	pthread_t cfs_thread;
93 	pthread_t rt_thread;
94 	pthread_attr_t cfs_thread_attrs;
95 	pthread_attr_t rt_thread_attrs;
96 	struct sched_param cfs_thread_sched_params;
97 	struct sched_param rt_thread_sched_params;
98 
99 	ERROR_CHECK(pthread_attr_init(&cfs_thread_attrs));
100 	ERROR_CHECK(pthread_attr_setinheritsched(&cfs_thread_attrs,
101 						 PTHREAD_EXPLICIT_SCHED));
102 	ERROR_CHECK(pthread_attr_setschedpolicy(&cfs_thread_attrs,
103 						SCHED_OTHER));
104 	cfs_thread_sched_params.sched_priority = 0;
105 	ERROR_CHECK(pthread_attr_setschedparam(&cfs_thread_attrs,
106 					       &cfs_thread_sched_params));
107 
108 	ERROR_CHECK(pthread_attr_init(&rt_thread_attrs));
109 	ERROR_CHECK(pthread_attr_setinheritsched(&rt_thread_attrs,
110 						 PTHREAD_EXPLICIT_SCHED));
111 	ERROR_CHECK(pthread_attr_setschedpolicy(&rt_thread_attrs,
112 						SCHED_FIFO));
113 	rt_thread_sched_params.sched_priority = 80;
114 	ERROR_CHECK(pthread_attr_setschedparam(&rt_thread_attrs,
115 					       &rt_thread_sched_params));
116 
117 	sem_init(&sem, 0, 0);
118 
119 	/* configure and enable tracing */
120 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
121 	SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
122 	SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
123 	SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
124 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
125 
126 	SAFE_PTHREAD_CREATE(&cfs_thread, &cfs_thread_attrs, cfs_fn, NULL);
127 	SAFE_PTHREAD_CREATE(&rt_thread, &rt_thread_attrs, rt_fn, NULL);
128 	SAFE_PTHREAD_JOIN(cfs_thread, NULL);
129 	SAFE_PTHREAD_JOIN(rt_thread, NULL);
130 
131 	/* disable tracing */
132 	SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
133 	LOAD_TRACE();
134 
135 	if (parse_results())
136 		tst_res(TFAIL, "RT task did not execute within required "
137 			"latency of %d usec.\n", MAX_EXEC_LATENCY_US);
138 	else
139 		tst_res(TPASS, "RT task executed within required latency "
140 			"of %d usec..\n", MAX_EXEC_LATENCY_US);
141 }
142 
143 static struct tst_test test = {
144 	.test_all = run,
145 	.cleanup = trace_cleanup,
146 };
147