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
53int		verbose;
54
55static void	intrctl_list(int, char **);
56static void	intrctl_affinity(int, char **);
57static void	intrctl_intr(int, char **);
58static void	intrctl_nointr(int, char **);
59
60static 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
71int
72main(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
97static void
98usage(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
111static int intrctl_io_alloc_retry_count = 4;
112
113static bool
114intrctl_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
129static void
130intrctl_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
244static void
245intrctl_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
278static void
279intrctl_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
324static void
325intrctl_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
364static void
365intrctl_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