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 19 #define DEFAULT_TIMERLAT_PERIOD 1000 /* 1ms */ 20 21 /* 22 * timerlat_apply_config - apply common configs to the initialized tool 23 */ 24 int 25 timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) 26 { 27 int retval, i; 28 29 if (!params->sleep_time) 30 params->sleep_time = 1; 31 32 retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all"); 33 if (retval) { 34 err_msg("Failed to apply CPUs config\n"); 35 goto out_err; 36 } 37 38 if (!params->cpus) { 39 for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) 40 CPU_SET(i, ¶ms->monitored_cpus); 41 } 42 43 if (params->mode != TRACING_MODE_BPF) { 44 /* 45 * In tracefs and mixed mode, timerlat tracer handles stopping 46 * on threshold 47 */ 48 retval = osnoise_set_stop_us(tool->context, params->stop_us); 49 if (retval) { 50 err_msg("Failed to set stop us\n"); 51 goto out_err; 52 } 53 54 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); 55 if (retval) { 56 err_msg("Failed to set stop total us\n"); 57 goto out_err; 58 } 59 } 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 if (params->hk_cpus) { 79 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), 80 ¶ms->hk_cpu_set); 81 if (retval == -1) { 82 err_msg("Failed to set rtla to the house keeping CPUs\n"); 83 goto out_err; 84 } 85 } else if (params->cpus) { 86 /* 87 * Even if the user do not set a house-keeping CPU, try to 88 * move rtla to a CPU set different to the one where the user 89 * set the workload to run. 90 * 91 * No need to check results as this is an automatic attempt. 92 */ 93 auto_house_keeping(¶ms->monitored_cpus); 94 } 95 96 /* 97 * If the user did not specify a type of thread, try user-threads first. 98 * Fall back to kernel threads otherwise. 99 */ 100 if (!params->kernel_workload && !params->user_data) { 101 retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd"); 102 if (retval) { 103 debug_msg("User-space interface detected, setting user-threads\n"); 104 params->user_workload = 1; 105 params->user_data = 1; 106 } else { 107 debug_msg("User-space interface not detected, setting kernel-threads\n"); 108 params->kernel_workload = 1; 109 } 110 } 111 112 /* 113 * Set workload according to type of thread if the kernel supports it. 114 * On kernels without support, user threads will have already failed 115 * on missing timerlat_fd, and kernel threads do not need it. 116 */ 117 retval = osnoise_set_workload(tool->context, params->kernel_workload); 118 if (retval < -1) { 119 err_msg("Failed to set OSNOISE_WORKLOAD option\n"); 120 goto out_err; 121 } 122 123 return 0; 124 125 out_err: 126 return -1; 127 } 128 129 static void timerlat_usage(int err) 130 { 131 int i; 132 133 static const char * const msg[] = { 134 "", 135 "timerlat version " VERSION, 136 "", 137 " usage: [rtla] timerlat [MODE] ...", 138 "", 139 " modes:", 140 " top - prints the summary from timerlat tracer", 141 " hist - prints a histogram of timer latencies", 142 "", 143 "if no MODE is given, the top mode is called, passing the arguments", 144 NULL, 145 }; 146 147 for (i = 0; msg[i]; i++) 148 fprintf(stderr, "%s\n", msg[i]); 149 exit(err); 150 } 151 152 int timerlat_main(int argc, char *argv[]) 153 { 154 if (argc == 0) 155 goto usage; 156 157 /* 158 * if timerlat was called without any argument, run the 159 * default cmdline. 160 */ 161 if (argc == 1) { 162 timerlat_top_main(argc, argv); 163 exit(0); 164 } 165 166 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { 167 timerlat_usage(0); 168 } else if (strncmp(argv[1], "-", 1) == 0) { 169 /* the user skipped the tool, call the default one */ 170 timerlat_top_main(argc, argv); 171 exit(0); 172 } else if (strcmp(argv[1], "top") == 0) { 173 timerlat_top_main(argc-1, &argv[1]); 174 exit(0); 175 } else if (strcmp(argv[1], "hist") == 0) { 176 timerlat_hist_main(argc-1, &argv[1]); 177 exit(0); 178 } 179 180 usage: 181 timerlat_usage(1); 182 exit(1); 183 } 184