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 (i = 0; i < nr_cpus; i++) { 130 if (params->common.cpus && !CPU_ISSET(i, ¶ms->common.monitored_cpus)) 131 continue; 132 if (save_cpu_idle_disable_state(i) < 0) { 133 err_msg("Could not save cpu idle state.\n"); 134 return -1; 135 } 136 if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) { 137 err_msg("Could not set deepest cpu idle state.\n"); 138 return -1; 139 } 140 } 141 } 142 143 if (!params->no_aa) { 144 tool->aa = osnoise_init_tool("timerlat_aa"); 145 if (!tool->aa) 146 return -1; 147 148 retval = timerlat_aa_init(tool->aa, params->dump_tasks); 149 if (retval) { 150 err_msg("Failed to enable the auto analysis instance\n"); 151 return retval; 152 } 153 154 retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat"); 155 if (retval) { 156 err_msg("Failed to enable aa tracer\n"); 157 return retval; 158 } 159 } 160 161 if (params->common.warmup > 0) { 162 debug_msg("Warming up for %d seconds\n", params->common.warmup); 163 sleep(params->common.warmup); 164 if (stop_tracing) 165 return -1; 166 } 167 168 /* 169 * Start the tracers here, after having set all instances. 170 * 171 * Let the trace instance start first for the case of hitting a stop 172 * tracing while enabling other instances. The trace instance is the 173 * one with most valuable information. 174 */ 175 if (tool->record) 176 trace_instance_start(&tool->record->trace); 177 if (!params->no_aa) 178 trace_instance_start(&tool->aa->trace); 179 if (params->mode == TRACING_MODE_TRACEFS) { 180 trace_instance_start(&tool->trace); 181 } else { 182 retval = timerlat_bpf_attach(); 183 if (retval) { 184 err_msg("Error attaching BPF program\n"); 185 return retval; 186 } 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(trace_inst->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 nr_cpus, 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 (i = 0; i < nr_cpus; i++) { 225 if (params->common.cpus && 226 !CPU_ISSET(i, ¶ms->common.monitored_cpus)) 227 continue; 228 restore_cpu_idle_disable_state(i); 229 } 230 } 231 232 osnoise_destroy_tool(tool->aa); 233 234 if (params->mode != TRACING_MODE_TRACEFS) 235 timerlat_bpf_destroy(); 236 free_cpu_idle_disable_states(); 237 } 238 239 static void timerlat_usage(int err) 240 { 241 int i; 242 243 static const char * const msg[] = { 244 "", 245 "timerlat version " VERSION, 246 "", 247 " usage: [rtla] timerlat [MODE] ...", 248 "", 249 " modes:", 250 " top - prints the summary from timerlat tracer", 251 " hist - prints a histogram of timer latencies", 252 "", 253 "if no MODE is given, the top mode is called, passing the arguments", 254 NULL, 255 }; 256 257 for (i = 0; msg[i]; i++) 258 fprintf(stderr, "%s\n", msg[i]); 259 exit(err); 260 } 261 262 int timerlat_main(int argc, char *argv[]) 263 { 264 if (argc == 0) 265 goto usage; 266 267 /* 268 * if timerlat was called without any argument, run the 269 * default cmdline. 270 */ 271 if (argc == 1) { 272 run_tool(&timerlat_top_ops, argc, argv); 273 exit(0); 274 } 275 276 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { 277 timerlat_usage(0); 278 } else if (strncmp(argv[1], "-", 1) == 0) { 279 /* the user skipped the tool, call the default one */ 280 run_tool(&timerlat_top_ops, argc, argv); 281 exit(0); 282 } else if (strcmp(argv[1], "top") == 0) { 283 run_tool(&timerlat_top_ops, argc-1, &argv[1]); 284 exit(0); 285 } else if (strcmp(argv[1], "hist") == 0) { 286 run_tool(&timerlat_hist_ops, argc-1, &argv[1]); 287 exit(0); 288 } 289 290 usage: 291 timerlat_usage(1); 292 exit(1); 293 } 294