1 /*- 2 * Copyright (c) 2010 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 u_int direct; 64 static u_int direct_force; 65 66 static struct sysctl_netisr_proto *proto_array; 67 static u_int proto_array_len; 68 69 static struct sysctl_netisr_workstream *workstream_array; 70 static u_int workstream_array_len; 71 72 static struct sysctl_netisr_work *work_array; 73 static u_int work_array_len; 74 75 static u_int *nws_array; 76 77 static u_int maxprot; 78 79 static void 80 netisr_load_kvm_uint(kvm_t *kd, char *name, u_int *p) 81 { 82 struct nlist nl[] = { 83 { .n_name = name }, 84 { .n_name = NULL }, 85 }; 86 int ret; 87 88 ret = kvm_nlist(kd, nl); 89 if (ret < 0) 90 errx(-1, "%s: kvm_nlist(%s): %s", __func__, name, 91 kvm_geterr(kd)); 92 if (ret != 0) 93 errx(-1, "%s: kvm_nlist(%s): unresolved symbol", __func__, 94 name); 95 if (kvm_read(kd, nl[0].n_value, p, sizeof(*p)) != sizeof(*p)) 96 errx(-1, "%s: kvm_read(%s): %s", __func__, name, 97 kvm_geterr(kd)); 98 } 99 100 /* 101 * Load a nul-terminated string from KVM up to 'limit', guarantee that the 102 * string in local memory is nul-terminated. 103 */ 104 static void 105 netisr_load_kvm_string(kvm_t *kd, uintptr_t addr, char *dest, u_int limit) 106 { 107 u_int i; 108 109 for (i = 0; i < limit; i++) { 110 if (kvm_read(kd, addr + i, &dest[i], sizeof(dest[i])) != 111 sizeof(dest[i])) 112 err(-1, "%s: kvm_read: %s", __func__, 113 kvm_geterr(kd)); 114 if (dest[i] == '\0') 115 break; 116 } 117 dest[limit - 1] = '\0'; 118 } 119 120 static const char * 121 netisr_proto2name(u_int proto) 122 { 123 u_int i; 124 125 for (i = 0; i < proto_array_len; i++) { 126 if (proto_array[i].snp_proto == proto) 127 return (proto_array[i].snp_name); 128 } 129 return ("unknown"); 130 } 131 132 static int 133 netisr_protoispresent(u_int proto) 134 { 135 u_int i; 136 137 for (i = 0; i < proto_array_len; i++) { 138 if (proto_array[i].snp_proto == proto) 139 return (1); 140 } 141 return (0); 142 } 143 144 static void 145 netisr_load_kvm_config(kvm_t *kd) 146 { 147 148 netisr_load_kvm_uint(kd, "_netisr_bindthreads", &bindthreads); 149 netisr_load_kvm_uint(kd, "_netisr_maxthreads", &maxthreads); 150 netisr_load_kvm_uint(kd, "_nws_count", &numthreads); 151 152 netisr_load_kvm_uint(kd, "_netisr_defaultqlimit", &defaultqlimit); 153 netisr_load_kvm_uint(kd, "_netisr_maxqlimit", &maxqlimit); 154 155 netisr_load_kvm_uint(kd, "_netisr_direct", &direct); 156 netisr_load_kvm_uint(kd, "_netisr_direct_force", &direct_force); 157 } 158 159 static void 160 netisr_load_sysctl_uint(const char *name, u_int *p) 161 { 162 size_t retlen; 163 164 retlen = sizeof(u_int); 165 if (sysctlbyname(name, p, &retlen, NULL, 0) < 0) 166 err(-1, "%s", name); 167 if (retlen != sizeof(u_int)) 168 errx(-1, "%s: invalid len %ju", name, (uintmax_t)retlen); 169 } 170 171 static void 172 netisr_load_sysctl_config(void) 173 { 174 175 netisr_load_sysctl_uint("net.isr.bindthreads", &bindthreads); 176 netisr_load_sysctl_uint("net.isr.maxthreads", &maxthreads); 177 netisr_load_sysctl_uint("net.isr.numthreads", &numthreads); 178 179 netisr_load_sysctl_uint("net.isr.defaultqlimit", &defaultqlimit); 180 netisr_load_sysctl_uint("net.isr.maxqlimit", &maxqlimit); 181 182 netisr_load_sysctl_uint("net.isr.direct", &direct); 183 netisr_load_sysctl_uint("net.isr.direct_force", &direct_force); 184 } 185 186 static void 187 netisr_load_kvm_proto(kvm_t *kd) 188 { 189 struct nlist nl[] = { 190 #define NLIST_NETISR_PROTO 0 191 { .n_name = "_netisr_proto" }, 192 { .n_name = NULL }, 193 }; 194 struct netisr_proto *np_array, *npp; 195 u_int i, protocount; 196 struct sysctl_netisr_proto *snpp; 197 size_t len; 198 int ret; 199 200 /* 201 * Kernel compile-time and user compile-time definitions of 202 * NETISR_MAXPROT must match, as we use that to size work arrays. 203 */ 204 netisr_load_kvm_uint(kd, "_netisr_maxprot", &maxprot); 205 if (maxprot != NETISR_MAXPROT) 206 errx(-1, "%s: NETISR_MAXPROT mismatch", __func__); 207 len = maxprot * sizeof(*np_array); 208 np_array = malloc(len); 209 if (np_array == NULL) 210 err(-1, "%s: malloc", __func__); 211 ret = kvm_nlist(kd, nl); 212 if (ret < 0) 213 errx(-1, "%s: kvm_nlist(_netisr_proto): %s", __func__, 214 kvm_geterr(kd)); 215 if (ret != 0) 216 errx(-1, "%s: kvm_nlist(_netisr_proto): unresolved symbol", 217 __func__); 218 if (kvm_read(kd, nl[NLIST_NETISR_PROTO].n_value, np_array, len) != 219 (ssize_t)len) 220 errx(-1, "%s: kvm_read(_netisr_proto): %s", __func__, 221 kvm_geterr(kd)); 222 223 /* 224 * Size and allocate memory to hold only live protocols. 225 */ 226 protocount = 0; 227 for (i = 0; i < maxprot; i++) { 228 if (np_array[i].np_name == NULL) 229 continue; 230 protocount++; 231 } 232 proto_array = calloc(protocount, sizeof(*proto_array)); 233 if (proto_array == NULL) 234 err(-1, "malloc"); 235 protocount = 0; 236 for (i = 0; i < maxprot; i++) { 237 npp = &np_array[i]; 238 if (npp->np_name == NULL) 239 continue; 240 snpp = &proto_array[protocount]; 241 snpp->snp_version = sizeof(*snpp); 242 netisr_load_kvm_string(kd, (uintptr_t)npp->np_name, 243 snpp->snp_name, sizeof(snpp->snp_name)); 244 snpp->snp_proto = i; 245 snpp->snp_qlimit = npp->np_qlimit; 246 snpp->snp_policy = npp->np_policy; 247 if (npp->np_m2flow != NULL) 248 snpp->snp_flags |= NETISR_SNP_FLAGS_M2FLOW; 249 if (npp->np_m2cpuid != NULL) 250 snpp->snp_flags |= NETISR_SNP_FLAGS_M2CPUID; 251 if (npp->np_drainedcpu != NULL) 252 snpp->snp_flags |= NETISR_SNP_FLAGS_DRAINEDCPU; 253 protocount++; 254 } 255 proto_array_len = protocount; 256 free(np_array); 257 } 258 259 static void 260 netisr_load_sysctl_proto(void) 261 { 262 size_t len; 263 264 if (sysctlbyname("net.isr.proto", NULL, &len, NULL, 0) < 0) 265 err(-1, "net.isr.proto: query len"); 266 if (len % sizeof(*proto_array) != 0) 267 errx(-1, "net.isr.proto: invalid len"); 268 proto_array = malloc(len); 269 if (proto_array == NULL) 270 err(-1, "malloc"); 271 if (sysctlbyname("net.isr.proto", proto_array, &len, NULL, 0) < 0) 272 err(-1, "net.isr.proto: query data"); 273 if (len % sizeof(*proto_array) != 0) 274 errx(-1, "net.isr.proto: invalid len"); 275 proto_array_len = len / sizeof(*proto_array); 276 if (proto_array_len < 1) 277 errx(-1, "net.isr.proto: no data"); 278 if (proto_array[0].snp_version != sizeof(proto_array[0])) 279 errx(-1, "net.isr.proto: invalid version"); 280 } 281 282 static void 283 netisr_load_kvm_workstream(kvm_t *kd) 284 { 285 struct nlist nl[] = { 286 #define NLIST_NWS_ARRAY 0 287 { .n_name = "_nws_array" }, 288 { .n_name = NULL }, 289 }; 290 struct netisr_workstream nws; 291 struct sysctl_netisr_workstream *snwsp; 292 struct sysctl_netisr_work *snwp; 293 struct netisr_work *nwp; 294 struct nlist nl_nws[2]; 295 u_int counter, cpuid, proto, wsid; 296 size_t len; 297 int ret; 298 299 len = numthreads * sizeof(*nws_array); 300 nws_array = malloc(len); 301 if (nws_array == NULL) 302 err(-1, "malloc"); 303 ret = kvm_nlist(kd, nl); 304 if (ret < 0) 305 errx(-1, "%s: kvm_nlist: %s", __func__, kvm_geterr(kd)); 306 if (ret != 0) 307 errx(-1, "%s: kvm_nlist: unresolved symbol", __func__); 308 if (kvm_read(kd, nl[NLIST_NWS_ARRAY].n_value, nws_array, len) != 309 (ssize_t)len) 310 errx(-1, "%s: kvm_read(_nws_array): %s", __func__, 311 kvm_geterr(kd)); 312 workstream_array = calloc(numthreads, sizeof(*workstream_array)); 313 if (workstream_array == NULL) 314 err(-1, "calloc"); 315 workstream_array_len = numthreads; 316 work_array = calloc(numthreads * proto_array_len, sizeof(*work_array)); 317 if (work_array == NULL) 318 err(-1, "calloc"); 319 counter = 0; 320 for (wsid = 0; wsid < numthreads; wsid++) { 321 cpuid = nws_array[wsid]; 322 if (kvm_dpcpu_setcpu(kd, cpuid) < 0) 323 errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__, 324 cpuid, kvm_geterr(kd)); 325 bzero(nl_nws, sizeof(nl_nws)); 326 nl_nws[0].n_name = "_nws"; 327 ret = kvm_nlist(kd, nl_nws); 328 if (ret < 0) 329 errx(-1, "%s: kvm_nlist looking up nws on CPU %u: %s", 330 __func__, cpuid, kvm_geterr(kd)); 331 if (ret != 0) 332 errx(-1, "%s: kvm_nlist(nws): unresolved symbol on " 333 "CPU %u", __func__, cpuid); 334 if (kvm_read(kd, nl_nws[0].n_value, &nws, sizeof(nws)) != 335 sizeof(nws)) 336 errx(-1, "%s: kvm_read(nw): %s", __func__, 337 kvm_geterr(kd)); 338 snwsp = &workstream_array[wsid]; 339 snwsp->snws_version = sizeof(*snwsp); 340 snwsp->snws_wsid = cpuid; 341 snwsp->snws_cpu = cpuid; 342 if (nws.nws_intr_event != NULL) 343 snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR; 344 345 /* 346 * Extract the CPU's per-protocol work information. 347 */ 348 printf("counting to maxprot: %u\n", maxprot); 349 for (proto = 0; proto < maxprot; proto++) { 350 if (!netisr_protoispresent(proto)) 351 continue; 352 nwp = &nws.nws_work[proto]; 353 snwp = &work_array[counter]; 354 snwp->snw_version = sizeof(*snwp); 355 snwp->snw_wsid = cpuid; 356 snwp->snw_proto = proto; 357 snwp->snw_len = nwp->nw_len; 358 snwp->snw_watermark = nwp->nw_watermark; 359 snwp->snw_dispatched = nwp->nw_dispatched; 360 snwp->snw_hybrid_dispatched = 361 nwp->nw_hybrid_dispatched; 362 snwp->snw_qdrops = nwp->nw_qdrops; 363 snwp->snw_queued = nwp->nw_queued; 364 snwp->snw_handled = nwp->nw_handled; 365 counter++; 366 } 367 } 368 work_array_len = counter; 369 } 370 371 static void 372 netisr_load_sysctl_workstream(void) 373 { 374 size_t len; 375 376 if (sysctlbyname("net.isr.workstream", NULL, &len, NULL, 0) < 0) 377 err(-1, "net.isr.workstream: query len"); 378 if (len % sizeof(*workstream_array) != 0) 379 errx(-1, "net.isr.workstream: invalid len"); 380 workstream_array = malloc(len); 381 if (workstream_array == NULL) 382 err(-1, "malloc"); 383 if (sysctlbyname("net.isr.workstream", workstream_array, &len, NULL, 384 0) < 0) 385 err(-1, "net.isr.workstream: query data"); 386 if (len % sizeof(*workstream_array) != 0) 387 errx(-1, "net.isr.workstream: invalid len"); 388 workstream_array_len = len / sizeof(*workstream_array); 389 if (workstream_array_len < 1) 390 errx(-1, "net.isr.workstream: no data"); 391 if (workstream_array[0].snws_version != sizeof(workstream_array[0])) 392 errx(-1, "net.isr.workstream: invalid version"); 393 } 394 395 static void 396 netisr_load_sysctl_work(void) 397 { 398 size_t len; 399 400 if (sysctlbyname("net.isr.work", NULL, &len, NULL, 0) < 0) 401 err(-1, "net.isr.work: query len"); 402 if (len % sizeof(*work_array) != 0) 403 errx(-1, "net.isr.work: invalid len"); 404 work_array = malloc(len); 405 if (work_array == NULL) 406 err(-1, "malloc"); 407 if (sysctlbyname("net.isr.work", work_array, &len, NULL, 0) < 0) 408 err(-1, "net.isr.work: query data"); 409 if (len % sizeof(*work_array) != 0) 410 errx(-1, "net.isr.work: invalid len"); 411 work_array_len = len / sizeof(*work_array); 412 if (work_array_len < 1) 413 errx(-1, "net.isr.work: no data"); 414 if (work_array[0].snw_version != sizeof(work_array[0])) 415 errx(-1, "net.isr.work: invalid version"); 416 } 417 418 static void 419 netisr_print_proto(struct sysctl_netisr_proto *snpp) 420 { 421 422 printf("%-6s", snpp->snp_name); 423 printf(" %5u", snpp->snp_proto); 424 printf(" %6u", snpp->snp_qlimit); 425 printf(" %6s", 426 (snpp->snp_policy == NETISR_POLICY_SOURCE) ? "source" : 427 (snpp->snp_policy == NETISR_POLICY_FLOW) ? "flow" : 428 (snpp->snp_policy == NETISR_POLICY_CPU) ? "cpu" : "-"); 429 printf(" %s%s%s\n", 430 (snpp->snp_flags & NETISR_SNP_FLAGS_M2CPUID) ? "C" : "-", 431 (snpp->snp_flags & NETISR_SNP_FLAGS_DRAINEDCPU) ? "D" : "-", 432 (snpp->snp_flags & NETISR_SNP_FLAGS_M2FLOW) ? "F" : "-"); 433 } 434 435 static void 436 netisr_print_workstream(struct sysctl_netisr_workstream *snwsp) 437 { 438 struct sysctl_netisr_work *snwp; 439 int first; 440 u_int i; 441 442 first = 1; 443 for (i = 0; i < work_array_len; i++) { 444 snwp = &work_array[i]; 445 if (snwp->snw_wsid != snwsp->snws_wsid) 446 continue; 447 if (first) { 448 printf("%4u", snwsp->snws_wsid); 449 printf(" %3u", snwsp->snws_cpu); 450 first = 0; 451 } else 452 printf("%4s %3s", "", ""); 453 printf("%2s", ""); 454 printf("%-6s", netisr_proto2name(snwp->snw_proto)); 455 printf(" %5u", snwp->snw_len); 456 printf(" %5u", snwp->snw_watermark); 457 printf(" %8ju", snwp->snw_dispatched); 458 printf(" %8ju", snwp->snw_hybrid_dispatched); 459 printf(" %8ju", snwp->snw_qdrops); 460 printf(" %8ju", snwp->snw_queued); 461 printf(" %8ju", snwp->snw_handled); 462 printf("\n"); 463 } 464 } 465 466 void 467 netisr_stats(void *kvmd) 468 { 469 struct sysctl_netisr_workstream *snwsp; 470 struct sysctl_netisr_proto *snpp; 471 kvm_t *kd = kvmd; 472 u_int i; 473 474 if (live) { 475 netisr_load_sysctl_config(); 476 netisr_load_sysctl_proto(); 477 netisr_load_sysctl_workstream(); 478 netisr_load_sysctl_work(); 479 } else { 480 if (kd == NULL) 481 errx(-1, "netisr_stats: !live but !kd"); 482 netisr_load_kvm_config(kd); 483 netisr_load_kvm_proto(kd); 484 netisr_load_kvm_workstream(kd); /* Also does work. */ 485 } 486 487 printf("Configuration:\n"); 488 printf("%-25s %12s %12s\n", "Setting", "Current", "Limit"); 489 printf("%-25s %12u %12u\n", "Thread count", numthreads, maxthreads); 490 printf("%-25s %12u %12u\n", "Default queue limit", defaultqlimit, 491 maxqlimit); 492 printf("%-25s %12s %12s\n", "Direct dispatch", 493 direct ? "enabled" : "disabled", "n/a"); 494 printf("%-25s %12s %12s\n", "Forced direct dispatch", 495 direct_force ? "enabled" : "disabled", "n/a"); 496 printf("%-25s %12s %12s\n", "Threads bound to CPUs", 497 bindthreads ? "enabled" : "disabled", "n/a"); 498 printf("\n"); 499 500 printf("Protocols:\n"); 501 printf("%-6s %5s %6s %-6s %-5s\n", "Name", "Proto", "QLimit", 502 "Policy", "Flags"); 503 for (i = 0; i < proto_array_len; i++) { 504 snpp = &proto_array[i]; 505 netisr_print_proto(snpp); 506 } 507 printf("\n"); 508 509 printf("Workstreams:\n"); 510 printf("%4s %3s ", "WSID", "CPU"); 511 printf("%2s", ""); 512 printf("%-6s %5s %5s %8s %8s %8s %8s %8s\n", "Name", "Len", "WMark", 513 "Disp'd", "HDisp'd", "QDrops", "Queued", "Handled"); 514 for (i = 0; i < workstream_array_len; i++) { 515 snwsp = &workstream_array[i]; 516 netisr_print_workstream(snwsp); 517 } 518 } 519