xref: /netbsd-src/usr.sbin/intrctl/intrctl.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: intrctl.c,v 1.11 2019/09/24 11:31:06 wiz 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.11 2019/09/24 11:31:06 wiz 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
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
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 void
114 intrctl_list_one(bool compact, bool skipzero)
115 {
116 	char buf[64];
117 	struct intrio_list_line *illine;
118 	int i, ncpus, *cpucol;
119 	void *handle;
120 	size_t intridlen;
121 
122 	handle = intrctl_io_alloc(intrctl_io_alloc_retry_count);
123 	if (handle == NULL)
124 		err(EXIT_FAILURE, "intrctl_io_alloc");
125 
126 	/* calc columns */
127 	ncpus = intrctl_io_ncpus(handle);
128 	intridlen = strlen("interrupt id");
129 	for (illine = intrctl_io_firstline(handle); illine != NULL;
130 	    illine = intrctl_io_nextline(handle, illine)) {
131 		size_t len = strlen(illine->ill_intrid);
132 		if (intridlen < len)
133 			intridlen = len;
134 	}
135 
136 	cpucol = malloc(sizeof(*cpucol) * (size_t)ncpus);
137 	if (cpucol == NULL)
138 		err(EXIT_FAILURE, "malloc");
139 	for (i = 0; i < ncpus; i++) {
140 		snprintf(buf, sizeof(buf), "CPU%u", i);
141 		cpucol[i] = strlen(buf);
142 	}
143 	for (illine = intrctl_io_firstline(handle); illine != NULL;
144 	    illine = intrctl_io_nextline(handle, illine)) {
145 		for (i = 0; i < ncpus; i++) {
146 			int len;
147 			snprintf(buf, sizeof(buf), "%" PRIu64,
148 			    illine->ill_cpu[i].illc_count);
149 			len = (int)strlen(buf);
150 			if (cpucol[i] < len)
151 				cpucol[i] = len;
152 		}
153 	}
154 
155 	/* header */
156 	printf("%-*s ", (int)intridlen, "interrupt id");
157 	if (compact) {
158 		printf("%20s ", "total");
159 		printf("%5s ", "aff");
160 	} else {
161 		for (i = 0; i < ncpus; i++) {
162 			snprintf(buf, sizeof(buf), "CPU%u", i);
163 			printf("%*s  ", cpucol[i], buf);
164 		}
165 	}
166 	printf("device name(s)\n");
167 
168 	/* body */
169 	for (illine = intrctl_io_firstline(handle); illine != NULL;
170 	    illine = intrctl_io_nextline(handle, illine)) {
171 		struct intrio_list_line_cpu *illc;
172 
173 		if (skipzero) {
174 			bool is_zero = true;
175 
176 			for (i = 0; i < ncpus; i++) {
177 				illc = &illine->ill_cpu[i];
178 				if (illc->illc_count != 0) {
179 					is_zero = false;
180 					break;
181 				}
182 			}
183 			if (is_zero)
184 				continue;
185 		}
186 
187 		printf("%-*s ", (int)intridlen, illine->ill_intrid);
188 		if (compact) {
189 			uint64_t total = 0;
190 			char *affinity = NULL, *oaffinity = NULL;
191 			for (i = 0; i < ncpus; i++) {
192 				illc = &illine->ill_cpu[i];
193 				total += illc->illc_count;
194 				if (illc->illc_assigned) {
195 					asprintf(&affinity, "%s%s%d",
196 					    oaffinity ? oaffinity : "",
197 					    oaffinity ? ", " : "",
198 					    i);
199 					if (oaffinity)
200 						free(oaffinity);
201 					oaffinity = affinity;
202 				}
203 			}
204 			printf("%20" PRIu64 " ", total);
205 			printf("%5s ", affinity ? affinity : "none");
206 			if (affinity)
207 				free(affinity);
208 		} else {
209 			for (i = 0; i < ncpus; i++) {
210 				illc = &illine->ill_cpu[i];
211 				printf("%*" PRIu64 "%c ", cpucol[i], illc->illc_count,
212 				    illc->illc_assigned ? '*' : ' ');
213 			}
214 		}
215 		printf("%s\n", illine->ill_xname);
216 	}
217 
218 	free(cpucol);
219 	intrctl_io_free(handle);
220 }
221 
222 static void
223 intrctl_list(int argc, char **argv)
224 {
225 	int seconds = 0;
226 	bool compact = false;
227 	bool skipzero = false;
228 	int ch;
229 
230 	while ((ch = getopt(argc, argv, "cw:z")) != -1) {
231 		switch (ch) {
232 		case 'c':
233 			compact = true;
234 			break;
235 		case 'z':
236 			skipzero = true;
237 			break;
238 		case 'w':
239 			seconds = atoi(optarg);
240 			if (seconds < 0)
241 				errx(1, "seconds must be positive.");
242 			break;
243 		default:
244 			usage();
245 		}
246 	}
247 
248 	for (;;) {
249 		intrctl_list_one(compact, skipzero);
250 		if (seconds == 0)
251 			break;
252 		sleep(seconds);
253 	}
254 }
255 
256 static void
257 intrctl_affinity(int argc, char **argv)
258 {
259 	struct intrio_set iset;
260 	cpuset_t *cpuset;
261 	unsigned long index;
262 	int ch, error;
263 
264 	index = ULONG_MAX;
265 	memset(&iset.intrid, 0, sizeof(iset.intrid));
266 
267 	while ((ch = getopt(argc, argv, "c:i:")) != -1) {
268 		switch (ch) {
269 		case 'c':
270 			index = strtoul(optarg, NULL, 10);
271 			break;
272 		case 'i':
273 			if (strnlen(optarg, ARG_MAX) > INTRIDBUF)
274 				usage();
275 			strlcpy(iset.intrid, optarg, INTRIDBUF);
276 			break;
277 		default:
278 			usage();
279 		}
280 	}
281 
282 	if (iset.intrid[0] == '\0' || index == ULONG_MAX)
283 		usage();
284 
285 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
286 		err(EXIT_FAILURE, "invalid cpu index");
287 
288 	cpuset = cpuset_create();
289 	if (cpuset == NULL)
290 		err(EXIT_FAILURE, "create_cpuset()");
291 
292 	cpuset_zero(cpuset);
293 	cpuset_set(index, cpuset);
294 	iset.cpuset = cpuset;
295 	iset.cpuset_size = cpuset_size(cpuset);
296 	error = sysctlbyname("kern.intr.affinity", NULL, NULL, &iset, sizeof(iset));
297 	cpuset_destroy(cpuset);
298 	if (error < 0)
299 		err(EXIT_FAILURE, "sysctl kern.intr.affinity");
300 }
301 
302 static void
303 intrctl_intr(int argc, char **argv)
304 {
305 	struct intrio_set iset;
306 	cpuset_t *cpuset;
307 	unsigned long index;
308 	int ch, error;
309 
310 	index = ULONG_MAX;
311 
312 	while ((ch = getopt(argc, argv, "c:")) != -1) {
313 		switch (ch) {
314 		case 'c':
315 			index = strtoul(optarg, NULL, 10);
316 			break;
317 		default:
318 			usage();
319 		}
320 	}
321 
322 	if (index == ULONG_MAX)
323 		usage();
324 
325 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
326 		err(EXIT_FAILURE, "invalid cpu index");
327 
328 	cpuset = cpuset_create();
329 	if (cpuset == NULL)
330 		err(EXIT_FAILURE, "create_cpuset()");
331 
332 	cpuset_zero(cpuset);
333 	cpuset_set(index, cpuset);
334 	iset.cpuset = cpuset;
335 	iset.cpuset_size = cpuset_size(cpuset);
336 	error = sysctlbyname("kern.intr.intr", NULL, NULL, &iset, sizeof(iset));
337 	cpuset_destroy(cpuset);
338 	if (error < 0)
339 		err(EXIT_FAILURE, "sysctl kern.intr.intr");
340 }
341 
342 static void
343 intrctl_nointr(int argc, char **argv)
344 {
345 	struct intrio_set iset;
346 	cpuset_t *cpuset;
347 	unsigned long index;
348 	int ch, error;
349 
350 	index = ULONG_MAX;
351 
352 	while ((ch = getopt(argc, argv, "c:")) != -1) {
353 		switch (ch) {
354 		case 'c':
355 			index = strtoul(optarg, NULL, 10);
356 			break;
357 		default:
358 			usage();
359 		}
360 	}
361 
362 	if (index == ULONG_MAX)
363 		usage();
364 
365 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
366 		err(EXIT_FAILURE, "invalid cpu index");
367 
368 	cpuset = cpuset_create();
369 	if (cpuset == NULL)
370 		err(EXIT_FAILURE, "create_cpuset()");
371 
372 	cpuset_zero(cpuset);
373 	cpuset_set(index, cpuset);
374 	iset.cpuset = cpuset;
375 	iset.cpuset_size = cpuset_size(cpuset);
376 	error = sysctlbyname("kern.intr.nointr", NULL, NULL, &iset, sizeof(iset));
377 	cpuset_destroy(cpuset);
378 	if (error < 0)
379 		err(EXIT_FAILURE, "sysctl kern.intr.nointr");
380 }
381