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
timerlat_apply_config(struct osnoise_tool * tool,struct timerlat_params * params)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 retval = osnoise_set_stop_us(tool->context, params->stop_us);
44 if (retval) {
45 err_msg("Failed to set stop us\n");
46 goto out_err;
47 }
48
49 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us);
50 if (retval) {
51 err_msg("Failed to set stop total us\n");
52 goto out_err;
53 }
54
55
56 retval = osnoise_set_timerlat_period_us(tool->context,
57 params->timerlat_period_us ?
58 params->timerlat_period_us :
59 DEFAULT_TIMERLAT_PERIOD);
60 if (retval) {
61 err_msg("Failed to set timerlat period\n");
62 goto out_err;
63 }
64
65
66 retval = osnoise_set_print_stack(tool->context, params->print_stack);
67 if (retval) {
68 err_msg("Failed to set print stack\n");
69 goto out_err;
70 }
71
72 if (params->hk_cpus) {
73 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
74 ¶ms->hk_cpu_set);
75 if (retval == -1) {
76 err_msg("Failed to set rtla to the house keeping CPUs\n");
77 goto out_err;
78 }
79 } else if (params->cpus) {
80 /*
81 * Even if the user do not set a house-keeping CPU, try to
82 * move rtla to a CPU set different to the one where the user
83 * set the workload to run.
84 *
85 * No need to check results as this is an automatic attempt.
86 */
87 auto_house_keeping(¶ms->monitored_cpus);
88 }
89
90 /*
91 * If the user did not specify a type of thread, try user-threads first.
92 * Fall back to kernel threads otherwise.
93 */
94 if (!params->kernel_workload && !params->user_data) {
95 retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd");
96 if (retval) {
97 debug_msg("User-space interface detected, setting user-threads\n");
98 params->user_workload = 1;
99 params->user_data = 1;
100 } else {
101 debug_msg("User-space interface not detected, setting kernel-threads\n");
102 params->kernel_workload = 1;
103 }
104 }
105
106 /*
107 * Set workload according to type of thread if the kernel supports it.
108 * On kernels without support, user threads will have already failed
109 * on missing timerlat_fd, and kernel threads do not need it.
110 */
111 retval = osnoise_set_workload(tool->context, params->kernel_workload);
112 if (retval < -1) {
113 err_msg("Failed to set OSNOISE_WORKLOAD option\n");
114 goto out_err;
115 }
116
117 return 0;
118
119 out_err:
120 return -1;
121 }
122
timerlat_usage(int err)123 static void timerlat_usage(int err)
124 {
125 int i;
126
127 static const char * const msg[] = {
128 "",
129 "timerlat version " VERSION,
130 "",
131 " usage: [rtla] timerlat [MODE] ...",
132 "",
133 " modes:",
134 " top - prints the summary from timerlat tracer",
135 " hist - prints a histogram of timer latencies",
136 "",
137 "if no MODE is given, the top mode is called, passing the arguments",
138 NULL,
139 };
140
141 for (i = 0; msg[i]; i++)
142 fprintf(stderr, "%s\n", msg[i]);
143 exit(err);
144 }
145
timerlat_main(int argc,char * argv[])146 int timerlat_main(int argc, char *argv[])
147 {
148 if (argc == 0)
149 goto usage;
150
151 /*
152 * if timerlat was called without any argument, run the
153 * default cmdline.
154 */
155 if (argc == 1) {
156 timerlat_top_main(argc, argv);
157 exit(0);
158 }
159
160 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
161 timerlat_usage(0);
162 } else if (strncmp(argv[1], "-", 1) == 0) {
163 /* the user skipped the tool, call the default one */
164 timerlat_top_main(argc, argv);
165 exit(0);
166 } else if (strcmp(argv[1], "top") == 0) {
167 timerlat_top_main(argc-1, &argv[1]);
168 exit(0);
169 } else if (strcmp(argv[1], "hist") == 0) {
170 timerlat_hist_main(argc-1, &argv[1]);
171 exit(0);
172 }
173
174 usage:
175 timerlat_usage(1);
176 exit(1);
177 }
178