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