projects/linux·blob master · refs

307 lines · 7.6 KiB

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