aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel/sched/eas/sched_latency_rt.c
blob: 68fb45a2623f0f6f70c49c7b8da6ac33fe5bd804 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * Copyright (c) 2018 Google, Inc.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * A CFS task and an RT task are created and affined to the same CPU. The CFS
 * task is a CPU hog. The latency for the RT task to execute after it has been
 * woken is measured and verified.
 */

#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <time.h>

#include "tst_test.h"
#include "tst_safe_file_ops.h"
#include "tst_safe_pthread.h"

#include "trace_parse.h"
#include "util.h"

#define TRACE_EVENTS "sched_wakeup sched_switch"

#define MAX_EXEC_LATENCY_US 100

static int rt_task_tid;
static sem_t sem;

static void *rt_fn(void *arg LTP_ATTRIBUTE_UNUSED)
{
	rt_task_tid = gettid();
	affine(0);
	sem_wait(&sem);
	return NULL;
}

#define BURN_MSEC 1000
static void *cfs_fn(void *arg LTP_ATTRIBUTE_UNUSED)
{
	affine(0);

	usleep(5000);
	tracefs_write("trace_marker", "WAKING");
	sem_post(&sem);

	burn(USEC_PER_SEC, 0);
	return NULL;
}

static int parse_results(void)
{
	int i;
	unsigned long long rt_wakeup_ts_us = 0;
	unsigned long long rt_exec_ts_us = 0;
	unsigned long rt_exec_latency_us;

	for (i = 0; i < num_trace_records; i++) {
		if (trace[i].event_type == TRACE_RECORD_SCHED_WAKEUP) {
			struct trace_sched_wakeup *t = trace[i].event_data;
			if (t->pid != rt_task_tid)
				continue;
			rt_wakeup_ts_us = TS_TO_USEC(trace[i].ts);
			continue;
		}
		if (rt_wakeup_ts_us &&
		    trace[i].event_type == TRACE_RECORD_SCHED_SWITCH) {
			struct trace_sched_switch *t = trace[i].event_data;
			if (t->next_pid != rt_task_tid)
				continue;
			if (!rt_wakeup_ts_us) {
				printf("RT task woke without being woken!\n");
				return -1;
			}
			rt_exec_ts_us = TS_TO_USEC(trace[i].ts);
			break;
		}
	}
	if (!rt_wakeup_ts_us || !rt_exec_ts_us) {
		printf("RT task either wasn't woken or didn't wake up.\n");
		return -1;
	}
	rt_exec_latency_us = rt_exec_ts_us - rt_wakeup_ts_us;
	printf("RT exec latency: %ld usec\n", rt_exec_latency_us);
	return (rt_exec_latency_us > MAX_EXEC_LATENCY_US);
}

static void run(void)
{
	pthread_t cfs_thread;
	pthread_t rt_thread;
	pthread_attr_t cfs_thread_attrs;
	pthread_attr_t rt_thread_attrs;
	struct sched_param cfs_thread_sched_params;
	struct sched_param rt_thread_sched_params;

	ERROR_CHECK(pthread_attr_init(&cfs_thread_attrs));
	ERROR_CHECK(pthread_attr_setinheritsched(&cfs_thread_attrs,
						 PTHREAD_EXPLICIT_SCHED));
	ERROR_CHECK(pthread_attr_setschedpolicy(&cfs_thread_attrs,
						SCHED_OTHER));
	cfs_thread_sched_params.sched_priority = 0;
	ERROR_CHECK(pthread_attr_setschedparam(&cfs_thread_attrs,
					       &cfs_thread_sched_params));

	ERROR_CHECK(pthread_attr_init(&rt_thread_attrs));
	ERROR_CHECK(pthread_attr_setinheritsched(&rt_thread_attrs,
						 PTHREAD_EXPLICIT_SCHED));
	ERROR_CHECK(pthread_attr_setschedpolicy(&rt_thread_attrs,
						SCHED_FIFO));
	rt_thread_sched_params.sched_priority = 80;
	ERROR_CHECK(pthread_attr_setschedparam(&rt_thread_attrs,
					       &rt_thread_sched_params));

	sem_init(&sem, 0, 0);

	/* configure and enable tracing */
	tracefs_write("tracing_on", "0");
	tracefs_write("buffer_size_kb", "16384");
	tracefs_write("set_event", TRACE_EVENTS);
	tracefs_write("trace", "\n");
	tracefs_write("tracing_on", "1");

	SAFE_PTHREAD_CREATE(&cfs_thread, &cfs_thread_attrs, cfs_fn, NULL);
	SAFE_PTHREAD_CREATE(&rt_thread, &rt_thread_attrs, rt_fn, NULL);
	SAFE_PTHREAD_JOIN(cfs_thread, NULL);
	SAFE_PTHREAD_JOIN(rt_thread, NULL);

	/* disable tracing */
	tracefs_write("tracing_on", "0");
	LOAD_TRACE();

	if (parse_results())
		tst_res(TFAIL, "RT task did not execute within required "
			"latency of %d usec.\n", MAX_EXEC_LATENCY_US);
	else
		tst_res(TPASS, "RT task executed within required latency "
			"of %d usec..\n", MAX_EXEC_LATENCY_US);
}

static struct tst_test test = {
	.test_all = run,
	.setup = trace_setup,
	.cleanup = trace_cleanup,
};