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 23 static int dma_latency_fd = -1; 24 25 /* 26 * timerlat_apply_config - apply common configs to the initialized tool 27 */ 28 int 29 timerlat_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, ¶ms->common); 103 104 out_err: 105 return -1; 106 } 107 108 int 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, ¶ms->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 190 void 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 213 void 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, ¶ms->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 235 static 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 258 int 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 286 usage: 287 timerlat_usage(1); 288 exit(1); 289 } 290