1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
4 */
5 #define _GNU_SOURCE
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <pthread.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <sched.h>
15
16 #include "timerlat.h"
17 #include "timerlat_aa.h"
18 #include "timerlat_bpf.h"
19
20 #define DEFAULT_TIMERLAT_PERIOD 1000 /* 1ms */
21
22 static int dma_latency_fd = -1;
23
24 /*
25 * timerlat_apply_config - apply common configs to the initialized tool
26 */
27 int
timerlat_apply_config(struct osnoise_tool * tool,struct timerlat_params * params)28 timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
29 {
30 int retval;
31
32 /*
33 * Try to enable BPF, unless disabled explicitly.
34 * If BPF enablement fails, fall back to tracefs mode.
35 */
36 if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) {
37 debug_msg("RTLA_NO_BPF set, disabling BPF\n");
38 params->mode = TRACING_MODE_TRACEFS;
39 } else if (!tep_find_event_by_name(tool->trace.tep, "osnoise", "timerlat_sample")) {
40 debug_msg("osnoise:timerlat_sample missing, disabling BPF\n");
41 params->mode = TRACING_MODE_TRACEFS;
42 } else {
43 retval = timerlat_bpf_init(params);
44 if (retval) {
45 debug_msg("Could not enable BPF\n");
46 params->mode = TRACING_MODE_TRACEFS;
47 }
48 }
49
50 /* Check if BPF action program is requested but BPF is not available */
51 if (params->bpf_action_program) {
52 if (params->mode == TRACING_MODE_TRACEFS) {
53 err_msg("BPF actions are not supported in tracefs-only mode\n");
54 goto out_err;
55 }
56
57 if (timerlat_load_bpf_action_program(params->bpf_action_program))
58 goto out_err;
59 }
60
61 retval = osnoise_set_timerlat_period_us(tool->context,
62 params->timerlat_period_us ?
63 params->timerlat_period_us :
64 DEFAULT_TIMERLAT_PERIOD);
65 if (retval) {
66 err_msg("Failed to set timerlat period\n");
67 goto out_err;
68 }
69
70
71 retval = osnoise_set_print_stack(tool->context, params->print_stack);
72 if (retval) {
73 err_msg("Failed to set print stack\n");
74 goto out_err;
75 }
76
77 /*
78 * If the user did not specify a type of thread, try user-threads first.
79 * Fall back to kernel threads otherwise.
80 */
81 if (!params->common.kernel_workload && !params->common.user_data) {
82 retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd");
83 if (retval) {
84 debug_msg("User-space interface detected, setting user-threads\n");
85 params->common.user_workload = 1;
86 params->common.user_data = 1;
87 } else {
88 debug_msg("User-space interface not detected, setting kernel-threads\n");
89 params->common.kernel_workload = 1;
90 }
91 }
92
93 return common_apply_config(tool, ¶ms->common);
94
95 out_err:
96 return -1;
97 }
98
timerlat_enable(struct osnoise_tool * tool)99 int timerlat_enable(struct osnoise_tool *tool)
100 {
101 struct timerlat_params *params = to_timerlat_params(tool->params);
102 int retval, nr_cpus, i;
103
104 if (params->dma_latency >= 0) {
105 dma_latency_fd = set_cpu_dma_latency(params->dma_latency);
106 if (dma_latency_fd < 0) {
107 err_msg("Could not set /dev/cpu_dma_latency.\n");
108 return -1;
109 }
110 }
111
112 if (params->deepest_idle_state >= -1) {
113 if (!have_libcpupower_support()) {
114 err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n");
115 return -1;
116 }
117
118 nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
119
120 for_each_monitored_cpu(i, nr_cpus, ¶ms->common) {
121 if (save_cpu_idle_disable_state(i) < 0) {
122 err_msg("Could not save cpu idle state.\n");
123 return -1;
124 }
125 if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) {
126 err_msg("Could not set deepest cpu idle state.\n");
127 return -1;
128 }
129 }
130 }
131
132 if (!params->no_aa) {
133 tool->aa = osnoise_init_tool("timerlat_aa");
134 if (!tool->aa)
135 return -1;
136
137 retval = timerlat_aa_init(tool->aa, params->dump_tasks);
138 if (retval) {
139 err_msg("Failed to enable the auto analysis instance\n");
140 return retval;
141 }
142
143 retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat");
144 if (retval) {
145 err_msg("Failed to enable aa tracer\n");
146 return retval;
147 }
148 }
149
150 if (params->common.warmup > 0) {
151 debug_msg("Warming up for %d seconds\n", params->common.warmup);
152 sleep(params->common.warmup);
153 if (stop_tracing)
154 return -1;
155 }
156
157 /*
158 * Start the tracers here, after having set all instances.
159 *
160 * Let the trace instance start first for the case of hitting a stop
161 * tracing while enabling other instances. The trace instance is the
162 * one with most valuable information.
163 */
164 if (tool->record)
165 trace_instance_start(&tool->record->trace);
166 if (!params->no_aa)
167 trace_instance_start(&tool->aa->trace);
168 if (params->mode == TRACING_MODE_TRACEFS) {
169 trace_instance_start(&tool->trace);
170 } else {
171 retval = timerlat_bpf_attach();
172 if (retval) {
173 err_msg("Error attaching BPF program\n");
174 return retval;
175 }
176 }
177
178 /*
179 * In tracefs and mixed mode, timerlat tracer handles stopping
180 * on threshold
181 */
182 if (params->mode != TRACING_MODE_BPF) {
183 retval = osn_set_stop(tool);
184 if (retval)
185 return retval;
186 }
187
188 return 0;
189 }
190
timerlat_analyze(struct osnoise_tool * tool,bool stopped)191 void timerlat_analyze(struct osnoise_tool *tool, bool stopped)
192 {
193 struct timerlat_params *params = to_timerlat_params(tool->params);
194
195 if (stopped) {
196 if (!params->no_aa)
197 timerlat_auto_analysis(params->common.stop_us,
198 params->common.stop_total_us);
199 } else if (params->common.aa_only) {
200 char *max_lat;
201
202 /*
203 * If the trace did not stop with --aa-only, at least print
204 * the max known latency.
205 */
206 max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL);
207 if (max_lat) {
208 printf(" Max latency was %s\n", max_lat);
209 free(max_lat);
210 }
211 }
212 }
213
timerlat_free(struct osnoise_tool * tool)214 void timerlat_free(struct osnoise_tool *tool)
215 {
216 struct timerlat_params *params = to_timerlat_params(tool->params);
217 int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
218 int i;
219
220 timerlat_aa_destroy();
221 if (dma_latency_fd >= 0)
222 close(dma_latency_fd);
223 if (params->deepest_idle_state >= -1) {
224 for_each_monitored_cpu(i, nr_cpus, ¶ms->common) {
225 restore_cpu_idle_disable_state(i);
226 }
227 }
228
229 osnoise_destroy_tool(tool->aa);
230
231 if (params->mode != TRACING_MODE_TRACEFS)
232 timerlat_bpf_destroy();
233 free_cpu_idle_disable_states();
234 }
235
timerlat_usage(int err)236 static void timerlat_usage(int err)
237 {
238 int i;
239
240 static const char * const msg[] = {
241 "",
242 "timerlat version " VERSION,
243 "",
244 " usage: [rtla] timerlat [MODE] ...",
245 "",
246 " modes:",
247 " top - prints the summary from timerlat tracer",
248 " hist - prints a histogram of timer latencies",
249 "",
250 "if no MODE is given, the top mode is called, passing the arguments",
251 NULL,
252 };
253
254 for (i = 0; msg[i]; i++)
255 fprintf(stderr, "%s\n", msg[i]);
256 exit(err);
257 }
258
timerlat_main(int argc,char * argv[])259 int timerlat_main(int argc, char *argv[])
260 {
261 if (argc == 0)
262 goto usage;
263
264 /*
265 * if timerlat was called without any argument, run the
266 * default cmdline.
267 */
268 if (argc == 1) {
269 run_tool(&timerlat_top_ops, argc, argv);
270 exit(0);
271 }
272
273 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
274 timerlat_usage(0);
275 } else if (strncmp(argv[1], "-", 1) == 0) {
276 /* the user skipped the tool, call the default one */
277 run_tool(&timerlat_top_ops, argc, argv);
278 exit(0);
279 } else if (strcmp(argv[1], "top") == 0) {
280 run_tool(&timerlat_top_ops, argc-1, &argv[1]);
281 exit(0);
282 } else if (strcmp(argv[1], "hist") == 0) {
283 run_tool(&timerlat_hist_ops, argc-1, &argv[1]);
284 exit(0);
285 }
286
287 usage:
288 timerlat_usage(1);
289 exit(1);
290 }
291