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