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