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 /* 81 * If the user did not specify a type of thread, try user-threads first. 82 * Fall back to kernel threads otherwise. 83 */ 84 if (!params->common.kernel_workload && !params->common.user_data) { 85 retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd"); 86 if (retval) { 87 debug_msg("User-space interface detected, setting user-threads\n"); 88 params->common.user_workload = 1; 89 params->common.user_data = 1; 90 } else { 91 debug_msg("User-space interface not detected, setting kernel-threads\n"); 92 params->common.kernel_workload = 1; 93 } 94 } 95 96 return common_apply_config(tool, ¶ms->common); 97 98 out_err: 99 return -1; 100 } 101 102 int timerlat_enable(struct osnoise_tool *tool) 103 { 104 struct timerlat_params *params = to_timerlat_params(tool->params); 105 int retval, i; 106 107 if (params->dma_latency >= 0) { 108 dma_latency_fd = set_cpu_dma_latency(params->dma_latency); 109 if (dma_latency_fd < 0) { 110 err_msg("Could not set /dev/cpu_dma_latency.\n"); 111 return -1; 112 } 113 } 114 115 if (params->deepest_idle_state >= -1) { 116 if (!have_libcpupower_support()) { 117 err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n"); 118 return -1; 119 } 120 121 for_each_monitored_cpu(i, ¶ms->common) { 122 if (save_cpu_idle_disable_state(i) < 0) { 123 err_msg("Could not save cpu idle state.\n"); 124 return -1; 125 } 126 if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) { 127 err_msg("Could not set deepest cpu idle state.\n"); 128 return -1; 129 } 130 } 131 } 132 133 if (!params->no_aa) { 134 tool->aa = osnoise_init_tool("timerlat_aa"); 135 if (!tool->aa) 136 return -1; 137 138 retval = timerlat_aa_init(tool->aa, params->dump_tasks, params->stack_format); 139 if (retval) { 140 err_msg("Failed to enable the auto analysis instance\n"); 141 return retval; 142 } 143 144 retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat"); 145 if (retval) { 146 err_msg("Failed to enable aa tracer\n"); 147 return retval; 148 } 149 } 150 151 if (params->common.warmup > 0) { 152 debug_msg("Warming up for %d seconds\n", params->common.warmup); 153 sleep(params->common.warmup); 154 if (stop_tracing) 155 return -1; 156 } 157 158 /* 159 * Start the tracers here, after having set all instances. 160 * 161 * Let the trace instance start first for the case of hitting a stop 162 * tracing while enabling other instances. The trace instance is the 163 * one with most valuable information. 164 */ 165 if (tool->record) 166 trace_instance_start(&tool->record->trace); 167 if (!params->no_aa) 168 trace_instance_start(&tool->aa->trace); 169 if (params->mode == TRACING_MODE_TRACEFS) { 170 trace_instance_start(&tool->trace); 171 } else { 172 retval = timerlat_bpf_attach(); 173 if (retval) { 174 err_msg("Error attaching BPF program\n"); 175 return retval; 176 } 177 } 178 179 /* 180 * In tracefs and mixed mode, timerlat tracer handles stopping 181 * on threshold 182 */ 183 if (params->mode != TRACING_MODE_BPF) { 184 retval = osn_set_stop(tool); 185 if (retval) 186 return retval; 187 } 188 189 return 0; 190 } 191 192 void timerlat_analyze(struct osnoise_tool *tool, bool stopped) 193 { 194 struct timerlat_params *params = to_timerlat_params(tool->params); 195 196 if (stopped) { 197 if (!params->no_aa) 198 timerlat_auto_analysis(params->common.stop_us, 199 params->common.stop_total_us); 200 } else if (params->common.aa_only) { 201 char *max_lat; 202 203 /* 204 * If the trace did not stop with --aa-only, at least print 205 * the max known latency. 206 */ 207 max_lat = tracefs_instance_file_read(tool->trace.inst, "tracing_max_latency", NULL); 208 if (max_lat) { 209 printf(" Max latency was %s\n", max_lat); 210 free(max_lat); 211 } 212 } 213 } 214 215 void timerlat_free(struct osnoise_tool *tool) 216 { 217 struct timerlat_params *params = to_timerlat_params(tool->params); 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, ¶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 236 __noreturn 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 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(129); 275 } else if (str_has_prefix(argv[1], "-")) { 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(129); 289 } 290