1*25801e14Sjmcneill /* $NetBSD: intrctl.c,v 1.12 2021/02/22 11:33:34 jmcneill Exp $ */
284f76c9cSknakahara
384f76c9cSknakahara /*
484f76c9cSknakahara * Copyright (c) 2015 Internet Initiative Japan Inc.
584f76c9cSknakahara * All rights reserved.
684f76c9cSknakahara *
784f76c9cSknakahara * Redistribution and use in source and binary forms, with or without
884f76c9cSknakahara * modification, are permitted provided that the following conditions
984f76c9cSknakahara * are met:
1084f76c9cSknakahara * 1. Redistributions of source code must retain the above copyright
1184f76c9cSknakahara * notice, this list of conditions and the following disclaimer.
1284f76c9cSknakahara * 2. Redistributions in binary form must reproduce the above copyright
1384f76c9cSknakahara * notice, this list of conditions and the following disclaimer in the
1484f76c9cSknakahara * documentation and/or other materials provided with the distribution.
1584f76c9cSknakahara *
1684f76c9cSknakahara * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1784f76c9cSknakahara * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1884f76c9cSknakahara * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1984f76c9cSknakahara * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2084f76c9cSknakahara * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2184f76c9cSknakahara * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2284f76c9cSknakahara * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2384f76c9cSknakahara * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2484f76c9cSknakahara * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2584f76c9cSknakahara * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2684f76c9cSknakahara * POSSIBILITY OF SUCH DAMAGE.
2784f76c9cSknakahara */
2884f76c9cSknakahara
2984f76c9cSknakahara #include <sys/cdefs.h>
30*25801e14Sjmcneill __RCSID("$NetBSD: intrctl.c,v 1.12 2021/02/22 11:33:34 jmcneill Exp $");
3184f76c9cSknakahara
3284f76c9cSknakahara #include <sys/param.h>
3384f76c9cSknakahara #include <sys/sysctl.h>
3484f76c9cSknakahara #include <sys/intrio.h>
3584f76c9cSknakahara #include <sys/types.h>
3684f76c9cSknakahara
3784f76c9cSknakahara #include <err.h>
3884f76c9cSknakahara #include <errno.h>
3984f76c9cSknakahara #include <fcntl.h>
4084f76c9cSknakahara #include <limits.h>
4184f76c9cSknakahara #include <paths.h>
4284f76c9cSknakahara #include <sched.h>
4384f76c9cSknakahara #include <stdint.h>
4484f76c9cSknakahara #include <stdio.h>
4584f76c9cSknakahara #include <stdlib.h>
4684f76c9cSknakahara #include <string.h>
4784f76c9cSknakahara #include <unistd.h>
4884f76c9cSknakahara
4984f76c9cSknakahara #include "intrctl_io.h"
5084f76c9cSknakahara
5184f76c9cSknakahara __dead static void usage(void);
5284f76c9cSknakahara
5384f76c9cSknakahara int verbose;
5484f76c9cSknakahara
5584f76c9cSknakahara static void intrctl_list(int, char **);
5684f76c9cSknakahara static void intrctl_affinity(int, char **);
5784f76c9cSknakahara static void intrctl_intr(int, char **);
5884f76c9cSknakahara static void intrctl_nointr(int, char **);
5984f76c9cSknakahara
6084f76c9cSknakahara static struct cmdtab {
6184f76c9cSknakahara const char *label;
6284f76c9cSknakahara void (*func)(int, char **);
6384f76c9cSknakahara } const intrctl_cmdtab[] = {
6484f76c9cSknakahara { "list", intrctl_list },
6584f76c9cSknakahara { "affinity", intrctl_affinity },
6684f76c9cSknakahara { "intr", intrctl_intr },
6784f76c9cSknakahara { "nointr", intrctl_nointr },
6884f76c9cSknakahara { NULL, NULL },
6984f76c9cSknakahara };
7084f76c9cSknakahara
7184f76c9cSknakahara int
main(int argc,char ** argv)7284f76c9cSknakahara main(int argc, char **argv)
7384f76c9cSknakahara {
7484f76c9cSknakahara const struct cmdtab *ct;
7584f76c9cSknakahara char *cmdname;
7684f76c9cSknakahara
7784f76c9cSknakahara if (argc < 2)
7884f76c9cSknakahara usage();
7984f76c9cSknakahara
8084f76c9cSknakahara cmdname = argv[1];
8184f76c9cSknakahara argv += 1;
8284f76c9cSknakahara argc -= 1;
8384f76c9cSknakahara
8484f76c9cSknakahara for (ct = intrctl_cmdtab; ct->label != NULL; ct++) {
8584f76c9cSknakahara if (strcmp(cmdname, ct->label) == 0) {
8684f76c9cSknakahara break;
8784f76c9cSknakahara }
8884f76c9cSknakahara }
8984f76c9cSknakahara if (ct->label == NULL)
9084f76c9cSknakahara errx(EXIT_FAILURE, "unknown command ``%s''", cmdname);
9184f76c9cSknakahara
9284f76c9cSknakahara (*ct->func)(argc, argv);
9384f76c9cSknakahara exit(EXIT_SUCCESS);
9484f76c9cSknakahara /* NOTREACHED */
9584f76c9cSknakahara }
9684f76c9cSknakahara
9784f76c9cSknakahara static void
usage(void)9884f76c9cSknakahara usage(void)
9984f76c9cSknakahara {
10084f76c9cSknakahara const char *progname = getprogname();
10184f76c9cSknakahara
102e78f83e1Swiz fprintf(stderr, "usage: %s list [-cz] [-w secs]\n", progname);
103be571cf8Smrg fprintf(stderr, " %s affinity -i interrupt_name -c cpu_index\n",
104be571cf8Smrg progname);
10584f76c9cSknakahara fprintf(stderr, " %s intr -c cpu_index\n", progname);
10684f76c9cSknakahara fprintf(stderr, " %s nointr -c cpu_index\n", progname);
10784f76c9cSknakahara exit(EXIT_FAILURE);
10884f76c9cSknakahara /* NOTREACHED */
10984f76c9cSknakahara }
11084f76c9cSknakahara
11184f76c9cSknakahara static int intrctl_io_alloc_retry_count = 4;
11284f76c9cSknakahara
113*25801e14Sjmcneill static bool
intrctl_list_line_allcpus(struct intrio_list_line * illine,int ncpus)114*25801e14Sjmcneill intrctl_list_line_allcpus(struct intrio_list_line *illine, int ncpus)
115*25801e14Sjmcneill {
116*25801e14Sjmcneill struct intrio_list_line_cpu *illc;
117*25801e14Sjmcneill int i;
118*25801e14Sjmcneill
119*25801e14Sjmcneill for (i = 0; i < ncpus; i++) {
120*25801e14Sjmcneill illc = &illine->ill_cpu[i];
121*25801e14Sjmcneill if (illc->illc_assigned == false) {
122*25801e14Sjmcneill return false;
123*25801e14Sjmcneill }
124*25801e14Sjmcneill }
125*25801e14Sjmcneill
126*25801e14Sjmcneill return true;
127*25801e14Sjmcneill }
128*25801e14Sjmcneill
12984f76c9cSknakahara static void
intrctl_list_one(bool compact,bool skipzero)130be571cf8Smrg intrctl_list_one(bool compact, bool skipzero)
13184f76c9cSknakahara {
132eb2d7ca7Sryo char buf[64];
13384f76c9cSknakahara struct intrio_list_line *illine;
134eb2d7ca7Sryo int i, ncpus, *cpucol;
13584f76c9cSknakahara void *handle;
1368a1a4ef2Sknakahara size_t intridlen;
13784f76c9cSknakahara
13884f76c9cSknakahara handle = intrctl_io_alloc(intrctl_io_alloc_retry_count);
13984f76c9cSknakahara if (handle == NULL)
14084f76c9cSknakahara err(EXIT_FAILURE, "intrctl_io_alloc");
14184f76c9cSknakahara
1428a1a4ef2Sknakahara /* calc columns */
14384f76c9cSknakahara ncpus = intrctl_io_ncpus(handle);
1448a1a4ef2Sknakahara intridlen = strlen("interrupt id");
145eb2d7ca7Sryo for (illine = intrctl_io_firstline(handle); illine != NULL;
146eb2d7ca7Sryo illine = intrctl_io_nextline(handle, illine)) {
1478a1a4ef2Sknakahara size_t len = strlen(illine->ill_intrid);
1488a1a4ef2Sknakahara if (intridlen < len)
1498a1a4ef2Sknakahara intridlen = len;
1508a1a4ef2Sknakahara }
1518a1a4ef2Sknakahara
152eb2d7ca7Sryo cpucol = malloc(sizeof(*cpucol) * (size_t)ncpus);
153eb2d7ca7Sryo if (cpucol == NULL)
154eb2d7ca7Sryo err(EXIT_FAILURE, "malloc");
155eb2d7ca7Sryo for (i = 0; i < ncpus; i++) {
156eb2d7ca7Sryo snprintf(buf, sizeof(buf), "CPU%u", i);
157eb2d7ca7Sryo cpucol[i] = strlen(buf);
158eb2d7ca7Sryo }
159eb2d7ca7Sryo for (illine = intrctl_io_firstline(handle); illine != NULL;
160eb2d7ca7Sryo illine = intrctl_io_nextline(handle, illine)) {
161eb2d7ca7Sryo for (i = 0; i < ncpus; i++) {
162eb2d7ca7Sryo int len;
163eb2d7ca7Sryo snprintf(buf, sizeof(buf), "%" PRIu64,
164eb2d7ca7Sryo illine->ill_cpu[i].illc_count);
165eb2d7ca7Sryo len = (int)strlen(buf);
166eb2d7ca7Sryo if (cpucol[i] < len)
167eb2d7ca7Sryo cpucol[i] = len;
168eb2d7ca7Sryo }
169eb2d7ca7Sryo }
170eb2d7ca7Sryo
1718a1a4ef2Sknakahara /* header */
1728a1a4ef2Sknakahara printf("%-*s ", (int)intridlen, "interrupt id");
1738804f261Sjdolecek if (compact) {
1748804f261Sjdolecek printf("%20s ", "total");
175efdf03edSjdolecek printf("%5s ", "aff");
1768804f261Sjdolecek } else {
17784f76c9cSknakahara for (i = 0; i < ncpus; i++) {
1788a1a4ef2Sknakahara snprintf(buf, sizeof(buf), "CPU%u", i);
179eb2d7ca7Sryo printf("%*s ", cpucol[i], buf);
18084f76c9cSknakahara }
1818804f261Sjdolecek }
18284f76c9cSknakahara printf("device name(s)\n");
18384f76c9cSknakahara
18484f76c9cSknakahara /* body */
185eb2d7ca7Sryo for (illine = intrctl_io_firstline(handle); illine != NULL;
186eb2d7ca7Sryo illine = intrctl_io_nextline(handle, illine)) {
1878804f261Sjdolecek struct intrio_list_line_cpu *illc;
1888804f261Sjdolecek
189be571cf8Smrg if (skipzero) {
190be571cf8Smrg bool is_zero = true;
191be571cf8Smrg
192be571cf8Smrg for (i = 0; i < ncpus; i++) {
193be571cf8Smrg illc = &illine->ill_cpu[i];
194be571cf8Smrg if (illc->illc_count != 0) {
195be571cf8Smrg is_zero = false;
196be571cf8Smrg break;
197be571cf8Smrg }
198be571cf8Smrg }
199be571cf8Smrg if (is_zero)
200be571cf8Smrg continue;
201be571cf8Smrg }
202be571cf8Smrg
2038a1a4ef2Sknakahara printf("%-*s ", (int)intridlen, illine->ill_intrid);
2048804f261Sjdolecek if (compact) {
2058804f261Sjdolecek uint64_t total = 0;
206*25801e14Sjmcneill bool allcpus = ncpus > 1 &&
207*25801e14Sjmcneill intrctl_list_line_allcpus(illine, ncpus);
208c8af0e80Sjdolecek char *affinity = NULL, *oaffinity = NULL;
20984f76c9cSknakahara for (i = 0; i < ncpus; i++) {
2108804f261Sjdolecek illc = &illine->ill_cpu[i];
2118804f261Sjdolecek total += illc->illc_count;
212*25801e14Sjmcneill if (allcpus && i != 0 && i != ncpus - 1) {
213*25801e14Sjmcneill continue;
214*25801e14Sjmcneill }
2158804f261Sjdolecek if (illc->illc_assigned) {
216*25801e14Sjmcneill const char *sep = allcpus ? "-" : ", ";
2178804f261Sjdolecek asprintf(&affinity, "%s%s%d",
2188804f261Sjdolecek oaffinity ? oaffinity : "",
219*25801e14Sjmcneill oaffinity ? sep : "",
2208804f261Sjdolecek i);
2218804f261Sjdolecek if (oaffinity)
2228804f261Sjdolecek free(oaffinity);
2238804f261Sjdolecek oaffinity = affinity;
2248804f261Sjdolecek }
2258804f261Sjdolecek }
2268804f261Sjdolecek printf("%20" PRIu64 " ", total);
227efdf03edSjdolecek printf("%5s ", affinity ? affinity : "none");
228c8af0e80Sjdolecek if (affinity)
2298804f261Sjdolecek free(affinity);
2308804f261Sjdolecek } else {
2318804f261Sjdolecek for (i = 0; i < ncpus; i++) {
2328804f261Sjdolecek illc = &illine->ill_cpu[i];
233eb2d7ca7Sryo printf("%*" PRIu64 "%c ", cpucol[i], illc->illc_count,
23484f76c9cSknakahara illc->illc_assigned ? '*' : ' ');
23584f76c9cSknakahara }
2368804f261Sjdolecek }
23784f76c9cSknakahara printf("%s\n", illine->ill_xname);
23884f76c9cSknakahara }
23984f76c9cSknakahara
240eb2d7ca7Sryo free(cpucol);
24184f76c9cSknakahara intrctl_io_free(handle);
24284f76c9cSknakahara }
24384f76c9cSknakahara
24484f76c9cSknakahara static void
intrctl_list(int argc,char ** argv)2455b627c2dSmrg intrctl_list(int argc, char **argv)
2465b627c2dSmrg {
2475b627c2dSmrg int seconds = 0;
2485b627c2dSmrg bool compact = false;
249be571cf8Smrg bool skipzero = false;
2505b627c2dSmrg int ch;
2515b627c2dSmrg
252be571cf8Smrg while ((ch = getopt(argc, argv, "cw:z")) != -1) {
2535b627c2dSmrg switch (ch) {
2545b627c2dSmrg case 'c':
2555b627c2dSmrg compact = true;
2565b627c2dSmrg break;
257be571cf8Smrg case 'z':
258be571cf8Smrg skipzero = true;
259be571cf8Smrg break;
2605b627c2dSmrg case 'w':
2615b627c2dSmrg seconds = atoi(optarg);
2625b627c2dSmrg if (seconds < 0)
2635b627c2dSmrg errx(1, "seconds must be positive.");
2645b627c2dSmrg break;
2655b627c2dSmrg default:
2665b627c2dSmrg usage();
2675b627c2dSmrg }
2685b627c2dSmrg }
2695b627c2dSmrg
270be571cf8Smrg for (;;) {
271be571cf8Smrg intrctl_list_one(compact, skipzero);
272be571cf8Smrg if (seconds == 0)
273be571cf8Smrg break;
2745b627c2dSmrg sleep(seconds);
275be571cf8Smrg }
2765b627c2dSmrg }
2775b627c2dSmrg
2785b627c2dSmrg static void
intrctl_affinity(int argc,char ** argv)27984f76c9cSknakahara intrctl_affinity(int argc, char **argv)
28084f76c9cSknakahara {
28184f76c9cSknakahara struct intrio_set iset;
28284f76c9cSknakahara cpuset_t *cpuset;
28384f76c9cSknakahara unsigned long index;
28484f76c9cSknakahara int ch, error;
28584f76c9cSknakahara
28684f76c9cSknakahara index = ULONG_MAX;
28784f76c9cSknakahara memset(&iset.intrid, 0, sizeof(iset.intrid));
28884f76c9cSknakahara
28984f76c9cSknakahara while ((ch = getopt(argc, argv, "c:i:")) != -1) {
29084f76c9cSknakahara switch (ch) {
29184f76c9cSknakahara case 'c':
29284f76c9cSknakahara index = strtoul(optarg, NULL, 10);
29384f76c9cSknakahara break;
29484f76c9cSknakahara case 'i':
29584f76c9cSknakahara if (strnlen(optarg, ARG_MAX) > INTRIDBUF)
29684f76c9cSknakahara usage();
29784f76c9cSknakahara strlcpy(iset.intrid, optarg, INTRIDBUF);
29884f76c9cSknakahara break;
29984f76c9cSknakahara default:
30084f76c9cSknakahara usage();
30184f76c9cSknakahara }
30284f76c9cSknakahara }
30384f76c9cSknakahara
30484f76c9cSknakahara if (iset.intrid[0] == '\0' || index == ULONG_MAX)
30584f76c9cSknakahara usage();
30684f76c9cSknakahara
30784f76c9cSknakahara if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
30884f76c9cSknakahara err(EXIT_FAILURE, "invalid cpu index");
30984f76c9cSknakahara
31084f76c9cSknakahara cpuset = cpuset_create();
31184f76c9cSknakahara if (cpuset == NULL)
31284f76c9cSknakahara err(EXIT_FAILURE, "create_cpuset()");
31384f76c9cSknakahara
31484f76c9cSknakahara cpuset_zero(cpuset);
31584f76c9cSknakahara cpuset_set(index, cpuset);
31684f76c9cSknakahara iset.cpuset = cpuset;
31784f76c9cSknakahara iset.cpuset_size = cpuset_size(cpuset);
31884f76c9cSknakahara error = sysctlbyname("kern.intr.affinity", NULL, NULL, &iset, sizeof(iset));
31984f76c9cSknakahara cpuset_destroy(cpuset);
32084f76c9cSknakahara if (error < 0)
32184f76c9cSknakahara err(EXIT_FAILURE, "sysctl kern.intr.affinity");
32284f76c9cSknakahara }
32384f76c9cSknakahara
32484f76c9cSknakahara static void
intrctl_intr(int argc,char ** argv)32584f76c9cSknakahara intrctl_intr(int argc, char **argv)
32684f76c9cSknakahara {
32784f76c9cSknakahara struct intrio_set iset;
32884f76c9cSknakahara cpuset_t *cpuset;
32984f76c9cSknakahara unsigned long index;
33084f76c9cSknakahara int ch, error;
33184f76c9cSknakahara
33284f76c9cSknakahara index = ULONG_MAX;
33384f76c9cSknakahara
33484f76c9cSknakahara while ((ch = getopt(argc, argv, "c:")) != -1) {
33584f76c9cSknakahara switch (ch) {
33684f76c9cSknakahara case 'c':
33784f76c9cSknakahara index = strtoul(optarg, NULL, 10);
33884f76c9cSknakahara break;
33984f76c9cSknakahara default:
34084f76c9cSknakahara usage();
34184f76c9cSknakahara }
34284f76c9cSknakahara }
34384f76c9cSknakahara
34484f76c9cSknakahara if (index == ULONG_MAX)
34584f76c9cSknakahara usage();
34684f76c9cSknakahara
34784f76c9cSknakahara if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
34884f76c9cSknakahara err(EXIT_FAILURE, "invalid cpu index");
34984f76c9cSknakahara
35084f76c9cSknakahara cpuset = cpuset_create();
35184f76c9cSknakahara if (cpuset == NULL)
35284f76c9cSknakahara err(EXIT_FAILURE, "create_cpuset()");
35384f76c9cSknakahara
35484f76c9cSknakahara cpuset_zero(cpuset);
35584f76c9cSknakahara cpuset_set(index, cpuset);
35684f76c9cSknakahara iset.cpuset = cpuset;
35784f76c9cSknakahara iset.cpuset_size = cpuset_size(cpuset);
35884f76c9cSknakahara error = sysctlbyname("kern.intr.intr", NULL, NULL, &iset, sizeof(iset));
35984f76c9cSknakahara cpuset_destroy(cpuset);
36084f76c9cSknakahara if (error < 0)
36184f76c9cSknakahara err(EXIT_FAILURE, "sysctl kern.intr.intr");
36284f76c9cSknakahara }
36384f76c9cSknakahara
36484f76c9cSknakahara static void
intrctl_nointr(int argc,char ** argv)36584f76c9cSknakahara intrctl_nointr(int argc, char **argv)
36684f76c9cSknakahara {
36784f76c9cSknakahara struct intrio_set iset;
36884f76c9cSknakahara cpuset_t *cpuset;
36984f76c9cSknakahara unsigned long index;
37084f76c9cSknakahara int ch, error;
37184f76c9cSknakahara
37284f76c9cSknakahara index = ULONG_MAX;
37384f76c9cSknakahara
37484f76c9cSknakahara while ((ch = getopt(argc, argv, "c:")) != -1) {
37584f76c9cSknakahara switch (ch) {
37684f76c9cSknakahara case 'c':
37784f76c9cSknakahara index = strtoul(optarg, NULL, 10);
37884f76c9cSknakahara break;
37984f76c9cSknakahara default:
38084f76c9cSknakahara usage();
38184f76c9cSknakahara }
38284f76c9cSknakahara }
38384f76c9cSknakahara
38484f76c9cSknakahara if (index == ULONG_MAX)
38584f76c9cSknakahara usage();
38684f76c9cSknakahara
38784f76c9cSknakahara if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF))
38884f76c9cSknakahara err(EXIT_FAILURE, "invalid cpu index");
38984f76c9cSknakahara
39084f76c9cSknakahara cpuset = cpuset_create();
39184f76c9cSknakahara if (cpuset == NULL)
39284f76c9cSknakahara err(EXIT_FAILURE, "create_cpuset()");
39384f76c9cSknakahara
39484f76c9cSknakahara cpuset_zero(cpuset);
39584f76c9cSknakahara cpuset_set(index, cpuset);
39684f76c9cSknakahara iset.cpuset = cpuset;
39784f76c9cSknakahara iset.cpuset_size = cpuset_size(cpuset);
39884f76c9cSknakahara error = sysctlbyname("kern.intr.nointr", NULL, NULL, &iset, sizeof(iset));
39984f76c9cSknakahara cpuset_destroy(cpuset);
40084f76c9cSknakahara if (error < 0)
40184f76c9cSknakahara err(EXIT_FAILURE, "sysctl kern.intr.nointr");
40284f76c9cSknakahara }
403