1 /* $NetBSD: intrctl.c,v 1.11 2019/09/24 11:31:06 wiz 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.11 2019/09/24 11:31:06 wiz 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 void 114 intrctl_list_one(bool compact, bool skipzero) 115 { 116 char buf[64]; 117 struct intrio_list_line *illine; 118 int i, ncpus, *cpucol; 119 void *handle; 120 size_t intridlen; 121 122 handle = intrctl_io_alloc(intrctl_io_alloc_retry_count); 123 if (handle == NULL) 124 err(EXIT_FAILURE, "intrctl_io_alloc"); 125 126 /* calc columns */ 127 ncpus = intrctl_io_ncpus(handle); 128 intridlen = strlen("interrupt id"); 129 for (illine = intrctl_io_firstline(handle); illine != NULL; 130 illine = intrctl_io_nextline(handle, illine)) { 131 size_t len = strlen(illine->ill_intrid); 132 if (intridlen < len) 133 intridlen = len; 134 } 135 136 cpucol = malloc(sizeof(*cpucol) * (size_t)ncpus); 137 if (cpucol == NULL) 138 err(EXIT_FAILURE, "malloc"); 139 for (i = 0; i < ncpus; i++) { 140 snprintf(buf, sizeof(buf), "CPU%u", i); 141 cpucol[i] = strlen(buf); 142 } 143 for (illine = intrctl_io_firstline(handle); illine != NULL; 144 illine = intrctl_io_nextline(handle, illine)) { 145 for (i = 0; i < ncpus; i++) { 146 int len; 147 snprintf(buf, sizeof(buf), "%" PRIu64, 148 illine->ill_cpu[i].illc_count); 149 len = (int)strlen(buf); 150 if (cpucol[i] < len) 151 cpucol[i] = len; 152 } 153 } 154 155 /* header */ 156 printf("%-*s ", (int)intridlen, "interrupt id"); 157 if (compact) { 158 printf("%20s ", "total"); 159 printf("%5s ", "aff"); 160 } else { 161 for (i = 0; i < ncpus; i++) { 162 snprintf(buf, sizeof(buf), "CPU%u", i); 163 printf("%*s ", cpucol[i], buf); 164 } 165 } 166 printf("device name(s)\n"); 167 168 /* body */ 169 for (illine = intrctl_io_firstline(handle); illine != NULL; 170 illine = intrctl_io_nextline(handle, illine)) { 171 struct intrio_list_line_cpu *illc; 172 173 if (skipzero) { 174 bool is_zero = true; 175 176 for (i = 0; i < ncpus; i++) { 177 illc = &illine->ill_cpu[i]; 178 if (illc->illc_count != 0) { 179 is_zero = false; 180 break; 181 } 182 } 183 if (is_zero) 184 continue; 185 } 186 187 printf("%-*s ", (int)intridlen, illine->ill_intrid); 188 if (compact) { 189 uint64_t total = 0; 190 char *affinity = NULL, *oaffinity = NULL; 191 for (i = 0; i < ncpus; i++) { 192 illc = &illine->ill_cpu[i]; 193 total += illc->illc_count; 194 if (illc->illc_assigned) { 195 asprintf(&affinity, "%s%s%d", 196 oaffinity ? oaffinity : "", 197 oaffinity ? ", " : "", 198 i); 199 if (oaffinity) 200 free(oaffinity); 201 oaffinity = affinity; 202 } 203 } 204 printf("%20" PRIu64 " ", total); 205 printf("%5s ", affinity ? affinity : "none"); 206 if (affinity) 207 free(affinity); 208 } else { 209 for (i = 0; i < ncpus; i++) { 210 illc = &illine->ill_cpu[i]; 211 printf("%*" PRIu64 "%c ", cpucol[i], illc->illc_count, 212 illc->illc_assigned ? '*' : ' '); 213 } 214 } 215 printf("%s\n", illine->ill_xname); 216 } 217 218 free(cpucol); 219 intrctl_io_free(handle); 220 } 221 222 static void 223 intrctl_list(int argc, char **argv) 224 { 225 int seconds = 0; 226 bool compact = false; 227 bool skipzero = false; 228 int ch; 229 230 while ((ch = getopt(argc, argv, "cw:z")) != -1) { 231 switch (ch) { 232 case 'c': 233 compact = true; 234 break; 235 case 'z': 236 skipzero = true; 237 break; 238 case 'w': 239 seconds = atoi(optarg); 240 if (seconds < 0) 241 errx(1, "seconds must be positive."); 242 break; 243 default: 244 usage(); 245 } 246 } 247 248 for (;;) { 249 intrctl_list_one(compact, skipzero); 250 if (seconds == 0) 251 break; 252 sleep(seconds); 253 } 254 } 255 256 static void 257 intrctl_affinity(int argc, char **argv) 258 { 259 struct intrio_set iset; 260 cpuset_t *cpuset; 261 unsigned long index; 262 int ch, error; 263 264 index = ULONG_MAX; 265 memset(&iset.intrid, 0, sizeof(iset.intrid)); 266 267 while ((ch = getopt(argc, argv, "c:i:")) != -1) { 268 switch (ch) { 269 case 'c': 270 index = strtoul(optarg, NULL, 10); 271 break; 272 case 'i': 273 if (strnlen(optarg, ARG_MAX) > INTRIDBUF) 274 usage(); 275 strlcpy(iset.intrid, optarg, INTRIDBUF); 276 break; 277 default: 278 usage(); 279 } 280 } 281 282 if (iset.intrid[0] == '\0' || index == ULONG_MAX) 283 usage(); 284 285 if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF)) 286 err(EXIT_FAILURE, "invalid cpu index"); 287 288 cpuset = cpuset_create(); 289 if (cpuset == NULL) 290 err(EXIT_FAILURE, "create_cpuset()"); 291 292 cpuset_zero(cpuset); 293 cpuset_set(index, cpuset); 294 iset.cpuset = cpuset; 295 iset.cpuset_size = cpuset_size(cpuset); 296 error = sysctlbyname("kern.intr.affinity", NULL, NULL, &iset, sizeof(iset)); 297 cpuset_destroy(cpuset); 298 if (error < 0) 299 err(EXIT_FAILURE, "sysctl kern.intr.affinity"); 300 } 301 302 static void 303 intrctl_intr(int argc, char **argv) 304 { 305 struct intrio_set iset; 306 cpuset_t *cpuset; 307 unsigned long index; 308 int ch, error; 309 310 index = ULONG_MAX; 311 312 while ((ch = getopt(argc, argv, "c:")) != -1) { 313 switch (ch) { 314 case 'c': 315 index = strtoul(optarg, NULL, 10); 316 break; 317 default: 318 usage(); 319 } 320 } 321 322 if (index == ULONG_MAX) 323 usage(); 324 325 if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF)) 326 err(EXIT_FAILURE, "invalid cpu index"); 327 328 cpuset = cpuset_create(); 329 if (cpuset == NULL) 330 err(EXIT_FAILURE, "create_cpuset()"); 331 332 cpuset_zero(cpuset); 333 cpuset_set(index, cpuset); 334 iset.cpuset = cpuset; 335 iset.cpuset_size = cpuset_size(cpuset); 336 error = sysctlbyname("kern.intr.intr", NULL, NULL, &iset, sizeof(iset)); 337 cpuset_destroy(cpuset); 338 if (error < 0) 339 err(EXIT_FAILURE, "sysctl kern.intr.intr"); 340 } 341 342 static void 343 intrctl_nointr(int argc, char **argv) 344 { 345 struct intrio_set iset; 346 cpuset_t *cpuset; 347 unsigned long index; 348 int ch, error; 349 350 index = ULONG_MAX; 351 352 while ((ch = getopt(argc, argv, "c:")) != -1) { 353 switch (ch) { 354 case 'c': 355 index = strtoul(optarg, NULL, 10); 356 break; 357 default: 358 usage(); 359 } 360 } 361 362 if (index == ULONG_MAX) 363 usage(); 364 365 if (index >= (u_long)sysconf(_SC_NPROCESSORS_CONF)) 366 err(EXIT_FAILURE, "invalid cpu index"); 367 368 cpuset = cpuset_create(); 369 if (cpuset == NULL) 370 err(EXIT_FAILURE, "create_cpuset()"); 371 372 cpuset_zero(cpuset); 373 cpuset_set(index, cpuset); 374 iset.cpuset = cpuset; 375 iset.cpuset_size = cpuset_size(cpuset); 376 error = sysctlbyname("kern.intr.nointr", NULL, NULL, &iset, sizeof(iset)); 377 cpuset_destroy(cpuset); 378 if (error < 0) 379 err(EXIT_FAILURE, "sysctl kern.intr.nointr"); 380 } 381