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