intrctl.c revision 1.8 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