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, &params->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 					   &params->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(&params->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 
timerlat_usage(int err)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 
timerlat_main(int argc,char * argv[])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