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 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 [-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 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 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 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 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 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 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