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