xref: /netbsd-src/usr.sbin/intrctl/intrctl.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: intrctl.c,v 1.7 2016/11/02 11:03:33 ryo 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.7 2016/11/02 11:03:33 ryo 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, *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 			free(affinity);
204 		} else {
205 			for (i = 0; i < ncpus; i++) {
206 				illc = &illine->ill_cpu[i];
207 				printf("%*" PRIu64 "%c ", cpucol[i], illc->illc_count,
208 				    illc->illc_assigned ? '*' : ' ');
209 			}
210 		}
211 		printf("%s\n", illine->ill_xname);
212 	}
213 
214 	free(cpucol);
215 	intrctl_io_free(handle);
216 }
217 
218 static void
219 intrctl_affinity(int argc, char **argv)
220 {
221 	struct intrio_set iset;
222 	cpuset_t *cpuset;
223 	unsigned long index;
224 	int ch, error;
225 
226 	index = ULONG_MAX;
227 	memset(&iset.intrid, 0, sizeof(iset.intrid));
228 
229 	while ((ch = getopt(argc, argv, "c:i:")) != -1) {
230 		switch (ch) {
231 		case 'c':
232 			index = strtoul(optarg, NULL, 10);
233 			break;
234 		case 'i':
235 			if (strnlen(optarg, ARG_MAX) > INTRIDBUF)
236 				usage();
237 			strlcpy(iset.intrid, optarg, INTRIDBUF);
238 			break;
239 		default:
240 			usage();
241 		}
242 	}
243 
244 	if (iset.intrid[0] == '\0' || index == ULONG_MAX)
245 		usage();
246 
247 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
248 		err(EXIT_FAILURE, "invalid cpu index");
249 
250 	cpuset = cpuset_create();
251 	if (cpuset == NULL)
252 		err(EXIT_FAILURE, "create_cpuset()");
253 
254 	cpuset_zero(cpuset);
255 	cpuset_set(index, cpuset);
256 	iset.cpuset = cpuset;
257 	iset.cpuset_size = cpuset_size(cpuset);
258 	error = sysctlbyname("kern.intr.affinity", NULL, NULL, &iset, sizeof(iset));
259 	cpuset_destroy(cpuset);
260 	if (error < 0)
261 		err(EXIT_FAILURE, "sysctl kern.intr.affinity");
262 }
263 
264 static void
265 intrctl_intr(int argc, char **argv)
266 {
267 	struct intrio_set iset;
268 	cpuset_t *cpuset;
269 	unsigned long index;
270 	int ch, error;
271 
272 	index = ULONG_MAX;
273 
274 	while ((ch = getopt(argc, argv, "c:")) != -1) {
275 		switch (ch) {
276 		case 'c':
277 			index = strtoul(optarg, NULL, 10);
278 			break;
279 		default:
280 			usage();
281 		}
282 	}
283 
284 	if (index == ULONG_MAX)
285 		usage();
286 
287 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
288 		err(EXIT_FAILURE, "invalid cpu index");
289 
290 	cpuset = cpuset_create();
291 	if (cpuset == NULL)
292 		err(EXIT_FAILURE, "create_cpuset()");
293 
294 	cpuset_zero(cpuset);
295 	cpuset_set(index, cpuset);
296 	iset.cpuset = cpuset;
297 	iset.cpuset_size = cpuset_size(cpuset);
298 	error = sysctlbyname("kern.intr.intr", NULL, NULL, &iset, sizeof(iset));
299 	cpuset_destroy(cpuset);
300 	if (error < 0)
301 		err(EXIT_FAILURE, "sysctl kern.intr.intr");
302 }
303 
304 static void
305 intrctl_nointr(int argc, char **argv)
306 {
307 	struct intrio_set iset;
308 	cpuset_t *cpuset;
309 	unsigned long index;
310 	int ch, error;
311 
312 	index = ULONG_MAX;
313 
314 	while ((ch = getopt(argc, argv, "c:")) != -1) {
315 		switch (ch) {
316 		case 'c':
317 			index = strtoul(optarg, NULL, 10);
318 			break;
319 		default:
320 			usage();
321 		}
322 	}
323 
324 	if (index == ULONG_MAX)
325 		usage();
326 
327 	if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
328 		err(EXIT_FAILURE, "invalid cpu index");
329 
330 	cpuset = cpuset_create();
331 	if (cpuset == NULL)
332 		err(EXIT_FAILURE, "create_cpuset()");
333 
334 	cpuset_zero(cpuset);
335 	cpuset_set(index, cpuset);
336 	iset.cpuset = cpuset;
337 	iset.cpuset_size = cpuset_size(cpuset);
338 	error = sysctlbyname("kern.intr.nointr", NULL, NULL, &iset, sizeof(iset));
339 	cpuset_destroy(cpuset);
340 	if (error < 0)
341 		err(EXIT_FAILURE, "sysctl kern.intr.nointr");
342 }
343