1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved. 24 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 25 */ 26 27 /* 28 * smbstat: Server Message Block File System statistics 29 * 30 * The statistics this CLI displays come from two sources: 31 * 32 * 1) The kernel module 'smbsrv'. 33 * 2) The SMB workers task queue statistics the task queue manager of Solaris 34 * maintains. 35 * 36 * The flow of the code is the following: 37 * 38 * 39 * +----------------+ 40 * | Initialization | 41 * +----------------+ 42 * | 43 * | 44 * v 45 * +--------------------------* 46 * | Take a snapshot the data | <--------+ 47 * +--------------------------+ | 48 * | | 49 * | | 50 * v | 51 * +----------------------+ | 52 * | Process the snapshot | | 53 * +----------------------+ | 54 * | | 55 * | | 56 * v | 57 * +------------------------------------+ | 58 * | Print the result of the processing | | 59 * +------------------------------------+ | 60 * | | 61 * | | 62 * v | 63 * Yes --------------- | 64 * +------------ < interval == 0 ? > | 65 * | --------------- | 66 * | | | 67 * | | No | 68 * | v | 69 * | +------------------------+ | 70 * | | Sleep for the duration | ----------+ 71 * | | of the interval. | 72 * | +------------------------+ 73 * | 74 * +---------------------+ 75 * | 76 * v 77 * 78 * Exit 79 * 80 * There are two sets of snapshots. One set for the smbsrv module and the other 81 * for the task queue (SMB workers). Each set contains 2 snapshots. One is 82 * labeled 'current' the other one 'previous'. Their role changes after each 83 * snapshot. The 'current' becomes 'previous' and vice versa. 84 * The first snapshot taken is compared against the data gathered since the 85 * smbsrv module was loaded. Subsequent snapshots will be compared against the 86 * previous snapshot. 87 */ 88 89 #include <stdio.h> 90 #include <stdlib.h> 91 #include <unistd.h> 92 #include <kstat.h> 93 #include <stdarg.h> 94 #include <errno.h> 95 #include <inttypes.h> 96 #include <strings.h> 97 #include <utility.h> 98 #include <libintl.h> 99 #include <zone.h> 100 #include <termios.h> 101 #include <stropts.h> 102 #include <math.h> 103 #include <umem.h> 104 #include <locale.h> 105 #include <smbsrv/smb_kstat.h> 106 107 #if !defined(TEXT_DOMAIN) 108 #define TEXT_DOMAIN "SYS_TEST" 109 #endif /* TEXT_DOMAIN */ 110 111 #define SMBSTAT_ID_NO_CPU -1 112 #define SMBSTAT_SNAPSHOT_COUNT 2 /* Must be a power of 2 */ 113 #define SMBSTAT_SNAPSHOT_MASK (SMBSTAT_SNAPSHOT_COUNT - 1) 114 115 #define SMBSTAT_HELP \ 116 "Usage: smbstat [-acnrtuz] [interval]\n" \ 117 " -c: display counters\n" \ 118 " -t: display throughput\n" \ 119 " -u: display utilization\n" \ 120 " -r: display requests\n" \ 121 " -a: all the requests (supported and unsupported)\n" \ 122 " -z: skip the requests not received\n" \ 123 " -n: display in alphabetic order\n" \ 124 " interval: refresh cycle in seconds\n" 125 126 #define SMBSRV_COUNTERS_BANNER "\n nbt tcp users trees files pipes\n" 127 #define SMBSRV_COUNTERS_FORMAT "%5d %5d %5d %5d %5d %5d\n" 128 129 #define SMBSRV_THROUGHPUT_BANNER \ 130 "\nrbytes/s tbytes/s reqs/s reads/s writes/s\n" 131 #define SMBSRV_THROUGHPUT_FORMAT \ 132 "%1.3e %1.3e %1.3e %1.3e %1.3e\n" 133 134 #define SMBSRV_UTILIZATION_BANNER \ 135 "\n wcnt rcnt wtime rtime" \ 136 " w%% r%% u%% sat usr%% sys%% idle%%\n" 137 #define SMBSRV_UTILIZATION_FORMAT \ 138 "%1.3e %1.3e %1.3e %1.3e %3.0f %3.0f %3.0f %s " \ 139 "%3.0f %3.0f %3.0f\n" 140 141 #define SMBSRV_REQUESTS_BANNER \ 142 "\n%30s code %% rbytes/s tbytes/s req/s rt-mean" \ 143 " rt-stddev\n" 144 #define SMBSRV_REQUESTS_FORMAT \ 145 "%30s %02X %3.0f %1.3e %1.3e %1.3e %1.3e %1.3e\n" 146 147 typedef enum { 148 CPU_TICKS_IDLE = 0, 149 CPU_TICKS_USER, 150 CPU_TICKS_KERNEL, 151 CPU_TICKS_SENTINEL 152 } cpu_state_idx_t; 153 154 typedef struct smbstat_cpu_snapshot { 155 processorid_t cs_id; 156 int cs_state; 157 uint64_t cs_ticks[CPU_TICKS_SENTINEL]; 158 } smbstat_cpu_snapshot_t; 159 160 typedef struct smbstat_srv_snapshot { 161 hrtime_t ss_snaptime; 162 smbsrv_kstats_t ss_data; 163 } smbstat_srv_snapshot_t; 164 165 typedef struct smbstat_wrk_snapshot { 166 uint64_t ws_maxthreads; 167 uint64_t ws_bnalloc; 168 } smbstat_wrk_snapshot_t; 169 170 typedef struct smbstat_req_info { 171 char ri_name[KSTAT_STRLEN]; 172 int ri_opcode; 173 double ri_pct; 174 double ri_tbs; 175 double ri_rbs; 176 double ri_rqs; 177 double ri_stddev; 178 double ri_mean; 179 } smbstat_req_info_t; 180 181 typedef struct smbstat_srv_info { 182 double si_hretime; 183 double si_etime; 184 double si_total_nreqs; 185 /* 186 * Counters 187 */ 188 uint32_t si_nbt_sess; /* NBT sessions */ 189 uint32_t si_tcp_sess; /* TCP sessions */ 190 uint32_t si_users; /* Users logged in */ 191 uint32_t si_trees; /* Trees connected */ 192 uint32_t si_files; /* Open files */ 193 uint32_t si_pipes; /* Open pipes */ 194 /* 195 * Throughput of the server 196 */ 197 double si_tbs; /* Bytes transmitted / second */ 198 double si_rbs; /* Bytes received / second */ 199 double si_rqs; /* Requests treated / second */ 200 double si_rds; /* Reads treated / second */ 201 double si_wrs; /* Writes treated / second */ 202 /* 203 * Utilization of the server 204 */ 205 double si_wpct; /* */ 206 double si_rpct; /* */ 207 double si_upct; /* Utilization in % */ 208 double si_avw; /* Average number of requests waiting */ 209 double si_avr; /* Average number of requests running */ 210 double si_wserv; /* Average waiting time */ 211 double si_rserv; /* Average running time */ 212 boolean_t si_sat; 213 double si_ticks[CPU_TICKS_SENTINEL]; 214 /* 215 * Latency & Throughput per request 216 */ 217 smbstat_req_info_t si_reqs[SMB_COM_NUM]; 218 } smbstat_srv_info_t; 219 220 static void smbstat_init(void); 221 static void smbstat_fini(void); 222 static void smbstat_kstat_snapshot(void); 223 static void smbstat_kstat_process(void); 224 static void smbstat_kstat_print(void); 225 226 static void smbstat_print_counters(void); 227 static void smbstat_print_throughput(void); 228 static void smbstat_print_utilization(void); 229 static void smbstat_print_requests(void); 230 231 static void smbstat_cpu_init(void); 232 static void smbstat_cpu_fini(void); 233 static smbstat_cpu_snapshot_t *smbstat_cpu_current_snapshot(void); 234 static smbstat_cpu_snapshot_t *smbstat_cpu_previous_snapshot(void); 235 static void smbstat_cpu_snapshot(void); 236 static void smbstat_cpu_process(void); 237 238 static void smbstat_wrk_init(void); 239 static void smbstat_wrk_fini(void); 240 static void smbstat_wrk_snapshot(void); 241 static void smbstat_wrk_process(void); 242 static smbstat_wrk_snapshot_t *smbstat_wrk_current_snapshot(void); 243 244 static void smbstat_srv_init(void); 245 static void smbstat_srv_fini(void); 246 static void smbstat_srv_snapshot(void); 247 static void smbstat_srv_process(void); 248 static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *); 249 static void smbstat_srv_process_throughput(smbstat_srv_snapshot_t *, 250 smbstat_srv_snapshot_t *); 251 static void smbstat_srv_process_utilization(smbstat_srv_snapshot_t *, 252 smbstat_srv_snapshot_t *); 253 static void smbstat_srv_process_requests(smbstat_srv_snapshot_t *, 254 smbstat_srv_snapshot_t *); 255 static smbstat_srv_snapshot_t *smbstat_srv_current_snapshot(void); 256 static smbstat_srv_snapshot_t *smbstat_srv_previous_snapshot(void); 257 258 static void *smbstat_zalloc(size_t); 259 static void smbstat_free(void *, size_t); 260 static void smbstat_fail(int, char *, ...); 261 static void smbstat_snapshot_inc_idx(void); 262 static void smbstat_usage(FILE *, int); 263 static uint_t smbstat_strtoi(char const *, char *); 264 static double smbstat_hrtime_delta(hrtime_t, hrtime_t); 265 static double smbstat_sub_64(uint64_t, uint64_t); 266 static void smbstat_req_order(void); 267 static double smbstat_zero(double); 268 static void smbstat_termio_init(void); 269 270 #pragma does_not_return(smbstat_fail, smbstat_usage) 271 272 static char *smbstat_cpu_states[CPU_TICKS_SENTINEL] = { 273 "cpu_ticks_idle", 274 "cpu_ticks_user", 275 "cpu_ticks_kernel" 276 }; 277 278 static boolean_t smbstat_opt_a = B_FALSE; /* all */ 279 static boolean_t smbstat_opt_c = B_FALSE; /* counters */ 280 static boolean_t smbstat_opt_n = B_FALSE; /* by name */ 281 static boolean_t smbstat_opt_u = B_FALSE; /* utilization */ 282 static boolean_t smbstat_opt_t = B_FALSE; /* throughput */ 283 static boolean_t smbstat_opt_r = B_FALSE; /* requests */ 284 static boolean_t smbstat_opt_z = B_FALSE; /* non-zero requests */ 285 286 static uint_t smbstat_interval = 0; 287 static long smbstat_nrcpus = 0; 288 static kstat_ctl_t *smbstat_ksc = NULL; 289 static kstat_t *smbstat_srv_ksp = NULL; 290 static kstat_t *smbstat_wrk_ksp = NULL; 291 static struct winsize smbstat_ws; 292 static uint16_t smbstat_rows = 0; 293 294 static int smbstat_snapshot_idx = 0; 295 static smbstat_cpu_snapshot_t *smbstat_cpu_snapshots[SMBSTAT_SNAPSHOT_COUNT]; 296 static smbstat_srv_snapshot_t smbstat_srv_snapshots[SMBSTAT_SNAPSHOT_COUNT]; 297 static smbstat_wrk_snapshot_t smbstat_wrk_snapshots[SMBSTAT_SNAPSHOT_COUNT]; 298 static smbstat_srv_info_t smbstat_srv_info; 299 300 /* 301 * main 302 */ 303 int 304 main(int argc, char *argv[]) 305 { 306 int c; 307 308 (void) setlocale(LC_ALL, ""); 309 (void) textdomain(TEXT_DOMAIN); 310 311 if (is_system_labeled()) { 312 (void) fprintf(stderr, 313 gettext("%s: Trusted Extensions not supported.\n"), 314 argv[0]); 315 return (1); 316 } 317 318 while ((c = getopt(argc, argv, "achnrtuz")) != EOF) { 319 switch (c) { 320 case 'a': 321 smbstat_opt_a = B_TRUE; 322 break; 323 case 'n': 324 smbstat_opt_n = B_TRUE; 325 break; 326 case 'u': 327 smbstat_opt_u = B_TRUE; 328 break; 329 case 'c': 330 smbstat_opt_c = B_TRUE; 331 break; 332 case 'r': 333 smbstat_opt_r = B_TRUE; 334 break; 335 case 't': 336 smbstat_opt_t = B_TRUE; 337 break; 338 case 'z': 339 smbstat_opt_z = B_TRUE; 340 break; 341 case 'h': 342 smbstat_usage(stdout, 0); 343 default: 344 smbstat_usage(stderr, 1); 345 } 346 } 347 348 if (!smbstat_opt_u && 349 !smbstat_opt_c && 350 !smbstat_opt_r && 351 !smbstat_opt_t) { 352 /* Default options when none is specified. */ 353 smbstat_opt_u = B_TRUE; 354 smbstat_opt_t = B_TRUE; 355 } 356 357 if (optind < argc) { 358 smbstat_interval = 359 smbstat_strtoi(argv[optind], "invalid count"); 360 optind++; 361 } 362 363 if ((argc - optind) > 1) 364 smbstat_usage(stderr, 1); 365 366 (void) atexit(smbstat_fini); 367 smbstat_init(); 368 for (;;) { 369 smbstat_kstat_snapshot(); 370 smbstat_kstat_process(); 371 smbstat_kstat_print(); 372 if (smbstat_interval == 0) 373 break; 374 (void) sleep(smbstat_interval); 375 smbstat_snapshot_inc_idx(); 376 } 377 return (0); 378 } 379 380 /* 381 * smbstat_init 382 * 383 * Global initialization. 384 */ 385 static void 386 smbstat_init(void) 387 { 388 if ((smbstat_ksc = kstat_open()) == NULL) 389 smbstat_fail(1, gettext("kstat_open(): can't open /dev/kstat")); 390 391 smbstat_cpu_init(); 392 smbstat_srv_init(); 393 smbstat_wrk_init(); 394 smbstat_req_order(); 395 } 396 397 /* 398 * smbstat_fini 399 * 400 * Releases the resources smbstat_init() allocated. 401 */ 402 static void 403 smbstat_fini(void) 404 { 405 smbstat_wrk_fini(); 406 smbstat_srv_fini(); 407 smbstat_cpu_fini(); 408 (void) kstat_close(smbstat_ksc); 409 } 410 411 /* 412 * smbstat_kstat_snapshot 413 * 414 * Takes a snapshot of the data. 415 */ 416 static void 417 smbstat_kstat_snapshot(void) 418 { 419 smbstat_cpu_snapshot(); 420 smbstat_srv_snapshot(); 421 smbstat_wrk_snapshot(); 422 } 423 424 /* 425 * smbstat_kstat_process 426 */ 427 static void 428 smbstat_kstat_process(void) 429 { 430 smbstat_cpu_process(); 431 smbstat_srv_process(); 432 smbstat_wrk_process(); 433 } 434 435 /* 436 * smbstat_kstat_print 437 * 438 * Print the data processed. 439 */ 440 static void 441 smbstat_kstat_print(void) 442 { 443 smbstat_termio_init(); 444 smbstat_print_counters(); 445 smbstat_print_throughput(); 446 smbstat_print_utilization(); 447 smbstat_print_requests(); 448 } 449 450 /* 451 * smbstat_print_counters 452 * 453 * Displays the SMB server counters (session, users...). 454 */ 455 static void 456 smbstat_print_counters(void) 457 { 458 if (!smbstat_opt_c) 459 return; 460 461 if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_t || 462 (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) { 463 (void) printf(SMBSRV_COUNTERS_BANNER); 464 smbstat_rows = 1; 465 } 466 467 (void) printf(SMBSRV_COUNTERS_FORMAT, 468 smbstat_srv_info.si_nbt_sess, 469 smbstat_srv_info.si_tcp_sess, 470 smbstat_srv_info.si_users, 471 smbstat_srv_info.si_trees, 472 smbstat_srv_info.si_files, 473 smbstat_srv_info.si_pipes); 474 475 ++smbstat_rows; 476 } 477 /* 478 * smbstat_print_throughput 479 * 480 * Formats the SMB server throughput output. 481 */ 482 static void 483 smbstat_print_throughput(void) 484 { 485 if (!smbstat_opt_t) 486 return; 487 488 if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_c || 489 (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) { 490 (void) printf(SMBSRV_THROUGHPUT_BANNER); 491 smbstat_rows = 1; 492 } 493 (void) printf(SMBSRV_THROUGHPUT_FORMAT, 494 smbstat_zero(smbstat_srv_info.si_rbs), 495 smbstat_zero(smbstat_srv_info.si_tbs), 496 smbstat_zero(smbstat_srv_info.si_rqs), 497 smbstat_zero(smbstat_srv_info.si_rds), 498 smbstat_zero(smbstat_srv_info.si_wrs)); 499 500 ++smbstat_rows; 501 } 502 503 /* 504 * smbstat_print_utilization 505 */ 506 static void 507 smbstat_print_utilization(void) 508 { 509 char *sat; 510 if (!smbstat_opt_u) 511 return; 512 513 if (smbstat_opt_t || smbstat_opt_r || smbstat_opt_c || 514 (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) { 515 (void) printf(SMBSRV_UTILIZATION_BANNER); 516 smbstat_rows = 1; 517 } 518 519 if (smbstat_srv_info.si_sat) 520 sat = "yes"; 521 else 522 sat = "no "; 523 524 (void) printf(SMBSRV_UTILIZATION_FORMAT, 525 smbstat_srv_info.si_avw, 526 smbstat_srv_info.si_avr, 527 smbstat_srv_info.si_wserv, 528 smbstat_srv_info.si_rserv, 529 smbstat_zero(smbstat_srv_info.si_wpct), 530 smbstat_zero(smbstat_srv_info.si_rpct), 531 smbstat_zero(smbstat_srv_info.si_upct), 532 sat, 533 smbstat_srv_info.si_ticks[CPU_TICKS_USER], 534 smbstat_srv_info.si_ticks[CPU_TICKS_KERNEL], 535 smbstat_srv_info.si_ticks[CPU_TICKS_IDLE]); 536 537 ++smbstat_rows; 538 } 539 540 /* 541 * smbstat_print_requests 542 */ 543 static void 544 smbstat_print_requests(void) 545 { 546 smbstat_req_info_t *prq; 547 int i; 548 549 if (!smbstat_opt_r) 550 return; 551 552 prq = smbstat_srv_info.si_reqs; 553 554 (void) printf(SMBSRV_REQUESTS_BANNER, " "); 555 556 for (i = 0; i < SMB_COM_NUM; i++) { 557 if (!smbstat_opt_a && 558 strncmp(prq[i].ri_name, "Invalid", sizeof ("Invalid")) == 0) 559 continue; 560 561 if (!smbstat_opt_z || (prq[i].ri_pct != 0)) { 562 (void) printf(SMBSRV_REQUESTS_FORMAT, 563 prq[i].ri_name, 564 prq[i].ri_opcode, 565 smbstat_zero(prq[i].ri_pct), 566 smbstat_zero(prq[i].ri_rbs), 567 smbstat_zero(prq[i].ri_tbs), 568 smbstat_zero(prq[i].ri_rqs), 569 prq[i].ri_mean, 570 prq[i].ri_stddev); 571 } 572 } 573 } 574 575 /* 576 * smbstat_cpu_init 577 */ 578 static void 579 smbstat_cpu_init(void) 580 { 581 size_t size; 582 int i; 583 584 smbstat_nrcpus = sysconf(_SC_CPUID_MAX) + 1; 585 size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t); 586 587 for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++) 588 smbstat_cpu_snapshots[i] = smbstat_zalloc(size); 589 } 590 591 /* 592 * smbstat_cpu_fini 593 */ 594 static void 595 smbstat_cpu_fini(void) 596 { 597 size_t size; 598 int i; 599 600 size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t); 601 602 for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++) 603 smbstat_free(smbstat_cpu_snapshots[i], size); 604 } 605 606 /* 607 * smbstat_cpu_current_snapshot 608 */ 609 static smbstat_cpu_snapshot_t * 610 smbstat_cpu_current_snapshot(void) 611 { 612 return (smbstat_cpu_snapshots[smbstat_snapshot_idx]); 613 } 614 615 /* 616 * smbstat_cpu_previous_snapshot 617 */ 618 static smbstat_cpu_snapshot_t * 619 smbstat_cpu_previous_snapshot(void) 620 { 621 int idx; 622 623 idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK; 624 return (smbstat_cpu_snapshots[idx]); 625 } 626 627 /* 628 * smbstat_cpu_snapshot 629 */ 630 static void 631 smbstat_cpu_snapshot(void) 632 { 633 kstat_t *ksp; 634 kstat_named_t *ksn; 635 smbstat_cpu_snapshot_t *curr; 636 long i; 637 int j; 638 639 curr = smbstat_cpu_current_snapshot(); 640 641 for (i = 0; i < smbstat_nrcpus; i++, curr++) { 642 curr->cs_id = SMBSTAT_ID_NO_CPU; 643 curr->cs_state = p_online(i, P_STATUS); 644 /* If no valid CPU is present, move on to the next one */ 645 if (curr->cs_state == -1) 646 continue; 647 648 curr->cs_id = i; 649 650 ksp = kstat_lookup(smbstat_ksc, "cpu", i, "sys"); 651 if (ksp == NULL) 652 smbstat_fail(1, 653 gettext("kstat_lookup('cpu sys %d') failed"), i); 654 655 if (kstat_read(smbstat_ksc, ksp, NULL) == -1) 656 smbstat_fail(1, 657 gettext("kstat_read('cpu sys %d') failed"), i); 658 659 for (j = 0; j < CPU_TICKS_SENTINEL; j++) { 660 ksn = kstat_data_lookup(ksp, smbstat_cpu_states[j]); 661 if (ksn == NULL) 662 smbstat_fail(1, 663 gettext("kstat_data_lookup('%s') failed"), 664 smbstat_cpu_states[j]); 665 curr->cs_ticks[j] = ksn->value.ui64; 666 } 667 } 668 } 669 670 /* 671 * smbstat_cpu_process 672 */ 673 static void 674 smbstat_cpu_process(void) 675 { 676 smbstat_cpu_snapshot_t *curr, *prev; 677 double total_ticks; 678 double agg_ticks[CPU_TICKS_SENTINEL]; 679 int i, j; 680 681 curr = smbstat_cpu_current_snapshot(); 682 prev = smbstat_cpu_previous_snapshot(); 683 bzero(agg_ticks, sizeof (agg_ticks)); 684 total_ticks = 0; 685 686 for (i = 0; i < smbstat_nrcpus; i++, curr++, prev++) { 687 for (j = 0; j < CPU_TICKS_SENTINEL; j++) { 688 agg_ticks[j] += smbstat_sub_64(curr->cs_ticks[j], 689 prev->cs_ticks[j]); 690 total_ticks += smbstat_sub_64(curr->cs_ticks[j], 691 prev->cs_ticks[j]); 692 } 693 } 694 695 for (j = 0; j < CPU_TICKS_SENTINEL; j++) 696 smbstat_srv_info.si_ticks[j] = 697 (agg_ticks[j] * 100.0) / total_ticks; 698 } 699 700 /* 701 * smbstat_wrk_init 702 */ 703 static void 704 smbstat_wrk_init(void) 705 { 706 smbstat_wrk_ksp = 707 kstat_lookup(smbstat_ksc, "unix", -1, SMBSRV_KSTAT_WORKERS); 708 if (smbstat_wrk_ksp == NULL) 709 smbstat_fail(1, 710 gettext("cannot retrieve smbsrv workers kstat\n")); 711 } 712 713 static void 714 smbstat_wrk_fini(void) 715 { 716 smbstat_wrk_ksp = NULL; 717 } 718 719 /* 720 * smbstat_wrk_snapshot 721 */ 722 static void 723 smbstat_wrk_snapshot(void) 724 { 725 smbstat_wrk_snapshot_t *curr; 726 kstat_named_t *kn; 727 728 curr = smbstat_wrk_current_snapshot(); 729 730 if (kstat_read(smbstat_ksc, smbstat_wrk_ksp, NULL) == -1) 731 smbstat_fail(1, gettext("kstat_read('%s') failed"), 732 smbstat_wrk_ksp->ks_name); 733 734 kn = kstat_data_lookup(smbstat_wrk_ksp, "maxthreads"); 735 if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64)) 736 smbstat_fail(1, gettext("kstat_read('%s') failed"), 737 "maxthreads"); 738 curr->ws_maxthreads = kn->value.ui64; 739 740 kn = kstat_data_lookup(smbstat_wrk_ksp, "bnalloc"); 741 if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64)) 742 smbstat_fail(1, gettext("kstat_read('%s') failed"), 743 "bnalloc"); 744 curr->ws_bnalloc = kn->value.ui64; 745 } 746 747 /* 748 * smbstat_wrk_process 749 */ 750 static void 751 smbstat_wrk_process(void) 752 { 753 smbstat_wrk_snapshot_t *curr; 754 755 curr = smbstat_wrk_current_snapshot(); 756 757 if (curr->ws_maxthreads >= curr->ws_bnalloc) 758 smbstat_srv_info.si_sat = B_TRUE; 759 else 760 smbstat_srv_info.si_sat = B_FALSE; 761 } 762 763 /* 764 * smbstat_wrk_current_snapshot 765 */ 766 static smbstat_wrk_snapshot_t * 767 smbstat_wrk_current_snapshot(void) 768 { 769 return (&smbstat_wrk_snapshots[smbstat_snapshot_idx]); 770 } 771 772 /* 773 * smbstat_srv_init 774 */ 775 static void 776 smbstat_srv_init(void) 777 { 778 smbstat_srv_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE, 779 getzoneid(), SMBSRV_KSTAT_STATISTICS); 780 if (smbstat_srv_ksp == NULL) 781 smbstat_fail(1, gettext("cannot retrieve smbsrv kstat\n")); 782 } 783 784 /* 785 * smbstat_srv_fini 786 */ 787 static void 788 smbstat_srv_fini(void) 789 { 790 smbstat_srv_ksp = NULL; 791 } 792 793 /* 794 * smbstat_srv_snapshot 795 * 796 * Take a snapshot of the smbsrv module statistics. 797 */ 798 static void 799 smbstat_srv_snapshot(void) 800 { 801 smbstat_srv_snapshot_t *curr; 802 803 curr = smbstat_srv_current_snapshot(); 804 805 if ((kstat_read(smbstat_ksc, smbstat_srv_ksp, NULL) == -1) || 806 (smbstat_srv_ksp->ks_data_size != sizeof (curr->ss_data))) 807 smbstat_fail(1, gettext("kstat_read('%s') failed"), 808 smbstat_srv_ksp->ks_name); 809 810 curr->ss_snaptime = smbstat_srv_ksp->ks_snaptime; 811 bcopy(smbstat_srv_ksp->ks_data, &curr->ss_data, sizeof (curr->ss_data)); 812 } 813 814 /* 815 * smbstat_srv_process 816 * 817 * Processes the snapshot data. 818 */ 819 static void 820 smbstat_srv_process(void) 821 { 822 smbstat_srv_snapshot_t *curr, *prev; 823 824 curr = smbstat_srv_current_snapshot(); 825 prev = smbstat_srv_previous_snapshot(); 826 827 if (prev->ss_snaptime == 0) 828 smbstat_srv_info.si_hretime = 829 smbstat_hrtime_delta(curr->ss_data.ks_start_time, 830 curr->ss_snaptime); 831 else 832 smbstat_srv_info.si_hretime = 833 smbstat_hrtime_delta(prev->ss_snaptime, curr->ss_snaptime); 834 835 smbstat_srv_info.si_etime = smbstat_srv_info.si_hretime / NANOSEC; 836 smbstat_srv_info.si_total_nreqs = 837 smbstat_sub_64(curr->ss_data.ks_nreq, prev->ss_data.ks_nreq); 838 839 if (smbstat_opt_c) 840 smbstat_srv_process_counters(curr); 841 if (smbstat_opt_t) 842 smbstat_srv_process_throughput(curr, prev); 843 if (smbstat_opt_u) 844 smbstat_srv_process_utilization(curr, prev); 845 if (smbstat_opt_r) 846 smbstat_srv_process_requests(curr, prev); 847 } 848 849 /* 850 * smbstat_srv_process_counters 851 */ 852 static void 853 smbstat_srv_process_counters(smbstat_srv_snapshot_t *curr) 854 { 855 smbstat_srv_info.si_nbt_sess = curr->ss_data.ks_nbt_sess; 856 smbstat_srv_info.si_tcp_sess = curr->ss_data.ks_tcp_sess; 857 smbstat_srv_info.si_users = curr->ss_data.ks_users; 858 smbstat_srv_info.si_trees = curr->ss_data.ks_trees; 859 smbstat_srv_info.si_files = curr->ss_data.ks_files; 860 smbstat_srv_info.si_pipes = curr->ss_data.ks_pipes; 861 } 862 863 /* 864 * smbstat_srv_process_throughput 865 * 866 * Processes the data relative to the throughput of the smbsrv module and 867 * stores the results in the structure smbstat_srv_info. 868 */ 869 static void 870 smbstat_srv_process_throughput( 871 smbstat_srv_snapshot_t *curr, 872 smbstat_srv_snapshot_t *prev) 873 { 874 smbstat_srv_info.si_tbs = 875 smbstat_sub_64(curr->ss_data.ks_txb, prev->ss_data.ks_txb); 876 smbstat_srv_info.si_tbs /= smbstat_srv_info.si_etime; 877 smbstat_srv_info.si_rbs = 878 smbstat_sub_64(curr->ss_data.ks_rxb, prev->ss_data.ks_rxb); 879 smbstat_srv_info.si_rbs /= smbstat_srv_info.si_etime; 880 smbstat_srv_info.si_rqs = smbstat_srv_info.si_total_nreqs; 881 smbstat_srv_info.si_rqs /= smbstat_srv_info.si_etime; 882 883 smbstat_srv_info.si_rds = smbstat_sub_64( 884 curr->ss_data.ks_reqs[SMB_COM_READ].kr_nreq, 885 prev->ss_data.ks_reqs[SMB_COM_READ].kr_nreq); 886 smbstat_srv_info.si_rds += smbstat_sub_64( 887 curr->ss_data.ks_reqs[SMB_COM_LOCK_AND_READ].kr_nreq, 888 prev->ss_data.ks_reqs[SMB_COM_LOCK_AND_READ].kr_nreq); 889 smbstat_srv_info.si_rds += smbstat_sub_64( 890 curr->ss_data.ks_reqs[SMB_COM_READ_RAW].kr_nreq, 891 prev->ss_data.ks_reqs[SMB_COM_READ_RAW].kr_nreq); 892 smbstat_srv_info.si_rds += smbstat_sub_64( 893 curr->ss_data.ks_reqs[SMB_COM_READ_ANDX].kr_nreq, 894 prev->ss_data.ks_reqs[SMB_COM_READ_ANDX].kr_nreq); 895 smbstat_srv_info.si_rds /= smbstat_srv_info.si_etime; 896 897 smbstat_srv_info.si_wrs = smbstat_sub_64( 898 curr->ss_data.ks_reqs[SMB_COM_WRITE].kr_nreq, 899 prev->ss_data.ks_reqs[SMB_COM_WRITE].kr_nreq); 900 smbstat_srv_info.si_wrs += smbstat_sub_64( 901 curr->ss_data.ks_reqs[SMB_COM_WRITE_AND_UNLOCK].kr_nreq, 902 prev->ss_data.ks_reqs[SMB_COM_WRITE_AND_UNLOCK].kr_nreq); 903 smbstat_srv_info.si_wrs += smbstat_sub_64( 904 curr->ss_data.ks_reqs[SMB_COM_WRITE_RAW].kr_nreq, 905 prev->ss_data.ks_reqs[SMB_COM_WRITE_RAW].kr_nreq); 906 smbstat_srv_info.si_wrs += smbstat_sub_64( 907 curr->ss_data.ks_reqs[SMB_COM_WRITE_AND_CLOSE].kr_nreq, 908 prev->ss_data.ks_reqs[SMB_COM_WRITE_AND_CLOSE].kr_nreq); 909 smbstat_srv_info.si_wrs += smbstat_sub_64( 910 curr->ss_data.ks_reqs[SMB_COM_WRITE_ANDX].kr_nreq, 911 prev->ss_data.ks_reqs[SMB_COM_WRITE_ANDX].kr_nreq); 912 smbstat_srv_info.si_wrs /= smbstat_srv_info.si_etime; 913 } 914 915 /* 916 * smbstat_srv_process_utilization 917 * 918 * Processes the data relative to the utilization of the smbsrv module and 919 * stores the results in the structure smbstat_srv_info. 920 */ 921 static void 922 smbstat_srv_process_utilization( 923 smbstat_srv_snapshot_t *curr, 924 smbstat_srv_snapshot_t *prev) 925 { 926 double tw_delta, tr_delta; 927 double w_delta, r_delta; 928 double tps, rqs; 929 930 w_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wlentime, 931 curr->ss_data.ks_utilization.ku_wlentime); 932 r_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rlentime, 933 curr->ss_data.ks_utilization.ku_rlentime); 934 tw_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wtime, 935 curr->ss_data.ks_utilization.ku_wtime); 936 tr_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rtime, 937 curr->ss_data.ks_utilization.ku_rtime); 938 rqs = smbstat_srv_info.si_total_nreqs / smbstat_srv_info.si_etime; 939 940 /* Average number of requests waiting */ 941 if (w_delta != 0) 942 smbstat_srv_info.si_avw = w_delta / smbstat_srv_info.si_hretime; 943 else 944 smbstat_srv_info.si_avw = 0.0; 945 946 /* Average number of request running */ 947 if (r_delta != 0) 948 smbstat_srv_info.si_avr = r_delta / smbstat_srv_info.si_hretime; 949 else 950 smbstat_srv_info.si_avr = 0.0; 951 952 /* Utilization */ 953 smbstat_srv_info.si_upct = 954 (smbstat_srv_info.si_avr / curr->ss_data.ks_maxreqs) * 100; 955 956 /* Average wait service time in milliseconds */ 957 smbstat_srv_info.si_rserv = 0.0; 958 smbstat_srv_info.si_wserv = 0.0; 959 if (rqs > 0.0 && 960 (smbstat_srv_info.si_avw != 0.0 || 961 smbstat_srv_info.si_avr != 0.0)) { 962 tps = 1 / rqs; 963 if (smbstat_srv_info.si_avw != 0.0) 964 smbstat_srv_info.si_wserv = 965 smbstat_srv_info.si_avw * tps; 966 if (smbstat_srv_info.si_avr != 0.0) 967 smbstat_srv_info.si_rserv = 968 smbstat_srv_info.si_avr * tps; 969 } 970 971 /* % of time there is a transaction waiting for service */ 972 if (tw_delta != 0) { 973 smbstat_srv_info.si_wpct = tw_delta; 974 smbstat_srv_info.si_wpct /= smbstat_srv_info.si_hretime; 975 smbstat_srv_info.si_wpct *= 100.0; 976 } else { 977 smbstat_srv_info.si_wpct = 0.0; 978 } 979 980 /* % of time there is a transaction running */ 981 if (tr_delta != 0) { 982 smbstat_srv_info.si_rpct = tr_delta; 983 smbstat_srv_info.si_rpct /= smbstat_srv_info.si_hretime; 984 smbstat_srv_info.si_rpct *= 100.0; 985 } else { 986 smbstat_srv_info.si_rpct = 0.0; 987 } 988 } 989 990 /* 991 * smbstat_srv_process_requests 992 * 993 * Processes the data relative to the SMB requests and stores the results in 994 * the structure smbstat_srv_info. 995 */ 996 static void 997 smbstat_srv_process_requests( 998 smbstat_srv_snapshot_t *curr, 999 smbstat_srv_snapshot_t *prev) 1000 { 1001 smbstat_req_info_t *info; 1002 double nrqs; 1003 int i, idx; 1004 1005 info = smbstat_srv_info.si_reqs; 1006 1007 for (i = 0; i < SMB_COM_NUM; i++) { 1008 idx = info[i].ri_opcode; 1009 1010 nrqs = smbstat_sub_64(curr->ss_data.ks_reqs[idx].kr_nreq, 1011 prev->ss_data.ks_reqs[idx].kr_nreq); 1012 1013 info[i].ri_rqs = nrqs / smbstat_srv_info.si_etime; 1014 1015 info[i].ri_rbs = smbstat_sub_64( 1016 curr->ss_data.ks_reqs[idx].kr_rxb, 1017 prev->ss_data.ks_reqs[idx].kr_rxb) / 1018 smbstat_srv_info.si_etime; 1019 1020 info[i].ri_tbs = smbstat_sub_64( 1021 curr->ss_data.ks_reqs[idx].kr_txb, 1022 prev->ss_data.ks_reqs[idx].kr_txb) / 1023 smbstat_srv_info.si_etime; 1024 1025 info[i].ri_pct = nrqs * 100; 1026 if (smbstat_srv_info.si_total_nreqs > 0) 1027 info[i].ri_pct /= smbstat_srv_info.si_total_nreqs; 1028 1029 if (prev->ss_snaptime == 0) { 1030 /* First time. Take the aggregate */ 1031 info[i].ri_stddev = 1032 curr->ss_data.ks_reqs[idx].kr_a_stddev; 1033 info[i].ri_mean = curr->ss_data.ks_reqs[idx].kr_a_mean; 1034 } else { 1035 /* Take the differential */ 1036 info[i].ri_stddev = 1037 curr->ss_data.ks_reqs[idx].kr_d_stddev; 1038 info[i].ri_mean = curr->ss_data.ks_reqs[idx].kr_d_mean; 1039 } 1040 if (nrqs > 0) { 1041 info[i].ri_stddev /= nrqs; 1042 info[i].ri_stddev = sqrt(info[i].ri_stddev); 1043 } else { 1044 info[i].ri_stddev = 0; 1045 } 1046 info[i].ri_stddev /= NANOSEC; 1047 info[i].ri_mean /= NANOSEC; 1048 } 1049 } 1050 1051 /* 1052 * smbstat_srv_current_snapshot 1053 * 1054 * Returns the current snapshot. 1055 */ 1056 static smbstat_srv_snapshot_t * 1057 smbstat_srv_current_snapshot(void) 1058 { 1059 return (&smbstat_srv_snapshots[smbstat_snapshot_idx]); 1060 } 1061 1062 /* 1063 * smbstat_srv_previous_snapshot 1064 * 1065 * Returns the previous snapshot. 1066 */ 1067 static smbstat_srv_snapshot_t * 1068 smbstat_srv_previous_snapshot(void) 1069 { 1070 int idx; 1071 1072 idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK; 1073 return (&smbstat_srv_snapshots[idx]); 1074 } 1075 1076 /* 1077 * smbstat_usage 1078 * 1079 * Prints out a help message. 1080 */ 1081 static void 1082 smbstat_usage(FILE *fd, int exit_code) 1083 { 1084 (void) fprintf(fd, gettext(SMBSTAT_HELP)); 1085 exit(exit_code); 1086 } 1087 1088 /* 1089 * smbstat_fail 1090 * 1091 * Prints out to stderr an error message and exits the process. 1092 */ 1093 static void 1094 smbstat_fail(int do_perror, char *message, ...) 1095 { 1096 va_list args; 1097 1098 va_start(args, message); 1099 (void) fprintf(stderr, gettext("smbstat: ")); 1100 /* LINTED E_SEC_PRINTF_VAR_FMT */ 1101 (void) vfprintf(stderr, message, args); 1102 va_end(args); 1103 if (do_perror) 1104 (void) fprintf(stderr, ": %s", strerror(errno)); 1105 (void) fprintf(stderr, "\n"); 1106 exit(1); 1107 } 1108 1109 /* 1110 * smbstat_sub_64 1111 * 1112 * Substract 2 uint64_t and returns a double. 1113 */ 1114 static double 1115 smbstat_sub_64(uint64_t a, uint64_t b) 1116 { 1117 return ((double)(a - b)); 1118 } 1119 1120 /* 1121 * smbstat_zero 1122 * 1123 * Returns zero if the value passed in is less than 1. 1124 */ 1125 static double 1126 smbstat_zero(double value) 1127 { 1128 if (value < 1) 1129 value = 0; 1130 return (value); 1131 } 1132 1133 /* 1134 * smbstat_strtoi 1135 * 1136 * Converts a string representing an integer value into its binary value. 1137 * If the conversion fails this routine exits the process. 1138 */ 1139 static uint_t 1140 smbstat_strtoi(char const *val, char *errmsg) 1141 { 1142 char *end; 1143 long tmp; 1144 1145 errno = 0; 1146 tmp = strtol(val, &end, 10); 1147 if (*end != '\0' || errno) 1148 smbstat_fail(1, "%s %s", errmsg, val); 1149 return ((uint_t)tmp); 1150 } 1151 1152 /* 1153 * smbstat_termio_init 1154 * 1155 * Determines the size of the terminal associated with the process. 1156 */ 1157 static void 1158 smbstat_termio_init(void) 1159 { 1160 char *envp; 1161 1162 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &smbstat_ws) != -1) { 1163 if (smbstat_ws.ws_row == 0) { 1164 envp = getenv("LINES"); 1165 if (envp != NULL) 1166 smbstat_ws.ws_row = atoi(envp); 1167 } 1168 1169 if (smbstat_ws.ws_col == 0) { 1170 envp = getenv("COLUMNS"); 1171 if (envp != NULL) 1172 smbstat_ws.ws_row = atoi(envp); 1173 } 1174 } 1175 if (smbstat_ws.ws_col == 0) 1176 smbstat_ws.ws_col = 80; 1177 if (smbstat_ws.ws_row == 0) 1178 smbstat_ws.ws_row = 25; 1179 } 1180 1181 /* 1182 * smbstat_snapshot_idx_inc 1183 * 1184 * Increments the snapshot index. 1185 */ 1186 static void 1187 smbstat_snapshot_inc_idx(void) 1188 { 1189 smbstat_snapshot_idx++; 1190 smbstat_snapshot_idx &= SMBSTAT_SNAPSHOT_MASK; 1191 } 1192 1193 /* 1194 * smbstat_req_cmp_name 1195 * 1196 * Call back function passed to qsort() when the list of requests must be sorted 1197 * by name. 1198 */ 1199 static int 1200 smbstat_req_cmp_name(const void *obj1, const void *obj2) 1201 { 1202 return (strncasecmp( 1203 ((smbstat_req_info_t *)obj1)->ri_name, 1204 ((smbstat_req_info_t *)obj2)->ri_name, 1205 sizeof (((smbstat_req_info_t *)obj2)->ri_name))); 1206 } 1207 1208 /* 1209 * smbstat_req_order 1210 * 1211 * Snapshots the smbsrv module statistics once to get the name of the requests. 1212 * The request list is smbstat_srv_info is then sorted by name or by code 1213 * depending on the boolean smbstat_opt_a. 1214 * The function should be called once during initialization. 1215 */ 1216 static void 1217 smbstat_req_order(void) 1218 { 1219 smbstat_req_info_t *info; 1220 smb_kstat_req_t *reqs; 1221 int i; 1222 1223 smbstat_srv_snapshot(); 1224 reqs = smbstat_srv_current_snapshot()->ss_data.ks_reqs; 1225 info = smbstat_srv_info.si_reqs; 1226 1227 for (i = 0; i < SMB_COM_NUM; i++) { 1228 (void) strlcpy(info[i].ri_name, reqs[i].kr_name, 1229 sizeof (reqs[i].kr_name)); 1230 info[i].ri_opcode = i; 1231 } 1232 if (smbstat_opt_n) 1233 qsort(info, SMB_COM_NUM, sizeof (smbstat_req_info_t), 1234 smbstat_req_cmp_name); 1235 } 1236 1237 /* 1238 * Return the number of ticks delta between two hrtime_t 1239 * values. Attempt to cater for various kinds of overflow 1240 * in hrtime_t - no matter how improbable. 1241 */ 1242 static double 1243 smbstat_hrtime_delta(hrtime_t old, hrtime_t new) 1244 { 1245 uint64_t del; 1246 1247 if ((new >= old) && (old >= 0L)) 1248 return ((double)(new - old)); 1249 /* 1250 * We've overflowed the positive portion of an hrtime_t. 1251 */ 1252 if (new < 0L) { 1253 /* 1254 * The new value is negative. Handle the case where the old 1255 * value is positive or negative. 1256 */ 1257 uint64_t n1; 1258 uint64_t o1; 1259 1260 n1 = -new; 1261 if (old > 0L) 1262 return ((double)(n1 - old)); 1263 1264 o1 = -old; 1265 del = n1 - o1; 1266 return ((double)del); 1267 } 1268 1269 /* 1270 * Either we've just gone from being negative to positive *or* the last 1271 * entry was positive and the new entry is also positive but *less* than 1272 * the old entry. This implies we waited quite a few days on a very fast 1273 * system between displays. 1274 */ 1275 if (old < 0L) { 1276 uint64_t o2; 1277 o2 = -old; 1278 del = UINT64_MAX - o2; 1279 } else { 1280 del = UINT64_MAX - old; 1281 } 1282 del += new; 1283 return ((double)del); 1284 } 1285 1286 static void * 1287 smbstat_zalloc(size_t size) 1288 { 1289 void *ptr; 1290 1291 ptr = umem_zalloc(size, UMEM_DEFAULT); 1292 if (ptr == NULL) 1293 smbstat_fail(1, gettext("out of memory")); 1294 return (ptr); 1295 } 1296 1297 static void 1298 smbstat_free(void *ptr, size_t size) 1299 { 1300 umem_free(ptr, size); 1301 } 1302