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 <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 24 static int dma_latency_fd = -1; 25 26 /* 27 * timerlat_apply_config - apply common configs to the initialized tool 28 */ 29 int 30 timerlat_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, ¶ms->common); 115 116 out_err: 117 return -1; 118 } 119 120 int 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, ¶ms->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 210 void 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 233 void 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, ¶ms->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 277 int 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 305 usage: 306 timerlat_usage(129); 307 } 308