File Index
Files and directories
Sign in
FILE

timerlat.c

File preview with syntax highlighting.

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