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
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
main(int argc,char ** argv)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
usage(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 bool
intrctl_list_line_allcpus(struct intrio_list_line * illine,int ncpus)114 intrctl_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
129 static void
intrctl_list_one(bool compact,bool skipzero)130 intrctl_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
244 static void
intrctl_list(int argc,char ** argv)245 intrctl_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
278 static void
intrctl_affinity(int argc,char ** argv)279 intrctl_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
324 static void
intrctl_intr(int argc,char ** argv)325 intrctl_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
364 static void
intrctl_nointr(int argc,char ** argv)365 intrctl_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