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