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