1 /*- 2 * Copyright (c) 2010-2011 Juniper Networks, Inc. 3 * All rights reserved. 4 * 5 * This software was developed by Robert N. M. Watson under contract 6 * to Juniper Networks, Inc. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/sysctl.h> 36 37 #include <sys/_lock.h> 38 #include <sys/_mutex.h> 39 40 #define _WANT_NETISR_INTERNAL 41 #include <net/netisr.h> 42 #include <net/netisr_internal.h> 43 44 #include <err.h> 45 #include <kvm.h> 46 #include <stdint.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 51 #include "netstat.h" 52 53 /* 54 * Print statistics for the kernel netisr subsystem. 55 */ 56 static u_int bindthreads; 57 static u_int maxthreads; 58 static u_int numthreads; 59 60 static u_int defaultqlimit; 61 static u_int maxqlimit; 62 63 static char dispatch_policy[20]; 64 65 static struct sysctl_netisr_proto *proto_array; 66 static u_int proto_array_len; 67 68 static struct sysctl_netisr_workstream *workstream_array; 69 static u_int workstream_array_len; 70 71 static struct sysctl_netisr_work *work_array; 72 static u_int work_array_len; 73 74 static u_int *nws_array; 75 76 static u_int maxprot; 77 78 static void 79 netisr_dispatch_policy_to_string(u_int policy, char *buf, 80 size_t buflen) 81 { 82 const char *str; 83 84 switch (policy) { 85 case NETISR_DISPATCH_DEFAULT: 86 str = "default"; 87 break; 88 case NETISR_DISPATCH_DEFERRED: 89 str = "deferred"; 90 break; 91 case NETISR_DISPATCH_HYBRID: 92 str = "hybrid"; 93 break; 94 case NETISR_DISPATCH_DIRECT: 95 str = "direct"; 96 break; 97 default: 98 str = "unknown"; 99 break; 100 } 101 snprintf(buf, buflen, "%s", str); 102 } 103 104 static void 105 netisr_load_kvm_uint(kvm_t *kd, const char *name, u_int *p) 106 { 107 struct nlist nl[] = { 108 { .n_name = name }, 109 { .n_name = NULL }, 110 }; 111 int ret; 112 113 ret = kvm_nlist(kd, nl); 114 if (ret < 0) 115 errx(-1, "%s: kvm_nlist(%s): %s", __func__, name, 116 kvm_geterr(kd)); 117 if (ret != 0) 118 errx(-1, "%s: kvm_nlist(%s): unresolved symbol", __func__, 119 name); 120 if (kvm_read(kd, nl[0].n_value, p, sizeof(*p)) != sizeof(*p)) 121 errx(-1, "%s: kvm_read(%s): %s", __func__, name, 122 kvm_geterr(kd)); 123 } 124 125 /* 126 * Load a nul-terminated string from KVM up to 'limit', guarantee that the 127 * string in local memory is nul-terminated. 128 */ 129 static void 130 netisr_load_kvm_string(kvm_t *kd, uintptr_t addr, char *dest, u_int limit) 131 { 132 u_int i; 133 134 for (i = 0; i < limit; i++) { 135 if (kvm_read(kd, addr + i, &dest[i], sizeof(dest[i])) != 136 sizeof(dest[i])) 137 err(-1, "%s: kvm_read: %s", __func__, 138 kvm_geterr(kd)); 139 if (dest[i] == '\0') 140 break; 141 } 142 dest[limit - 1] = '\0'; 143 } 144 145 static const char * 146 netisr_proto2name(u_int proto) 147 { 148 u_int i; 149 150 for (i = 0; i < proto_array_len; i++) { 151 if (proto_array[i].snp_proto == proto) 152 return (proto_array[i].snp_name); 153 } 154 return ("unknown"); 155 } 156 157 static int 158 netisr_protoispresent(u_int proto) 159 { 160 u_int i; 161 162 for (i = 0; i < proto_array_len; i++) { 163 if (proto_array[i].snp_proto == proto) 164 return (1); 165 } 166 return (0); 167 } 168 169 static void 170 netisr_load_kvm_config(kvm_t *kd) 171 { 172 u_int tmp; 173 174 netisr_load_kvm_uint(kd, "_netisr_bindthreads", &bindthreads); 175 netisr_load_kvm_uint(kd, "_netisr_maxthreads", &maxthreads); 176 netisr_load_kvm_uint(kd, "_nws_count", &numthreads); 177 178 netisr_load_kvm_uint(kd, "_netisr_defaultqlimit", &defaultqlimit); 179 netisr_load_kvm_uint(kd, "_netisr_maxqlimit", &maxqlimit); 180 181 netisr_load_kvm_uint(kd, "_netisr_dispatch_policy", &tmp); 182 netisr_dispatch_policy_to_string(tmp, dispatch_policy, 183 sizeof(dispatch_policy)); 184 } 185 186 static void 187 netisr_load_sysctl_uint(const char *name, u_int *p) 188 { 189 size_t retlen; 190 191 retlen = sizeof(u_int); 192 if (sysctlbyname(name, p, &retlen, NULL, 0) < 0) 193 err(-1, "%s", name); 194 if (retlen != sizeof(u_int)) 195 errx(-1, "%s: invalid len %ju", name, (uintmax_t)retlen); 196 } 197 198 static void 199 netisr_load_sysctl_string(const char *name, char *p, size_t len) 200 { 201 size_t retlen; 202 203 retlen = len; 204 if (sysctlbyname(name, p, &retlen, NULL, 0) < 0) 205 err(-1, "%s", name); 206 p[len - 1] = '\0'; 207 } 208 209 static void 210 netisr_load_sysctl_config(void) 211 { 212 213 netisr_load_sysctl_uint("net.isr.bindthreads", &bindthreads); 214 netisr_load_sysctl_uint("net.isr.maxthreads", &maxthreads); 215 netisr_load_sysctl_uint("net.isr.numthreads", &numthreads); 216 217 netisr_load_sysctl_uint("net.isr.defaultqlimit", &defaultqlimit); 218 netisr_load_sysctl_uint("net.isr.maxqlimit", &maxqlimit); 219 220 netisr_load_sysctl_string("net.isr.dispatch", dispatch_policy, 221 sizeof(dispatch_policy)); 222 } 223 224 static void 225 netisr_load_kvm_proto(kvm_t *kd) 226 { 227 struct nlist nl[] = { 228 #define NLIST_NETISR_PROTO 0 229 { .n_name = "_netisr_proto" }, 230 { .n_name = NULL }, 231 }; 232 struct netisr_proto *np_array, *npp; 233 u_int i, protocount; 234 struct sysctl_netisr_proto *snpp; 235 size_t len; 236 int ret; 237 238 /* 239 * Kernel compile-time and user compile-time definitions of 240 * NETISR_MAXPROT must match, as we use that to size work arrays. 241 */ 242 netisr_load_kvm_uint(kd, "_netisr_maxprot", &maxprot); 243 if (maxprot != NETISR_MAXPROT) 244 errx(-1, "%s: NETISR_MAXPROT mismatch", __func__); 245 len = maxprot * sizeof(*np_array); 246 np_array = malloc(len); 247 if (np_array == NULL) 248 err(-1, "%s: malloc", __func__); 249 ret = kvm_nlist(kd, nl); 250 if (ret < 0) 251 errx(-1, "%s: kvm_nlist(_netisr_proto): %s", __func__, 252 kvm_geterr(kd)); 253 if (ret != 0) 254 errx(-1, "%s: kvm_nlist(_netisr_proto): unresolved symbol", 255 __func__); 256 if (kvm_read(kd, nl[NLIST_NETISR_PROTO].n_value, np_array, len) != 257 (ssize_t)len) 258 errx(-1, "%s: kvm_read(_netisr_proto): %s", __func__, 259 kvm_geterr(kd)); 260 261 /* 262 * Size and allocate memory to hold only live protocols. 263 */ 264 protocount = 0; 265 for (i = 0; i < maxprot; i++) { 266 if (np_array[i].np_name == NULL) 267 continue; 268 protocount++; 269 } 270 proto_array = calloc(protocount, sizeof(*proto_array)); 271 if (proto_array == NULL) 272 err(-1, "malloc"); 273 protocount = 0; 274 for (i = 0; i < maxprot; i++) { 275 npp = &np_array[i]; 276 if (npp->np_name == NULL) 277 continue; 278 snpp = &proto_array[protocount]; 279 snpp->snp_version = sizeof(*snpp); 280 netisr_load_kvm_string(kd, (uintptr_t)npp->np_name, 281 snpp->snp_name, sizeof(snpp->snp_name)); 282 snpp->snp_proto = i; 283 snpp->snp_qlimit = npp->np_qlimit; 284 snpp->snp_policy = npp->np_policy; 285 snpp->snp_dispatch = npp->np_dispatch; 286 if (npp->np_m2flow != NULL) 287 snpp->snp_flags |= NETISR_SNP_FLAGS_M2FLOW; 288 if (npp->np_m2cpuid != NULL) 289 snpp->snp_flags |= NETISR_SNP_FLAGS_M2CPUID; 290 if (npp->np_drainedcpu != NULL) 291 snpp->snp_flags |= NETISR_SNP_FLAGS_DRAINEDCPU; 292 protocount++; 293 } 294 proto_array_len = protocount; 295 free(np_array); 296 } 297 298 static void 299 netisr_load_sysctl_proto(void) 300 { 301 size_t len; 302 303 if (sysctlbyname("net.isr.proto", NULL, &len, NULL, 0) < 0) 304 err(-1, "net.isr.proto: query len"); 305 if (len % sizeof(*proto_array) != 0) 306 errx(-1, "net.isr.proto: invalid len"); 307 proto_array = malloc(len); 308 if (proto_array == NULL) 309 err(-1, "malloc"); 310 if (sysctlbyname("net.isr.proto", proto_array, &len, NULL, 0) < 0) 311 err(-1, "net.isr.proto: query data"); 312 if (len % sizeof(*proto_array) != 0) 313 errx(-1, "net.isr.proto: invalid len"); 314 proto_array_len = len / sizeof(*proto_array); 315 if (proto_array_len < 1) 316 errx(-1, "net.isr.proto: no data"); 317 if (proto_array[0].snp_version != sizeof(proto_array[0])) 318 errx(-1, "net.isr.proto: invalid version"); 319 } 320 321 static void 322 netisr_load_kvm_workstream(kvm_t *kd) 323 { 324 struct nlist nl[] = { 325 #define NLIST_NWS_ARRAY 0 326 { .n_name = "_nws_array" }, 327 { .n_name = NULL }, 328 }; 329 struct netisr_workstream nws; 330 struct sysctl_netisr_workstream *snwsp; 331 struct sysctl_netisr_work *snwp; 332 struct netisr_work *nwp; 333 struct nlist nl_nws[2]; 334 u_int counter, cpuid, proto, wsid; 335 size_t len; 336 int ret; 337 338 len = numthreads * sizeof(*nws_array); 339 nws_array = malloc(len); 340 if (nws_array == NULL) 341 err(-1, "malloc"); 342 ret = kvm_nlist(kd, nl); 343 if (ret < 0) 344 errx(-1, "%s: kvm_nlist: %s", __func__, kvm_geterr(kd)); 345 if (ret != 0) 346 errx(-1, "%s: kvm_nlist: unresolved symbol", __func__); 347 if (kvm_read(kd, nl[NLIST_NWS_ARRAY].n_value, nws_array, len) != 348 (ssize_t)len) 349 errx(-1, "%s: kvm_read(_nws_array): %s", __func__, 350 kvm_geterr(kd)); 351 workstream_array = calloc(numthreads, sizeof(*workstream_array)); 352 if (workstream_array == NULL) 353 err(-1, "calloc"); 354 workstream_array_len = numthreads; 355 work_array = calloc(numthreads * proto_array_len, sizeof(*work_array)); 356 if (work_array == NULL) 357 err(-1, "calloc"); 358 counter = 0; 359 for (wsid = 0; wsid < numthreads; wsid++) { 360 cpuid = nws_array[wsid]; 361 if (kvm_dpcpu_setcpu(kd, cpuid) < 0) 362 errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__, 363 cpuid, kvm_geterr(kd)); 364 bzero(nl_nws, sizeof(nl_nws)); 365 nl_nws[0].n_name = "_nws"; 366 ret = kvm_nlist(kd, nl_nws); 367 if (ret < 0) 368 errx(-1, "%s: kvm_nlist looking up nws on CPU %u: %s", 369 __func__, cpuid, kvm_geterr(kd)); 370 if (ret != 0) 371 errx(-1, "%s: kvm_nlist(nws): unresolved symbol on " 372 "CPU %u", __func__, cpuid); 373 if (kvm_read(kd, nl_nws[0].n_value, &nws, sizeof(nws)) != 374 sizeof(nws)) 375 errx(-1, "%s: kvm_read(nw): %s", __func__, 376 kvm_geterr(kd)); 377 snwsp = &workstream_array[wsid]; 378 snwsp->snws_version = sizeof(*snwsp); 379 snwsp->snws_wsid = cpuid; 380 snwsp->snws_cpu = cpuid; 381 if (nws.nws_intr_event != NULL) 382 snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR; 383 384 /* 385 * Extract the CPU's per-protocol work information. 386 */ 387 printf("counting to maxprot: %u\n", maxprot); 388 for (proto = 0; proto < maxprot; proto++) { 389 if (!netisr_protoispresent(proto)) 390 continue; 391 nwp = &nws.nws_work[proto]; 392 snwp = &work_array[counter]; 393 snwp->snw_version = sizeof(*snwp); 394 snwp->snw_wsid = cpuid; 395 snwp->snw_proto = proto; 396 snwp->snw_len = nwp->nw_len; 397 snwp->snw_watermark = nwp->nw_watermark; 398 snwp->snw_dispatched = nwp->nw_dispatched; 399 snwp->snw_hybrid_dispatched = 400 nwp->nw_hybrid_dispatched; 401 snwp->snw_qdrops = nwp->nw_qdrops; 402 snwp->snw_queued = nwp->nw_queued; 403 snwp->snw_handled = nwp->nw_handled; 404 counter++; 405 } 406 } 407 work_array_len = counter; 408 } 409 410 static void 411 netisr_load_sysctl_workstream(void) 412 { 413 size_t len; 414 415 if (sysctlbyname("net.isr.workstream", NULL, &len, NULL, 0) < 0) 416 err(-1, "net.isr.workstream: query len"); 417 if (len % sizeof(*workstream_array) != 0) 418 errx(-1, "net.isr.workstream: invalid len"); 419 workstream_array = malloc(len); 420 if (workstream_array == NULL) 421 err(-1, "malloc"); 422 if (sysctlbyname("net.isr.workstream", workstream_array, &len, NULL, 423 0) < 0) 424 err(-1, "net.isr.workstream: query data"); 425 if (len % sizeof(*workstream_array) != 0) 426 errx(-1, "net.isr.workstream: invalid len"); 427 workstream_array_len = len / sizeof(*workstream_array); 428 if (workstream_array_len < 1) 429 errx(-1, "net.isr.workstream: no data"); 430 if (workstream_array[0].snws_version != sizeof(workstream_array[0])) 431 errx(-1, "net.isr.workstream: invalid version"); 432 } 433 434 static void 435 netisr_load_sysctl_work(void) 436 { 437 size_t len; 438 439 if (sysctlbyname("net.isr.work", NULL, &len, NULL, 0) < 0) 440 err(-1, "net.isr.work: query len"); 441 if (len % sizeof(*work_array) != 0) 442 errx(-1, "net.isr.work: invalid len"); 443 work_array = malloc(len); 444 if (work_array == NULL) 445 err(-1, "malloc"); 446 if (sysctlbyname("net.isr.work", work_array, &len, NULL, 0) < 0) 447 err(-1, "net.isr.work: query data"); 448 if (len % sizeof(*work_array) != 0) 449 errx(-1, "net.isr.work: invalid len"); 450 work_array_len = len / sizeof(*work_array); 451 if (work_array_len < 1) 452 errx(-1, "net.isr.work: no data"); 453 if (work_array[0].snw_version != sizeof(work_array[0])) 454 errx(-1, "net.isr.work: invalid version"); 455 } 456 457 static void 458 netisr_print_proto(struct sysctl_netisr_proto *snpp) 459 { 460 char tmp[20]; 461 462 printf("%-6s", snpp->snp_name); 463 printf(" %5u", snpp->snp_proto); 464 printf(" %6u", snpp->snp_qlimit); 465 printf(" %6s", 466 (snpp->snp_policy == NETISR_POLICY_SOURCE) ? "source" : 467 (snpp->snp_policy == NETISR_POLICY_FLOW) ? "flow" : 468 (snpp->snp_policy == NETISR_POLICY_CPU) ? "cpu" : "-"); 469 netisr_dispatch_policy_to_string(snpp->snp_dispatch, tmp, 470 sizeof(tmp)); 471 printf(" %8s", tmp); 472 printf(" %s%s%s\n", 473 (snpp->snp_flags & NETISR_SNP_FLAGS_M2CPUID) ? "C" : "-", 474 (snpp->snp_flags & NETISR_SNP_FLAGS_DRAINEDCPU) ? "D" : "-", 475 (snpp->snp_flags & NETISR_SNP_FLAGS_M2FLOW) ? "F" : "-"); 476 } 477 478 static void 479 netisr_print_workstream(struct sysctl_netisr_workstream *snwsp) 480 { 481 struct sysctl_netisr_work *snwp; 482 u_int i; 483 484 for (i = 0; i < work_array_len; i++) { 485 snwp = &work_array[i]; 486 if (snwp->snw_wsid != snwsp->snws_wsid) 487 continue; 488 printf("%4u ", snwsp->snws_wsid); 489 printf("%3u ", snwsp->snws_cpu); 490 printf("%2s", ""); 491 printf("%-6s", netisr_proto2name(snwp->snw_proto)); 492 printf(" %5u", snwp->snw_len); 493 printf(" %5u", snwp->snw_watermark); 494 printf(" %8ju", snwp->snw_dispatched); 495 printf(" %8ju", snwp->snw_hybrid_dispatched); 496 printf(" %8ju", snwp->snw_qdrops); 497 printf(" %8ju", snwp->snw_queued); 498 printf(" %8ju", snwp->snw_handled); 499 printf("\n"); 500 } 501 } 502 503 void 504 netisr_stats(void *kvmd) 505 { 506 struct sysctl_netisr_workstream *snwsp; 507 struct sysctl_netisr_proto *snpp; 508 kvm_t *kd = kvmd; 509 u_int i; 510 511 if (live) { 512 netisr_load_sysctl_config(); 513 netisr_load_sysctl_proto(); 514 netisr_load_sysctl_workstream(); 515 netisr_load_sysctl_work(); 516 } else { 517 if (kd == NULL) 518 errx(-1, "netisr_stats: !live but !kd"); 519 netisr_load_kvm_config(kd); 520 netisr_load_kvm_proto(kd); 521 netisr_load_kvm_workstream(kd); /* Also does work. */ 522 } 523 524 printf("Configuration:\n"); 525 printf("%-25s %12s %12s\n", "Setting", "Current", "Limit"); 526 printf("%-25s %12u %12u\n", "Thread count", numthreads, maxthreads); 527 printf("%-25s %12u %12u\n", "Default queue limit", defaultqlimit, 528 maxqlimit); 529 printf("%-25s %12s %12s\n", "Dispatch policy", dispatch_policy, 530 "n/a"); 531 printf("%-25s %12s %12s\n", "Threads bound to CPUs", 532 bindthreads ? "enabled" : "disabled", "n/a"); 533 printf("\n"); 534 535 printf("Protocols:\n"); 536 printf("%-6s %5s %6s %-6s %-8s %-5s\n", "Name", "Proto", "QLimit", 537 "Policy", "Dispatch", "Flags"); 538 for (i = 0; i < proto_array_len; i++) { 539 snpp = &proto_array[i]; 540 netisr_print_proto(snpp); 541 } 542 printf("\n"); 543 544 printf("Workstreams:\n"); 545 printf("%4s %3s ", "WSID", "CPU"); 546 printf("%2s", ""); 547 printf("%-6s %5s %5s %8s %8s %8s %8s %8s\n", "Name", "Len", "WMark", 548 "Disp'd", "HDisp'd", "QDrops", "Queued", "Handled"); 549 for (i = 0; i < workstream_array_len; i++) { 550 snwsp = &workstream_array[i]; 551 netisr_print_workstream(snwsp); 552 } 553 } 554