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