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