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