xref: /netbsd-src/usr.sbin/intrctl/intrctl.c (revision 25801e14c490be147c48fe22e75cf5203307c8f2)
1 /*	$NetBSD: intrctl.c,v 1.12 2021/02/22 11:33:34 jmcneill Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 Internet Initiative Japan Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: intrctl.c,v 1.12 2021/02/22 11:33:34 jmcneill Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/sysctl.h>
34 #include <sys/intrio.h>
35 #include <sys/types.h>
36 
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <paths.h>
42 #include <sched.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include "intrctl_io.h"
50 
51 __dead static void	usage(void);
52 
53 int		verbose;
54 
55 static void	intrctl_list(int, char **);
56 static void	intrctl_affinity(int, char **);
57 static void	intrctl_intr(int, char **);
58 static void	intrctl_nointr(int, char **);
59 
60 static struct cmdtab {
61 	const char	*label;
62 	void	(*func)(int, char **);
63 } const intrctl_cmdtab[] = {
64 	{ "list", intrctl_list },
65 	{ "affinity", intrctl_affinity },
66 	{ "intr", intrctl_intr },
67 	{ "nointr", intrctl_nointr },
68 	{ NULL, NULL },
69 };
70 
71 int
main(int argc,char ** argv)72 main(int argc, char **argv)
73 {
74 	const struct cmdtab *ct;
75 	char *cmdname;
76 
77 	if (argc < 2)
78 		usage();
79 
80 	cmdname = argv[1];
81 	argv += 1;
82 	argc -= 1;
83 
84 	for (ct = intrctl_cmdtab; ct->label != NULL; ct++) {
85 		if (strcmp(cmdname, ct->label) == 0) {
86 			break;
87 		}
88 	}
89 	if (ct->label == NULL)
90 		errx(EXIT_FAILURE, "unknown command ``%s''", cmdname);
91 
92 	(*ct->func)(argc, argv);
93 	exit(EXIT_SUCCESS);
94 	/* NOTREACHED */
95 }
96 
97 static void
usage(void)98 usage(void)
99 {
100 	const char *progname = getprogname();
101 
102 	fprintf(stderr, "usage: %s list [-cz] [-w secs]\n", progname);
103 	fprintf(stderr, "       %s affinity -i interrupt_name -c cpu_index\n",
104 	    progname);
105 	fprintf(stderr, "       %s intr -c cpu_index\n", progname);
106 	fprintf(stderr, "       %s nointr -c cpu_index\n", progname);
107 	exit(EXIT_FAILURE);
108 	/* NOTREACHED */
109 }
110 
111 static int intrctl_io_alloc_retry_count = 4;
112 
113 static bool
intrctl_list_line_allcpus(struct intrio_list_line * illine,int ncpus)114 intrctl_list_line_allcpus(struct intrio_list_line *illine, int ncpus)
115 {
116 	struct intrio_list_line_cpu *illc;
117 	int i;
118 
119 	for (i = 0; i < ncpus; i++) {
120 		illc = &illine->ill_cpu[i];
121 		if (illc->illc_assigned == false) {
122 			return false;
123 		}
124 	}
125 
126 	return true;
127 }
128 
129 static void
intrctl_list_one(bool compact,bool skipzero)130 intrctl_list_one(bool compact, bool skipzero)
131 {
132 	char buf[64];
133 	struct intrio_list_line *illine;
134 	int i, ncpus, *cpucol;
135 	void *handle;
136 	size_t intridlen;
137 
138 	handle = intrctl_io_alloc(intrctl_io_alloc_retry_count);
139 	if (handle == NULL)
140 		err(EXIT_FAILURE, "intrctl_io_alloc");
141 
142 	/* calc columns */
143 	ncpus = intrctl_io_ncpus(handle);
144 	intridlen = strlen("interrupt id");
145 	for (illine = intrctl_io_firstline(handle); illine != NULL;
146 	    illine = intrctl_io_nextline(handle, illine)) {
147 		size_t len = strlen(illine->ill_intrid);
148 		if (intridlen < len)
149 			intridlen = len;
150 	}
151 
152 	cpucol = malloc(sizeof(*cpucol) * (size_t)ncpus);
153 	if (cpucol == NULL)
154 		err(EXIT_FAILURE, "malloc");
155 	for (i = 0; i < ncpus; i++) {
156 		snprintf(buf, sizeof(buf), "CPU%u", i);
157 		cpucol[i] = strlen(buf);
158 	}
159 	for (illine = intrctl_io_firstline(handle); illine != NULL;
160 	    illine = intrctl_io_nextline(handle, illine)) {
161 		for (i = 0; i < ncpus; i++) {
162 			int len;
163 			snprintf(buf, sizeof(buf), "%" PRIu64,
164 			    illine->ill_cpu[i].illc_count);
165 			len = (int)strlen(buf);
166 			if (cpucol[i] < len)
167 				cpucol[i] = len;
168 		}
169 	}
170 
171 	/* header */
172 	printf("%-*s ", (int)intridlen, "interrupt id");
173 	if (compact) {
174 		printf("%20s ", "total");
175 		printf("%5s ", "aff");
176 	} else {
177 		for (i = 0; i < ncpus; i++) {
178 			snprintf(buf, sizeof(buf), "CPU%u", i);
179 			printf("%*s  ", cpucol[i], buf);
180 		}
181 	}
182 	printf("device name(s)\n");
183 
184 	/* body */
185 	for (illine = intrctl_io_firstline(handle); illine != NULL;
186 	    illine = intrctl_io_nextline(handle, illine)) {
187 		struct intrio_list_line_cpu *illc;
188 
189 		if (skipzero) {
190 			bool is_zero = true;
191 
192 			for (i = 0; i < ncpus; i++) {
193 				illc = &illine->ill_cpu[i];
194 				if (illc->illc_count != 0) {
195 					is_zero = false;
196 					break;
197 				}
198 			}
199 			if (is_zero)
200 				continue;
201 		}
202 
203 		printf("%-*s ", (int)intridlen, illine->ill_intrid);
204 		if (compact) {
205 			uint64_t total = 0;
206 			bool allcpus = ncpus > 1 &&
207 			    intrctl_list_line_allcpus(illine, ncpus);
208 			char *affinity = NULL, *oaffinity = NULL;
209 			for (i = 0; i < ncpus; i++) {
210 				illc = &illine->ill_cpu[i];
211 				total += illc->illc_count;
212 				if (allcpus && i != 0 && i != ncpus - 1) {
213 					continue;
214 				}
215 				if (illc->illc_assigned) {
216 					const char *sep = allcpus ? "-" : ", ";
217 					asprintf(&affinity, "%s%s%d",
218 					    oaffinity ? oaffinity : "",
219 					    oaffinity ? sep : "",
220 					    i);
221 					if (oaffinity)
222 						free(oaffinity);
223 					oaffinity = affinity;
224 				}
225 			}
226 			printf("%20" PRIu64 " ", total);
227 			printf("%5s ", affinity ? affinity : "none");
228 			if (affinity)
229 				free(affinity);
230 		} else {
231 			for (i = 0; i < ncpus; i++) {
232 				illc = &illine->ill_cpu[i];
233 				printf("%*" PRIu64 "%c ", cpucol[i], illc->illc_count,
234 				    illc->illc_assigned ? '*' : ' ');
235 			}
236 		}
237 		printf("%s\n", illine->ill_xname);
238 	}
239 
240 	free(cpucol);
241 	intrctl_io_free(handle);
242 }
243 
244 static void
intrctl_list(int argc,char ** argv)245 intrctl_list(int argc, char **argv)
246 {
247 	int seconds = 0;
248 	bool compact = false;
249 	bool skipzero = false;
250 	int ch;
251 
252 	while ((ch = getopt(argc, argv, "cw:z")) != -1) {
253 		switch (ch) {
254 		case 'c':
255 			compact = true;
256 			break;
257 		case 'z':
258 			skipzero = true;
259 			break;
260 		case 'w':
261 			seconds = atoi(optarg);
262 			if (seconds < 0)
263 				errx(1, "seconds must be positive.");
264 			break;
265 		default:
266 			usage();
267 		}
268 	}
269 
270 	for (;;) {
271 		intrctl_list_one(compact, skipzero);
272 		if (seconds == 0)
273 			break;
274 		sleep(seconds);
275 	}
276 }
277 
278 static void
intrctl_affinity(int argc,char ** argv)279 intrctl_affinity(int argc, char **argv)
280 {
281 	struct intrio_set iset;
282 	cpuset_t *cpuset;
283 	unsigned long index;
284 	int ch, error;
285 
286 	index = ULONG_MAX;
287 	memset(&iset.intrid, 0, sizeof(iset.intrid));
288 
289 	while ((ch = getopt(argc, argv, "c:i:")) != -1) {
290 		switch (ch) {
291 		case 'c':
292 			index = strtoul(optarg, NULL, 10);
293 			break;
294 		case 'i':
295 			if (strnlen(optarg, ARG_MAX) > INTRIDBUF)
296 				usage();
297 			strlcpy(iset.intrid, optarg, INTRIDBUF);
298 			break;
299 		default:
300 			usage();
301 		}
302 	}
303 
304 	if (iset.intrid[0] == '\0' || index == ULONG_MAX)
305 		usage();
306 
307 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
308 		err(EXIT_FAILURE, "invalid cpu index");
309 
310 	cpuset = cpuset_create();
311 	if (cpuset == NULL)
312 		err(EXIT_FAILURE, "create_cpuset()");
313 
314 	cpuset_zero(cpuset);
315 	cpuset_set(index, cpuset);
316 	iset.cpuset = cpuset;
317 	iset.cpuset_size = cpuset_size(cpuset);
318 	error = sysctlbyname("kern.intr.affinity", NULL, NULL, &iset, sizeof(iset));
319 	cpuset_destroy(cpuset);
320 	if (error < 0)
321 		err(EXIT_FAILURE, "sysctl kern.intr.affinity");
322 }
323 
324 static void
intrctl_intr(int argc,char ** argv)325 intrctl_intr(int argc, char **argv)
326 {
327 	struct intrio_set iset;
328 	cpuset_t *cpuset;
329 	unsigned long index;
330 	int ch, error;
331 
332 	index = ULONG_MAX;
333 
334 	while ((ch = getopt(argc, argv, "c:")) != -1) {
335 		switch (ch) {
336 		case 'c':
337 			index = strtoul(optarg, NULL, 10);
338 			break;
339 		default:
340 			usage();
341 		}
342 	}
343 
344 	if (index == ULONG_MAX)
345 		usage();
346 
347 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
348 		err(EXIT_FAILURE, "invalid cpu index");
349 
350 	cpuset = cpuset_create();
351 	if (cpuset == NULL)
352 		err(EXIT_FAILURE, "create_cpuset()");
353 
354 	cpuset_zero(cpuset);
355 	cpuset_set(index, cpuset);
356 	iset.cpuset = cpuset;
357 	iset.cpuset_size = cpuset_size(cpuset);
358 	error = sysctlbyname("kern.intr.intr", NULL, NULL, &iset, sizeof(iset));
359 	cpuset_destroy(cpuset);
360 	if (error < 0)
361 		err(EXIT_FAILURE, "sysctl kern.intr.intr");
362 }
363 
364 static void
intrctl_nointr(int argc,char ** argv)365 intrctl_nointr(int argc, char **argv)
366 {
367 	struct intrio_set iset;
368 	cpuset_t *cpuset;
369 	unsigned long index;
370 	int ch, error;
371 
372 	index = ULONG_MAX;
373 
374 	while ((ch = getopt(argc, argv, "c:")) != -1) {
375 		switch (ch) {
376 		case 'c':
377 			index = strtoul(optarg, NULL, 10);
378 			break;
379 		default:
380 			usage();
381 		}
382 	}
383 
384 	if (index == ULONG_MAX)
385 		usage();
386 
387 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
388 		err(EXIT_FAILURE, "invalid cpu index");
389 
390 	cpuset = cpuset_create();
391 	if (cpuset == NULL)
392 		err(EXIT_FAILURE, "create_cpuset()");
393 
394 	cpuset_zero(cpuset);
395 	cpuset_set(index, cpuset);
396 	iset.cpuset = cpuset;
397 	iset.cpuset_size = cpuset_size(cpuset);
398 	error = sysctlbyname("kern.intr.nointr", NULL, NULL, &iset, sizeof(iset));
399 	cpuset_destroy(cpuset);
400 	if (error < 0)
401 		err(EXIT_FAILURE, "sysctl kern.intr.nointr");
402 }
403