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  * Copyright 2016 Nexenta Systems, Inc.  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 #include <smbsrv/smb.h>
 107 #include <smbsrv/smb2.h>
 108 
 109 #if !defined(TEXT_DOMAIN)
 110 #define TEXT_DOMAIN "SYS_TEST"
 111 #endif /* TEXT_DOMAIN */
 112 
 113 #define SMBSTAT_ID_NO_CPU       -1
 114 #define SMBSTAT_SNAPSHOT_COUNT  2               /* Must be a power of 2 */
 115 #define SMBSTAT_SNAPSHOT_MASK   (SMBSTAT_SNAPSHOT_COUNT - 1)
 116 
 117 #define SMBSTAT_HELP    \
 118         "Usage: smbstat [-acnrtuz] [interval]\n" \
 119         "    -c: display counters\n" \
 120         "    -t: display throughput\n" \
 121         "    -u: display utilization\n" \
 122         "    -r: display requests\n" \
 123         "        -a: all the requests (supported and unsupported)\n" \
 124         "        -z: skip the requests not received\n" \
 125         "        -n: display in alphabetic order\n" \
 126         "    interval: refresh cycle in seconds\n"
 127 
 128 #define SMBSRV_COUNTERS_BANNER  "\n  nbt   tcp users trees files pipes\n"
 129 #define SMBSRV_COUNTERS_FORMAT  "%5d %5d %5d %5d %5d %5d\n"
 130 
 131 #define SMBSRV_THROUGHPUT_BANNER        \
 132         "\nrbytes/s   tbytes/s    reqs/s     reads/s   writes/s\n"
 133 #define SMBSRV_THROUGHPUT_FORMAT        \
 134         "%1.3e  %1.3e  %1.3e  %1.3e  %1.3e\n"
 135 
 136 #define SMBSRV_UTILIZATION_BANNER       \
 137         "\n  wcnt       rcnt       wtime      rtime" \
 138         "     w%%   r%%   u%%  sat usr%% sys%%  idle%%\n"
 139 #define SMBSRV_UTILIZATION_FORMAT       \
 140         "%1.3e  %1.3e  %1.3e  %1.3e  %3.0f  %3.0f  %3.0f  %s " \
 141         "%3.0f  %3.0f    %3.0f\n"
 142 
 143 #define SMBSRV_REQUESTS_BANNER  \
 144         "\n%30s code   %%   rbytes/s   tbytes/s     req/s     rt-mean"  \
 145         "   rt-stddev\n"
 146 #define SMBSRV_REQUESTS_FORMAT  \
 147         "%30s  %02X   %3.0f  %1.3e  %1.3e  %1.3e  %1.3e  %1.3e\n"
 148 
 149 #define SMBSRV_CLNT_SHARE_BANNER        \
 150         "%30s  rbytes/s   tbytes/s     req/s     rt-mean   rt-stddev\n"
 151 #define SMBSRV_CLNT_SHARE_FORMAT        \
 152         "%30s  %1.3e  %1.3e  %1.3e  %1.3e  %1.3e\n"
 153 
 154 
 155 typedef enum {
 156         CPU_TICKS_IDLE = 0,
 157         CPU_TICKS_USER,
 158         CPU_TICKS_KERNEL,
 159         CPU_TICKS_SENTINEL
 160 } cpu_state_idx_t;
 161 
 162 typedef struct smbstat_cpu_snapshot {
 163         processorid_t   cs_id;
 164         int             cs_state;
 165         uint64_t        cs_ticks[CPU_TICKS_SENTINEL];
 166 } smbstat_cpu_snapshot_t;
 167 
 168 typedef struct smbstat_srv_snapshot {
 169         hrtime_t        ss_snaptime;
 170         smbsrv_kstats_t ss_data;
 171 } smbstat_srv_snapshot_t;
 172 
 173 typedef struct smbstat_wrk_snapshot {
 174         uint64_t        ws_maxthreads;
 175         uint64_t        ws_bnalloc;
 176 } smbstat_wrk_snapshot_t;
 177 
 178 /* Per-Client or Per-Share statistics. */
 179 typedef struct smbstat_clsh_snapshot {
 180         hrtime_t        cs_snaptime;
 181         smbsrv_clsh_kstats_t    cs_data;
 182 } smbstat_clsh_snapshot_t;
 183 
 184 typedef struct smbstat_req_info {
 185         char            ri_name[KSTAT_STRLEN];
 186         int             ri_opcode;
 187         double          ri_pct;
 188         double          ri_tbs;
 189         double          ri_rbs;
 190         double          ri_rqs;
 191         double          ri_stddev;
 192         double          ri_mean;
 193 } smbstat_req_info_t;
 194 
 195 typedef struct smbstat_srv_info {
 196         double          si_hretime;
 197         double          si_etime;
 198         double          si_total_nreqs;
 199         /*
 200          * Counters
 201          */
 202         uint32_t        si_nbt_sess;    /* NBT sessions */
 203         uint32_t        si_tcp_sess;    /* TCP sessions */
 204         uint32_t        si_users;       /* Users logged in */
 205         uint32_t        si_trees;       /* Trees connected */
 206         uint32_t        si_files;       /* Open files */
 207         uint32_t        si_pipes;       /* Open pipes */
 208         /*
 209          * Throughput of the server
 210          */
 211         double          si_tbs;         /* Bytes transmitted / second */
 212         double          si_rbs;         /* Bytes received / second */
 213         double          si_rqs;         /* Requests treated / second */
 214         double          si_rds;         /* Reads treated / second */
 215         double          si_wrs;         /* Writes treated / second */
 216         /*
 217          * Utilization of the server
 218          */
 219         double          si_wpct;        /* */
 220         double          si_rpct;        /* */
 221         double          si_upct;        /* Utilization in % */
 222         double          si_avw;         /* Average number of requests waiting */
 223         double          si_avr;         /* Average number of requests running */
 224         double          si_wserv;       /* Average waiting time */
 225         double          si_rserv;       /* Average running time */
 226         boolean_t       si_sat;
 227         double          si_ticks[CPU_TICKS_SENTINEL];
 228         /*
 229          * Latency & Throughput per request
 230          */
 231         smbstat_req_info_t      si_reqs1[SMBSRV_KS_NREQS1];
 232         smbstat_req_info_t      si_reqs2[SMBSRV_KS_NREQS2];
 233         /*
 234          * Latency & Throughput on specified client or share
 235          */
 236         smbstat_req_info_t      si_clsh[SMBSRV_CLSH__NREQ];
 237 } smbstat_srv_info_t;
 238 
 239 static void smbstat_init(void);
 240 static void smbstat_fini(void);
 241 static void smbstat_kstat_snapshot(void);
 242 static void smbstat_kstat_process(void);
 243 static void smbstat_kstat_print(void);
 244 
 245 static void smbstat_print_counters(void);
 246 static void smbstat_print_throughput(void);
 247 static void smbstat_print_utilization(void);
 248 static void smbstat_print_requests(void);
 249 static void smbstat_print_client_or_share(void);
 250 
 251 static void smbstat_cpu_init(void);
 252 static void smbstat_cpu_fini(void);
 253 static smbstat_cpu_snapshot_t *smbstat_cpu_current_snapshot(void);
 254 static smbstat_cpu_snapshot_t *smbstat_cpu_previous_snapshot(void);
 255 static void smbstat_cpu_snapshot(void);
 256 static void smbstat_cpu_process(void);
 257 
 258 static void smbstat_wrk_init(void);
 259 static void smbstat_wrk_fini(void);
 260 static void smbstat_wrk_snapshot(void);
 261 static void smbstat_wrk_process(void);
 262 static smbstat_wrk_snapshot_t *smbstat_wrk_current_snapshot(void);
 263 
 264 static void smbstat_clsh_init(void);
 265 static void smbstat_clsh_fini(void);
 266 static void smbstat_clsh_snapshot(void);
 267 static void smbstat_clsh_process(void);
 268 static smbstat_clsh_snapshot_t *smbstat_clsh_current_snapshot(void);
 269 static smbstat_clsh_snapshot_t *smbstat_clsh_previous_snapshot(void);
 270 
 271 static void smbstat_srv_init(void);
 272 static void smbstat_srv_fini(void);
 273 static void smbstat_srv_snapshot(void);
 274 static void smbstat_srv_process(void);
 275 static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *);
 276 static void smbstat_srv_process_throughput(smbstat_srv_snapshot_t *,
 277     smbstat_srv_snapshot_t *);
 278 static void smbstat_srv_process_utilization(smbstat_srv_snapshot_t *,
 279     smbstat_srv_snapshot_t *);
 280 static void smbstat_srv_process_requests(smbstat_srv_snapshot_t *,
 281     smbstat_srv_snapshot_t *);
 282 static void smbstat_srv_process_one_req(smbstat_req_info_t *,
 283     smb_kstat_req_t *, smb_kstat_req_t *, boolean_t);
 284 
 285 static smbstat_srv_snapshot_t *smbstat_srv_current_snapshot(void);
 286 static smbstat_srv_snapshot_t *smbstat_srv_previous_snapshot(void);
 287 
 288 static void *smbstat_zalloc(size_t);
 289 static void smbstat_free(void *, size_t);
 290 static void smbstat_fail(int, char *, ...);
 291 static void smbstat_snapshot_inc_idx(void);
 292 static void smbstat_usage(FILE *, int);
 293 static uint_t smbstat_strtoi(char const *, char *);
 294 static double smbstat_hrtime_delta(hrtime_t, hrtime_t);
 295 static double smbstat_sub_64(uint64_t, uint64_t);
 296 static void smbstat_req_order(void);
 297 static double smbstat_zero(double);
 298 static void smbstat_termio_init(void);
 299 
 300 #pragma does_not_return(smbstat_fail, smbstat_usage)
 301 
 302 static char *smbstat_cpu_states[CPU_TICKS_SENTINEL] = {
 303         "cpu_ticks_idle",
 304         "cpu_ticks_user",
 305         "cpu_ticks_kernel"
 306 };
 307 
 308 static boolean_t        smbstat_opt_a = B_FALSE;        /* all */
 309 static boolean_t        smbstat_opt_c = B_FALSE;        /* counters */
 310 static boolean_t        smbstat_opt_n = B_FALSE;        /* by name */
 311 static boolean_t        smbstat_opt_u = B_FALSE;        /* utilization */
 312 static boolean_t        smbstat_opt_t = B_FALSE;        /* throughput */
 313 static boolean_t        smbstat_opt_r = B_FALSE;        /* requests */
 314 static boolean_t        smbstat_opt_z = B_FALSE;        /* non-zero requests */
 315 static boolean_t        smbstat_opt_C = B_FALSE;        /* per-client stats */
 316 static boolean_t        smbstat_opt_S = B_FALSE;        /* per-share stats */
 317 static char             *smbstat_opt_clsh = NULL;       /* -C or -S arg. */
 318 
 319 static uint_t           smbstat_interval = 0;
 320 static long             smbstat_nrcpus = 0;
 321 static kstat_ctl_t      *smbstat_ksc = NULL;
 322 static kstat_t          *smbstat_srv_ksp = NULL;
 323 static kstat_t          *smbstat_wrk_ksp = NULL;
 324 static kstat_t          *smbstat_clsh_ksp = NULL;
 325 static struct winsize   smbstat_ws;
 326 static uint16_t         smbstat_rows = 0;
 327 
 328 static int smbstat_snapshot_idx = 0;
 329 static smbstat_cpu_snapshot_t *smbstat_cpu_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 330 static smbstat_srv_snapshot_t smbstat_srv_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 331 static smbstat_wrk_snapshot_t smbstat_wrk_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 332 static smbstat_clsh_snapshot_t smbstat_clsh_snapshots[SMBSTAT_SNAPSHOT_COUNT];
 333 static smbstat_srv_info_t smbstat_srv_info;
 334 
 335 /*
 336  * main
 337  */
 338 int
 339 main(int argc, char *argv[])
 340 {
 341         int     c;
 342 
 343         (void) setlocale(LC_ALL, "");
 344         (void) textdomain(TEXT_DOMAIN);
 345 
 346         if (is_system_labeled()) {
 347                 (void) fprintf(stderr,
 348                     gettext("%s: Trusted Extensions not supported.\n"),
 349                     argv[0]);
 350                 return (1);
 351         }
 352 
 353         while ((c = getopt(argc, argv, "achnrtuzC:S:")) != EOF) {
 354                 switch (c) {
 355                 case 'a':
 356                         smbstat_opt_a = B_TRUE;
 357                         break;
 358                 case 'n':
 359                         smbstat_opt_n = B_TRUE;
 360                         break;
 361                 case 'u':
 362                         smbstat_opt_u = B_TRUE;
 363                         break;
 364                 case 'c':
 365                         smbstat_opt_c = B_TRUE;
 366                         break;
 367                 case 'r':
 368                         smbstat_opt_r = B_TRUE;
 369                         break;
 370                 case 't':
 371                         smbstat_opt_t = B_TRUE;
 372                         break;
 373                 case 'z':
 374                         smbstat_opt_z = B_TRUE;
 375                         break;
 376                 case 'C': /* per-client stats. */
 377                         smbstat_opt_S = B_FALSE;
 378                         smbstat_opt_C = B_TRUE;
 379                         smbstat_opt_clsh = optarg;
 380                         break;
 381                 case 'S': /* per-share stats. */
 382                         smbstat_opt_C = B_FALSE;
 383                         smbstat_opt_S = B_TRUE;
 384                         smbstat_opt_clsh = optarg;
 385                         break;
 386                 case 'h':
 387                         smbstat_usage(stdout, 0);
 388                 default:
 389                         smbstat_usage(stderr, 1);
 390                 }
 391         }
 392 
 393         if (!smbstat_opt_u &&
 394             !smbstat_opt_c &&
 395             !smbstat_opt_r &&
 396             !smbstat_opt_t &&
 397             !smbstat_opt_C &&
 398             !smbstat_opt_S) {
 399                 /* Default options when none is specified. */
 400                 smbstat_opt_u = B_TRUE;
 401                 smbstat_opt_t = B_TRUE;
 402         }
 403 
 404         if (optind < argc) {
 405                 smbstat_interval =
 406                     smbstat_strtoi(argv[optind], "invalid count");
 407                 optind++;
 408         }
 409 
 410         if ((argc - optind) > 1)
 411                 smbstat_usage(stderr, 1);
 412 
 413         (void) atexit(smbstat_fini);
 414         smbstat_init();
 415         smbstat_req_order();
 416         for (;;) {
 417                 smbstat_kstat_snapshot();
 418                 smbstat_kstat_process();
 419                 smbstat_kstat_print();
 420                 if (smbstat_interval == 0)
 421                         break;
 422                 (void) sleep(smbstat_interval);
 423                 smbstat_snapshot_inc_idx();
 424         }
 425         return (0);
 426 }
 427 
 428 /*
 429  * smbstat_init
 430  *
 431  * Global initialization.
 432  */
 433 static void
 434 smbstat_init(void)
 435 {
 436         if ((smbstat_ksc = kstat_open()) == NULL)
 437                 smbstat_fail(1, gettext("kstat_open(): can't open /dev/kstat"));
 438 
 439         smbstat_cpu_init();
 440         smbstat_srv_init();
 441         smbstat_wrk_init();
 442         smbstat_clsh_init();
 443 }
 444 
 445 /*
 446  * smbstat_fini
 447  *
 448  * Releases the resources smbstat_init() allocated.
 449  */
 450 static void
 451 smbstat_fini(void)
 452 {
 453         smbstat_clsh_fini();
 454         smbstat_wrk_fini();
 455         smbstat_srv_fini();
 456         smbstat_cpu_fini();
 457         (void) kstat_close(smbstat_ksc);
 458 }
 459 
 460 /*
 461  * smbstat_kstat_snapshot
 462  *
 463  * Takes a snapshot of the data.
 464  */
 465 static void
 466 smbstat_kstat_snapshot(void)
 467 {
 468         smbstat_cpu_snapshot();
 469         smbstat_srv_snapshot();
 470         smbstat_wrk_snapshot();
 471         smbstat_clsh_snapshot();
 472 }
 473 
 474 /*
 475  * smbstat_kstat_process
 476  */
 477 static void
 478 smbstat_kstat_process(void)
 479 {
 480         smbstat_cpu_process();
 481         smbstat_srv_process();
 482         smbstat_wrk_process();
 483         smbstat_clsh_process();
 484 }
 485 
 486 /*
 487  * smbstat_kstat_print
 488  *
 489  * Print the data processed.
 490  */
 491 static void
 492 smbstat_kstat_print(void)
 493 {
 494         smbstat_termio_init();
 495         smbstat_print_counters();
 496         smbstat_print_throughput();
 497         smbstat_print_utilization();
 498         smbstat_print_requests();
 499         smbstat_print_client_or_share();
 500         (void) fflush(stdout);
 501 }
 502 
 503 /*
 504  * smbstat_print_counters
 505  *
 506  * Displays the SMB server counters (session, users...).
 507  */
 508 static void
 509 smbstat_print_counters(void)
 510 {
 511         if (!smbstat_opt_c)
 512                 return;
 513 
 514         if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_t ||
 515             (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) {
 516                 (void) printf(SMBSRV_COUNTERS_BANNER);
 517                 smbstat_rows = 1;
 518         }
 519 
 520         (void) printf(SMBSRV_COUNTERS_FORMAT,
 521             smbstat_srv_info.si_nbt_sess,
 522             smbstat_srv_info.si_tcp_sess,
 523             smbstat_srv_info.si_users,
 524             smbstat_srv_info.si_trees,
 525             smbstat_srv_info.si_files,
 526             smbstat_srv_info.si_pipes);
 527 
 528         ++smbstat_rows;
 529 }
 530 /*
 531  * smbstat_print_throughput
 532  *
 533  * Formats the SMB server throughput output.
 534  */
 535 static void
 536 smbstat_print_throughput(void)
 537 {
 538         if (!smbstat_opt_t)
 539                 return;
 540 
 541         if (smbstat_opt_u || smbstat_opt_r || smbstat_opt_c ||
 542             smbstat_opt_C || smbstat_opt_S || (smbstat_rows == 0) ||
 543             (smbstat_rows >= smbstat_ws.ws_row)) {
 544                 (void) printf(SMBSRV_THROUGHPUT_BANNER);
 545                 smbstat_rows = 1;
 546         }
 547         (void) printf(SMBSRV_THROUGHPUT_FORMAT,
 548             smbstat_zero(smbstat_srv_info.si_rbs),
 549             smbstat_zero(smbstat_srv_info.si_tbs),
 550             smbstat_zero(smbstat_srv_info.si_rqs),
 551             smbstat_zero(smbstat_srv_info.si_rds),
 552             smbstat_zero(smbstat_srv_info.si_wrs));
 553 
 554         ++smbstat_rows;
 555 }
 556 
 557 /*
 558  * smbstat_print_utilization
 559  */
 560 static void
 561 smbstat_print_utilization(void)
 562 {
 563         char    *sat;
 564         if (!smbstat_opt_u)
 565                 return;
 566 
 567         if (smbstat_opt_t || smbstat_opt_r || smbstat_opt_c ||
 568             (smbstat_rows == 0) || (smbstat_rows >= smbstat_ws.ws_row)) {
 569                 (void) printf(SMBSRV_UTILIZATION_BANNER);
 570                 smbstat_rows = 1;
 571         }
 572 
 573         if (smbstat_srv_info.si_sat)
 574                 sat = "yes";
 575         else
 576                 sat = "no ";
 577 
 578         (void) printf(SMBSRV_UTILIZATION_FORMAT,
 579             smbstat_srv_info.si_avw,
 580             smbstat_srv_info.si_avr,
 581             smbstat_srv_info.si_wserv,
 582             smbstat_srv_info.si_rserv,
 583             smbstat_zero(smbstat_srv_info.si_wpct),
 584             smbstat_zero(smbstat_srv_info.si_rpct),
 585             smbstat_zero(smbstat_srv_info.si_upct),
 586             sat,
 587             smbstat_srv_info.si_ticks[CPU_TICKS_USER],
 588             smbstat_srv_info.si_ticks[CPU_TICKS_KERNEL],
 589             smbstat_srv_info.si_ticks[CPU_TICKS_IDLE]);
 590 
 591         ++smbstat_rows;
 592 }
 593 
 594 /*
 595  * smbstat_print_requests
 596  */
 597 static void
 598 smbstat_print_requests(void)
 599 {
 600         smbstat_req_info_t      *prq;
 601         int                     i;
 602 
 603         if (!smbstat_opt_r)
 604                 return;
 605 
 606         (void) printf(SMBSRV_REQUESTS_BANNER, "       ");
 607 
 608         prq = smbstat_srv_info.si_reqs1;
 609         for (i = 0; i < SMBSRV_KS_NREQS1; i++) {
 610                 if (!smbstat_opt_a &&
 611                     strncmp(prq[i].ri_name, "Invalid", sizeof ("Invalid")) == 0)
 612                         continue;
 613 
 614                 if (!smbstat_opt_z || (prq[i].ri_pct != 0)) {
 615                         (void) printf(SMBSRV_REQUESTS_FORMAT,
 616                             prq[i].ri_name,
 617                             prq[i].ri_opcode,
 618                             smbstat_zero(prq[i].ri_pct),
 619                             smbstat_zero(prq[i].ri_rbs),
 620                             smbstat_zero(prq[i].ri_tbs),
 621                             smbstat_zero(prq[i].ri_rqs),
 622                             prq[i].ri_mean,
 623                             prq[i].ri_stddev);
 624                 }
 625         }
 626 
 627         prq = smbstat_srv_info.si_reqs2;
 628         for (i = 0; i < SMBSRV_KS_NREQS2; i++) {
 629                 if (!smbstat_opt_a && i == SMB2_INVALID_CMD)
 630                         continue;
 631 
 632                 if (!smbstat_opt_z || (prq[i].ri_pct != 0)) {
 633                         (void) printf(SMBSRV_REQUESTS_FORMAT,
 634                             prq[i].ri_name,
 635                             prq[i].ri_opcode,
 636                             smbstat_zero(prq[i].ri_pct),
 637                             smbstat_zero(prq[i].ri_rbs),
 638                             smbstat_zero(prq[i].ri_tbs),
 639                             smbstat_zero(prq[i].ri_rqs),
 640                             prq[i].ri_mean,
 641                             prq[i].ri_stddev);
 642                 }
 643         }
 644 }
 645 
 646 static void
 647 smbstat_print_client_or_share(void)
 648 {
 649         smbstat_req_info_t      *prq;
 650         int     idx;
 651 
 652         if (smbstat_opt_clsh == NULL)
 653                 return;
 654 
 655         (void) printf(SMBSRV_CLNT_SHARE_BANNER, "       ");
 656 
 657         prq = &smbstat_srv_info.si_clsh[0];
 658 
 659         for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++, prq++) {
 660         (void) printf(SMBSRV_CLNT_SHARE_FORMAT,
 661             prq->ri_name,
 662             smbstat_zero(prq->ri_rbs),
 663             smbstat_zero(prq->ri_tbs),
 664             smbstat_zero(prq->ri_rqs),
 665             prq->ri_mean,
 666             prq->ri_stddev);
 667         }
 668 }
 669 
 670 
 671 /*
 672  * smbstat_cpu_init
 673  */
 674 static void
 675 smbstat_cpu_init(void)
 676 {
 677         size_t  size;
 678         int     i;
 679 
 680         smbstat_nrcpus = sysconf(_SC_CPUID_MAX) + 1;
 681         size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t);
 682 
 683         for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++)
 684                 smbstat_cpu_snapshots[i] = smbstat_zalloc(size);
 685 }
 686 
 687 /*
 688  * smbstat_cpu_fini
 689  */
 690 static void
 691 smbstat_cpu_fini(void)
 692 {
 693         size_t  size;
 694         int     i;
 695 
 696         size = smbstat_nrcpus * sizeof (smbstat_cpu_snapshot_t);
 697 
 698         for (i = 0; i < SMBSTAT_SNAPSHOT_COUNT; i++)
 699                 smbstat_free(smbstat_cpu_snapshots[i], size);
 700 }
 701 
 702 /*
 703  * smbstat_cpu_current_snapshot
 704  */
 705 static smbstat_cpu_snapshot_t *
 706 smbstat_cpu_current_snapshot(void)
 707 {
 708         return (smbstat_cpu_snapshots[smbstat_snapshot_idx]);
 709 }
 710 
 711 /*
 712  * smbstat_cpu_previous_snapshot
 713  */
 714 static smbstat_cpu_snapshot_t *
 715 smbstat_cpu_previous_snapshot(void)
 716 {
 717         int     idx;
 718 
 719         idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK;
 720         return (smbstat_cpu_snapshots[idx]);
 721 }
 722 
 723 /*
 724  * smbstat_cpu_snapshot
 725  */
 726 static void
 727 smbstat_cpu_snapshot(void)
 728 {
 729         kstat_t                 *ksp;
 730         kstat_named_t           *ksn;
 731         smbstat_cpu_snapshot_t  *curr;
 732         long                    i;
 733         int                     j;
 734 
 735         curr =  smbstat_cpu_current_snapshot();
 736 
 737         for (i = 0; i < smbstat_nrcpus;      i++, curr++) {
 738                 curr->cs_id = SMBSTAT_ID_NO_CPU;
 739                 curr->cs_state = p_online(i, P_STATUS);
 740                 /* If no valid CPU is present, move on to the next one */
 741                 if (curr->cs_state == -1)
 742                         continue;
 743 
 744                 curr->cs_id = i;
 745 
 746                 ksp = kstat_lookup(smbstat_ksc, "cpu", i, "sys");
 747                 if (ksp == NULL)
 748                         smbstat_fail(1,
 749                             gettext("kstat_lookup('cpu sys %d') failed"), i);
 750 
 751                 if (kstat_read(smbstat_ksc, ksp, NULL) == -1)
 752                         smbstat_fail(1,
 753                             gettext("kstat_read('cpu sys %d') failed"), i);
 754 
 755                 for (j = 0; j < CPU_TICKS_SENTINEL; j++) {
 756                         ksn = kstat_data_lookup(ksp, smbstat_cpu_states[j]);
 757                         if (ksn == NULL)
 758                                 smbstat_fail(1,
 759                                     gettext("kstat_data_lookup('%s') failed"),
 760                                     smbstat_cpu_states[j]);
 761                         curr->cs_ticks[j] = ksn->value.ui64;
 762                 }
 763         }
 764 }
 765 
 766 /*
 767  * smbstat_cpu_process
 768  */
 769 static void
 770 smbstat_cpu_process(void)
 771 {
 772         smbstat_cpu_snapshot_t  *curr, *prev;
 773         double                  total_ticks;
 774         double                  agg_ticks[CPU_TICKS_SENTINEL];
 775         int                     i, j;
 776 
 777         curr =  smbstat_cpu_current_snapshot();
 778         prev =  smbstat_cpu_previous_snapshot();
 779         bzero(agg_ticks, sizeof (agg_ticks));
 780         total_ticks = 0;
 781 
 782         for (i = 0; i < smbstat_nrcpus; i++, curr++, prev++) {
 783                 for (j = 0; j < CPU_TICKS_SENTINEL; j++) {
 784                         agg_ticks[j] += smbstat_sub_64(curr->cs_ticks[j],
 785                             prev->cs_ticks[j]);
 786                         total_ticks += smbstat_sub_64(curr->cs_ticks[j],
 787                             prev->cs_ticks[j]);
 788                 }
 789         }
 790 
 791         for (j = 0; j < CPU_TICKS_SENTINEL; j++)
 792                 smbstat_srv_info.si_ticks[j] =
 793                     (agg_ticks[j] * 100.0) / total_ticks;
 794 }
 795 
 796 /*
 797  * smbstat_wrk_init
 798  */
 799 static void
 800 smbstat_wrk_init(void)
 801 {
 802         smbstat_wrk_ksp =
 803             kstat_lookup(smbstat_ksc, "unix", -1, SMBSRV_KSTAT_WORKERS);
 804         if (smbstat_wrk_ksp == NULL)
 805                 smbstat_fail(1,
 806                     gettext("cannot retrieve smbsrv workers kstat\n"));
 807 }
 808 
 809 static void
 810 smbstat_wrk_fini(void)
 811 {
 812         smbstat_wrk_ksp = NULL;
 813 }
 814 
 815 /*
 816  * smbstat_wrk_snapshot
 817  */
 818 static void
 819 smbstat_wrk_snapshot(void)
 820 {
 821         smbstat_wrk_snapshot_t  *curr;
 822         kstat_named_t           *kn;
 823 
 824         curr = smbstat_wrk_current_snapshot();
 825 
 826         if (kstat_read(smbstat_ksc, smbstat_wrk_ksp, NULL) == -1)
 827                 smbstat_fail(1, gettext("kstat_read('%s') failed"),
 828                     smbstat_wrk_ksp->ks_name);
 829 
 830         kn = kstat_data_lookup(smbstat_wrk_ksp, "maxthreads");
 831         if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64))
 832                 smbstat_fail(1, gettext("kstat_read('%s') failed"),
 833                     "maxthreads");
 834         curr->ws_maxthreads = kn->value.ui64;
 835 
 836         kn = kstat_data_lookup(smbstat_wrk_ksp, "bnalloc");
 837         if ((kn == NULL) || (kn->data_type != KSTAT_DATA_UINT64))
 838                 smbstat_fail(1, gettext("kstat_read('%s') failed"),
 839                     "bnalloc");
 840         curr->ws_bnalloc = kn->value.ui64;
 841 }
 842 
 843 /*
 844  * smbstat_wrk_process
 845  */
 846 static void
 847 smbstat_wrk_process(void)
 848 {
 849         smbstat_wrk_snapshot_t  *curr;
 850 
 851         curr = smbstat_wrk_current_snapshot();
 852 
 853         if (curr->ws_bnalloc >= curr->ws_maxthreads)
 854                 smbstat_srv_info.si_sat = B_TRUE;
 855         else
 856                 smbstat_srv_info.si_sat = B_FALSE;
 857 }
 858 
 859 /*
 860  * smbstat_wrk_current_snapshot
 861  */
 862 static smbstat_wrk_snapshot_t *
 863 smbstat_wrk_current_snapshot(void)
 864 {
 865         return (&smbstat_wrk_snapshots[smbstat_snapshot_idx]);
 866 }
 867 
 868 /*
 869  * smbstat_clsh_init
 870  *
 871  * Lookup the kstat for the client or share specified.
 872  * The names for these look like:
 873  *      session (a.k.a. client)
 874  *              cl/$IPADDR  and instance ss->s_kid
 875  *      share
 876  *              sh/sharename    (always instance 0)
 877  *
 878  * These will look like: smbsrv:0:sh/myshare
 879  * or smbsrv:N:cl/10.10.0.2
 880  */
 881 static void
 882 smbstat_clsh_init(void)
 883 {
 884         static const char       *kr_names[] = SMBSRV_CLSH__NAMES;
 885         char    ks_name[KSTAT_STRLEN];
 886         char    *prefix = "";
 887         smbstat_req_info_t *info;
 888         int     instance = -1;
 889         int     i;
 890 
 891         if (smbstat_opt_clsh == NULL)
 892                 return;
 893 
 894         /*
 895          * Currently we don't take an instance so this will return data
 896          * from the first or the only client from the IP address specified.
 897          */
 898         if (smbstat_opt_C)
 899                 prefix = "cl";
 900         if (smbstat_opt_S)
 901                 prefix = "sh";
 902         (void) snprintf(ks_name, sizeof (ks_name),
 903             "%s/%s", prefix, smbstat_opt_clsh);
 904 
 905         smbstat_clsh_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE,
 906             instance, ks_name);
 907         if (smbstat_clsh_ksp == NULL) {
 908                 smbstat_fail(1, gettext("cannot retrieve smbsrv %s kstat\n"),
 909                     ks_name);
 910         }
 911 
 912         info = smbstat_srv_info.si_clsh;
 913         for (i = 0; i < SMBSRV_CLSH__NREQ; i++) {
 914                 (void) strlcpy(info[i].ri_name, kr_names[i],
 915                     sizeof (info[i].ri_name));
 916         }
 917 }
 918 
 919 static void
 920 smbstat_clsh_fini(void)
 921 {
 922         smbstat_clsh_ksp = NULL;
 923 }
 924 
 925 /*
 926  * smbstat_clsh_snapshot
 927  */
 928 static void
 929 smbstat_clsh_snapshot(void)
 930 {
 931         smbstat_clsh_snapshot_t *curr;
 932 
 933         if (smbstat_clsh_ksp == NULL)
 934                 return;
 935 
 936         curr = smbstat_clsh_current_snapshot();
 937 
 938         if ((kstat_read(smbstat_ksc, smbstat_clsh_ksp, NULL) == -1) ||
 939             (smbstat_clsh_ksp->ks_data_size != sizeof (curr->cs_data)))
 940                 smbstat_fail(1, gettext("kstat_read('%s') failed"),
 941                     smbstat_clsh_ksp->ks_name);
 942 
 943         curr->cs_snaptime = smbstat_clsh_ksp->ks_snaptime;
 944 
 945         bcopy(smbstat_clsh_ksp->ks_data, &curr->cs_data,
 946             sizeof (curr->cs_data));
 947 }
 948 
 949 /*
 950  * smbstat_clsh_process
 951  */
 952 static void
 953 smbstat_clsh_process(void)
 954 {
 955         smbstat_clsh_snapshot_t *curr, *prev;
 956         boolean_t               firstcall;
 957         int     idx;
 958 
 959         curr = smbstat_clsh_current_snapshot();
 960         prev = smbstat_clsh_previous_snapshot();
 961         firstcall = (prev->cs_snaptime == 0);
 962 
 963 
 964         for (idx = 0; idx < SMBSRV_CLSH__NREQ; idx++) {
 965                 smbstat_srv_process_one_req(
 966                     &smbstat_srv_info.si_clsh[idx],
 967                     &curr->cs_data.ks_clsh[idx],
 968                     &prev->cs_data.ks_clsh[idx],
 969                     firstcall);
 970         }
 971 }
 972 
 973 /*
 974  * smbstat_clsh_current_snapshot
 975  */
 976 static smbstat_clsh_snapshot_t *
 977 smbstat_clsh_current_snapshot(void)
 978 {
 979         return (&smbstat_clsh_snapshots[smbstat_snapshot_idx]);
 980 }
 981 
 982 static smbstat_clsh_snapshot_t *
 983 smbstat_clsh_previous_snapshot(void)
 984 {
 985         int     idx;
 986 
 987         idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK;
 988         return (&smbstat_clsh_snapshots[idx]);
 989 }
 990 
 991 /*
 992  * smbstat_srv_init
 993  */
 994 static void
 995 smbstat_srv_init(void)
 996 {
 997         smbstat_srv_ksp = kstat_lookup(smbstat_ksc, SMBSRV_KSTAT_MODULE,
 998             -1, SMBSRV_KSTAT_STATISTICS);
 999         if (smbstat_srv_ksp == NULL)
1000                 smbstat_fail(1, gettext("cannot retrieve smbsrv kstat\n"));
1001 }
1002 
1003 /*
1004  * smbstat_srv_fini
1005  */
1006 static void
1007 smbstat_srv_fini(void)
1008 {
1009         smbstat_srv_ksp = NULL;
1010 }
1011 
1012 /*
1013  * smbstat_srv_snapshot
1014  *
1015  * Take a snapshot of the smbsrv module statistics.
1016  */
1017 static void
1018 smbstat_srv_snapshot(void)
1019 {
1020         smbstat_srv_snapshot_t  *curr;
1021 
1022         curr = smbstat_srv_current_snapshot();
1023 
1024         if ((kstat_read(smbstat_ksc, smbstat_srv_ksp, NULL) == -1) ||
1025             (smbstat_srv_ksp->ks_data_size != sizeof (curr->ss_data)))
1026                 smbstat_fail(1, gettext("kstat_read('%s') failed"),
1027                     smbstat_srv_ksp->ks_name);
1028 
1029         curr->ss_snaptime = smbstat_srv_ksp->ks_snaptime;
1030         bcopy(smbstat_srv_ksp->ks_data, &curr->ss_data, sizeof (curr->ss_data));
1031 }
1032 
1033 /*
1034  * smbstat_srv_process
1035  *
1036  * Processes the snapshot data.
1037  */
1038 static void
1039 smbstat_srv_process(void)
1040 {
1041         smbstat_srv_snapshot_t  *curr, *prev;
1042 
1043         curr = smbstat_srv_current_snapshot();
1044         prev = smbstat_srv_previous_snapshot();
1045 
1046         if (prev->ss_snaptime == 0)
1047                 smbstat_srv_info.si_hretime =
1048                     smbstat_hrtime_delta(curr->ss_data.ks_start_time,
1049                     curr->ss_snaptime);
1050         else
1051                 smbstat_srv_info.si_hretime =
1052                     smbstat_hrtime_delta(prev->ss_snaptime, curr->ss_snaptime);
1053 
1054         smbstat_srv_info.si_etime = smbstat_srv_info.si_hretime / NANOSEC;
1055         smbstat_srv_info.si_total_nreqs =
1056             smbstat_sub_64(curr->ss_data.ks_nreq, prev->ss_data.ks_nreq);
1057 
1058         if (smbstat_opt_c)
1059                 smbstat_srv_process_counters(curr);
1060         if (smbstat_opt_t)
1061                 smbstat_srv_process_throughput(curr, prev);
1062         if (smbstat_opt_u)
1063                 smbstat_srv_process_utilization(curr, prev);
1064         if (smbstat_opt_r)
1065                 smbstat_srv_process_requests(curr, prev);
1066 }
1067 
1068 /*
1069  * smbstat_srv_process_counters
1070  */
1071 static void
1072 smbstat_srv_process_counters(smbstat_srv_snapshot_t *curr)
1073 {
1074         smbstat_srv_info.si_nbt_sess = curr->ss_data.ks_nbt_sess;
1075         smbstat_srv_info.si_tcp_sess = curr->ss_data.ks_tcp_sess;
1076         smbstat_srv_info.si_users = curr->ss_data.ks_users;
1077         smbstat_srv_info.si_trees = curr->ss_data.ks_trees;
1078         smbstat_srv_info.si_files = curr->ss_data.ks_files;
1079         smbstat_srv_info.si_pipes = curr->ss_data.ks_pipes;
1080 }
1081 
1082 /*
1083  * smbstat_srv_process_throughput
1084  *
1085  * Processes the data relative to the throughput of the smbsrv module and
1086  * stores the results in the structure smbstat_srv_info.
1087  */
1088 static void
1089 smbstat_srv_process_throughput(
1090     smbstat_srv_snapshot_t      *curr,
1091     smbstat_srv_snapshot_t      *prev)
1092 {
1093         smbstat_srv_info.si_tbs =
1094             smbstat_sub_64(curr->ss_data.ks_txb, prev->ss_data.ks_txb);
1095         smbstat_srv_info.si_tbs /= smbstat_srv_info.si_etime;
1096         smbstat_srv_info.si_rbs =
1097             smbstat_sub_64(curr->ss_data.ks_rxb, prev->ss_data.ks_rxb);
1098         smbstat_srv_info.si_rbs /= smbstat_srv_info.si_etime;
1099         smbstat_srv_info.si_rqs = smbstat_srv_info.si_total_nreqs;
1100         smbstat_srv_info.si_rqs /= smbstat_srv_info.si_etime;
1101 
1102         smbstat_srv_info.si_rds = smbstat_sub_64(
1103             curr->ss_data.ks_reqs1[SMB_COM_READ].kr_nreq,
1104             prev->ss_data.ks_reqs1[SMB_COM_READ].kr_nreq);
1105         smbstat_srv_info.si_rds += smbstat_sub_64(
1106             curr->ss_data.ks_reqs1[SMB_COM_LOCK_AND_READ].kr_nreq,
1107             prev->ss_data.ks_reqs1[SMB_COM_LOCK_AND_READ].kr_nreq);
1108         smbstat_srv_info.si_rds += smbstat_sub_64(
1109             curr->ss_data.ks_reqs1[SMB_COM_READ_RAW].kr_nreq,
1110             prev->ss_data.ks_reqs1[SMB_COM_READ_RAW].kr_nreq);
1111         smbstat_srv_info.si_rds += smbstat_sub_64(
1112             curr->ss_data.ks_reqs1[SMB_COM_READ_ANDX].kr_nreq,
1113             prev->ss_data.ks_reqs1[SMB_COM_READ_ANDX].kr_nreq);
1114         smbstat_srv_info.si_rds += smbstat_sub_64(
1115             curr->ss_data.ks_reqs2[SMB2_READ].kr_nreq,
1116             prev->ss_data.ks_reqs2[SMB2_READ].kr_nreq);
1117         smbstat_srv_info.si_rds /= smbstat_srv_info.si_etime;
1118 
1119         smbstat_srv_info.si_wrs = smbstat_sub_64(
1120             curr->ss_data.ks_reqs1[SMB_COM_WRITE].kr_nreq,
1121             prev->ss_data.ks_reqs1[SMB_COM_WRITE].kr_nreq);
1122         smbstat_srv_info.si_wrs += smbstat_sub_64(
1123             curr->ss_data.ks_reqs1[SMB_COM_WRITE_AND_UNLOCK].kr_nreq,
1124             prev->ss_data.ks_reqs1[SMB_COM_WRITE_AND_UNLOCK].kr_nreq);
1125         smbstat_srv_info.si_wrs += smbstat_sub_64(
1126             curr->ss_data.ks_reqs1[SMB_COM_WRITE_RAW].kr_nreq,
1127             prev->ss_data.ks_reqs1[SMB_COM_WRITE_RAW].kr_nreq);
1128         smbstat_srv_info.si_wrs += smbstat_sub_64(
1129             curr->ss_data.ks_reqs1[SMB_COM_WRITE_AND_CLOSE].kr_nreq,
1130             prev->ss_data.ks_reqs1[SMB_COM_WRITE_AND_CLOSE].kr_nreq);
1131         smbstat_srv_info.si_wrs += smbstat_sub_64(
1132             curr->ss_data.ks_reqs1[SMB_COM_WRITE_ANDX].kr_nreq,
1133             prev->ss_data.ks_reqs1[SMB_COM_WRITE_ANDX].kr_nreq);
1134         smbstat_srv_info.si_wrs += smbstat_sub_64(
1135             curr->ss_data.ks_reqs2[SMB2_WRITE].kr_nreq,
1136             prev->ss_data.ks_reqs2[SMB2_WRITE].kr_nreq);
1137         smbstat_srv_info.si_wrs /= smbstat_srv_info.si_etime;
1138 }
1139 
1140 /*
1141  * smbstat_srv_process_utilization
1142  *
1143  * Processes the data relative to the utilization of the smbsrv module and
1144  * stores the results in the structure smbstat_srv_info.
1145  */
1146 static void
1147 smbstat_srv_process_utilization(
1148     smbstat_srv_snapshot_t      *curr,
1149     smbstat_srv_snapshot_t      *prev)
1150 {
1151         double  tw_delta, tr_delta;
1152         double  w_delta, r_delta;
1153         double  tps, rqs;
1154 
1155         w_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wlentime,
1156             curr->ss_data.ks_utilization.ku_wlentime);
1157         r_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rlentime,
1158             curr->ss_data.ks_utilization.ku_rlentime);
1159         tw_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_wtime,
1160             curr->ss_data.ks_utilization.ku_wtime);
1161         tr_delta = smbstat_hrtime_delta(prev->ss_data.ks_utilization.ku_rtime,
1162             curr->ss_data.ks_utilization.ku_rtime);
1163         rqs = smbstat_srv_info.si_total_nreqs / smbstat_srv_info.si_etime;
1164 
1165         /* Average number of requests waiting */
1166         if (w_delta != 0)
1167                 smbstat_srv_info.si_avw = w_delta / smbstat_srv_info.si_hretime;
1168         else
1169                 smbstat_srv_info.si_avw = 0.0;
1170 
1171         /* Average number of request running */
1172         if (r_delta != 0)
1173                 smbstat_srv_info.si_avr = r_delta / smbstat_srv_info.si_hretime;
1174         else
1175                 smbstat_srv_info.si_avr = 0.0;
1176 
1177         /* Utilization */
1178         smbstat_srv_info.si_upct =
1179             (smbstat_srv_info.si_avr / curr->ss_data.ks_maxreqs) * 100;
1180 
1181         /* Average wait service time in milliseconds */
1182         smbstat_srv_info.si_rserv = 0.0;
1183         smbstat_srv_info.si_wserv = 0.0;
1184         if (rqs > 0.0 &&
1185             (smbstat_srv_info.si_avw != 0.0 ||
1186             smbstat_srv_info.si_avr != 0.0)) {
1187                 tps = 1 / rqs;
1188                 if (smbstat_srv_info.si_avw != 0.0)
1189                         smbstat_srv_info.si_wserv =
1190                             smbstat_srv_info.si_avw * tps;
1191                 if (smbstat_srv_info.si_avr != 0.0)
1192                         smbstat_srv_info.si_rserv =
1193                             smbstat_srv_info.si_avr * tps;
1194         }
1195 
1196         /* % of time there is a transaction waiting for service */
1197         if (tw_delta != 0) {
1198                 smbstat_srv_info.si_wpct = tw_delta;
1199                 smbstat_srv_info.si_wpct /= smbstat_srv_info.si_hretime;
1200                 smbstat_srv_info.si_wpct *= 100.0;
1201         } else {
1202                 smbstat_srv_info.si_wpct = 0.0;
1203         }
1204 
1205         /* % of time there is a transaction running */
1206         if (tr_delta != 0) {
1207                 smbstat_srv_info.si_rpct = tr_delta;
1208                 smbstat_srv_info.si_rpct /= smbstat_srv_info.si_hretime;
1209                 smbstat_srv_info.si_rpct *= 100.0;
1210         } else {
1211                 smbstat_srv_info.si_rpct = 0.0;
1212         }
1213 }
1214 
1215 /*
1216  * smbstat_srv_process_requests
1217  *
1218  * Processes the data relative to the SMB requests and stores the results in
1219  * the structure smbstat_srv_info.
1220  */
1221 static void
1222 smbstat_srv_process_requests(
1223     smbstat_srv_snapshot_t      *curr,
1224     smbstat_srv_snapshot_t      *prev)
1225 {
1226         smbstat_req_info_t      *info;
1227         smb_kstat_req_t         *curr_req;
1228         smb_kstat_req_t         *prev_req;
1229         int                     i, idx;
1230         boolean_t       firstcall = (prev->ss_snaptime == 0);
1231 
1232         for (i = 0; i < SMBSRV_KS_NREQS1; i++) {
1233                 info = &smbstat_srv_info.si_reqs1[i];
1234                 idx = info[i].ri_opcode & 0xFF;
1235                 curr_req = &curr->ss_data.ks_reqs1[idx];
1236                 prev_req = &prev->ss_data.ks_reqs1[idx];
1237                 smbstat_srv_process_one_req(
1238                     info, curr_req, prev_req, firstcall);
1239         }
1240 
1241         for (i = 0; i < SMBSRV_KS_NREQS2; i++) {
1242                 info = &smbstat_srv_info.si_reqs2[i];
1243                 curr_req = &curr->ss_data.ks_reqs2[i];
1244                 prev_req = &prev->ss_data.ks_reqs2[i];
1245                 smbstat_srv_process_one_req(
1246                     info, curr_req, prev_req, firstcall);
1247         }
1248 }
1249 
1250 static void
1251 smbstat_srv_process_one_req(
1252         smbstat_req_info_t      *info,
1253         smb_kstat_req_t         *curr_req,
1254         smb_kstat_req_t         *prev_req,
1255         boolean_t               firstcall)
1256 {
1257         double                  nrqs;
1258 
1259         nrqs = smbstat_sub_64(curr_req->kr_nreq,
1260             prev_req->kr_nreq);
1261 
1262         info->ri_rqs = nrqs / smbstat_srv_info.si_etime;
1263 
1264         info->ri_rbs = smbstat_sub_64(
1265             curr_req->kr_rxb,
1266             prev_req->kr_rxb) /
1267             smbstat_srv_info.si_etime;
1268 
1269         info->ri_tbs = smbstat_sub_64(
1270             curr_req->kr_txb,
1271             prev_req->kr_txb) /
1272             smbstat_srv_info.si_etime;
1273 
1274         info->ri_pct = nrqs * 100;
1275         if (smbstat_srv_info.si_total_nreqs > 0)
1276                 info->ri_pct /= smbstat_srv_info.si_total_nreqs;
1277 
1278         if (firstcall) {
1279                 /* First time. Take the aggregate */
1280                 info->ri_stddev =
1281                     curr_req->kr_a_stddev;
1282                 info->ri_mean = curr_req->kr_a_mean;
1283         } else {
1284                 /* Take the differential */
1285                 info->ri_stddev =
1286                     curr_req->kr_d_stddev;
1287                 info->ri_mean = curr_req->kr_d_mean;
1288         }
1289         if (nrqs > 0) {
1290                 info->ri_stddev /= nrqs;
1291                 info->ri_stddev = sqrt(info->ri_stddev);
1292         } else {
1293                 info->ri_stddev = 0;
1294         }
1295         info->ri_stddev /= NANOSEC;
1296         info->ri_mean /= NANOSEC;
1297 }
1298 
1299 /*
1300  * smbstat_srv_current_snapshot
1301  *
1302  * Returns the current snapshot.
1303  */
1304 static smbstat_srv_snapshot_t *
1305 smbstat_srv_current_snapshot(void)
1306 {
1307         return (&smbstat_srv_snapshots[smbstat_snapshot_idx]);
1308 }
1309 
1310 /*
1311  * smbstat_srv_previous_snapshot
1312  *
1313  * Returns the previous snapshot.
1314  */
1315 static smbstat_srv_snapshot_t *
1316 smbstat_srv_previous_snapshot(void)
1317 {
1318         int     idx;
1319 
1320         idx = (smbstat_snapshot_idx - 1) & SMBSTAT_SNAPSHOT_MASK;
1321         return (&smbstat_srv_snapshots[idx]);
1322 }
1323 
1324 /*
1325  * smbstat_usage
1326  *
1327  * Prints out a help message.
1328  */
1329 static void
1330 smbstat_usage(FILE *fd, int exit_code)
1331 {
1332         (void) fprintf(fd, gettext(SMBSTAT_HELP));
1333         exit(exit_code);
1334 }
1335 
1336 /*
1337  * smbstat_fail
1338  *
1339  * Prints out to stderr an error message and exits the process.
1340  */
1341 static void
1342 smbstat_fail(int do_perror, char *message, ...)
1343 {
1344         va_list args;
1345 
1346         va_start(args, message);
1347         (void) fprintf(stderr, gettext("smbstat: "));
1348         /* LINTED E_SEC_PRINTF_VAR_FMT */
1349         (void) vfprintf(stderr, message, args);
1350         va_end(args);
1351         if (do_perror)
1352                 (void) fprintf(stderr, ": %s", strerror(errno));
1353         (void) fprintf(stderr, "\n");
1354         exit(1);
1355 }
1356 
1357 /*
1358  * smbstat_sub_64
1359  *
1360  * Substract 2 uint64_t and returns a double.
1361  */
1362 static double
1363 smbstat_sub_64(uint64_t a, uint64_t b)
1364 {
1365         return ((double)(a - b));
1366 }
1367 
1368 /*
1369  * smbstat_zero
1370  *
1371  * Returns zero if the value passed in is less than 1.
1372  */
1373 static double
1374 smbstat_zero(double value)
1375 {
1376         if (value < 1)
1377                 value = 0;
1378         return (value);
1379 }
1380 
1381 /*
1382  * smbstat_strtoi
1383  *
1384  * Converts a string representing an integer value into its binary value.
1385  * If the conversion fails this routine exits the process.
1386  */
1387 static uint_t
1388 smbstat_strtoi(char const *val, char *errmsg)
1389 {
1390         char    *end;
1391         long    tmp;
1392 
1393         errno = 0;
1394         tmp = strtol(val, &end, 10);
1395         if (*end != '\0' || errno)
1396                 smbstat_fail(1, "%s %s", errmsg, val);
1397         return ((uint_t)tmp);
1398 }
1399 
1400 /*
1401  * smbstat_termio_init
1402  *
1403  * Determines the size of the terminal associated with the process.
1404  */
1405 static void
1406 smbstat_termio_init(void)
1407 {
1408         char    *envp;
1409 
1410         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &smbstat_ws) != -1) {
1411                 if (smbstat_ws.ws_row == 0) {
1412                         envp = getenv("LINES");
1413                         if (envp != NULL)
1414                                 smbstat_ws.ws_row = atoi(envp);
1415                 }
1416 
1417                 if (smbstat_ws.ws_col == 0) {
1418                         envp = getenv("COLUMNS");
1419                         if (envp != NULL)
1420                                 smbstat_ws.ws_row = atoi(envp);
1421                 }
1422         }
1423         if (smbstat_ws.ws_col == 0)
1424                 smbstat_ws.ws_col = 80;
1425         if (smbstat_ws.ws_row == 0)
1426                 smbstat_ws.ws_row = 25;
1427 }
1428 
1429 /*
1430  * smbstat_snapshot_idx_inc
1431  *
1432  * Increments the snapshot index.
1433  */
1434 static void
1435 smbstat_snapshot_inc_idx(void)
1436 {
1437         smbstat_snapshot_idx++;
1438         smbstat_snapshot_idx &= SMBSTAT_SNAPSHOT_MASK;
1439 }
1440 
1441 /*
1442  * smbstat_req_cmp_name
1443  *
1444  * Call back function passed to qsort() when the list of requests must be sorted
1445  * by name.
1446  */
1447 static int
1448 smbstat_req_cmp_name(const void *obj1, const void *obj2)
1449 {
1450         return (strncasecmp(
1451             ((smbstat_req_info_t *)obj1)->ri_name,
1452             ((smbstat_req_info_t *)obj2)->ri_name,
1453             sizeof (((smbstat_req_info_t *)obj2)->ri_name)));
1454 }
1455 
1456 /*
1457  * smbstat_req_order
1458  *
1459  * Snapshots the smbsrv module statistics once to get the name of the requests.
1460  * The request list is smbstat_srv_info is then sorted by name or by code
1461  * depending on the boolean smbstat_opt_a.
1462  * The function should be called once during initialization.
1463  */
1464 static void
1465 smbstat_req_order(void)
1466 {
1467         smbstat_srv_snapshot_t  *ss;
1468         smbstat_req_info_t      *info;
1469         smb_kstat_req_t         *reqs;
1470         int                     i;
1471 
1472         smbstat_srv_snapshot();
1473         ss = smbstat_srv_current_snapshot();
1474 
1475         reqs = ss->ss_data.ks_reqs1;
1476         info = smbstat_srv_info.si_reqs1;
1477         for (i = 0; i < SMBSRV_KS_NREQS1; i++) {
1478                 (void) strlcpy(info[i].ri_name, reqs[i].kr_name,
1479                     sizeof (reqs[i].kr_name));
1480                 info[i].ri_opcode = i;
1481         }
1482         if (smbstat_opt_n)
1483                 qsort(info, SMBSRV_KS_NREQS1, sizeof (smbstat_req_info_t),
1484                     smbstat_req_cmp_name);
1485 
1486         reqs = ss->ss_data.ks_reqs2;
1487         info = smbstat_srv_info.si_reqs2;
1488         for (i = 0; i < SMBSRV_KS_NREQS2; i++) {
1489                 (void) strlcpy(info[i].ri_name, reqs[i].kr_name,
1490                     sizeof (reqs[i].kr_name));
1491                 info[i].ri_opcode = i;
1492         }
1493         if (smbstat_opt_n)
1494                 qsort(info, SMBSRV_KS_NREQS2, sizeof (smbstat_req_info_t),
1495                     smbstat_req_cmp_name);
1496 }
1497 
1498 /*
1499  * Return the number of ticks delta between two hrtime_t
1500  * values. Attempt to cater for various kinds of overflow
1501  * in hrtime_t - no matter how improbable.
1502  */
1503 static double
1504 smbstat_hrtime_delta(hrtime_t old, hrtime_t new)
1505 {
1506         uint64_t        del;
1507 
1508         if ((new >= old) && (old >= 0L))
1509                 return ((double)(new - old));
1510         /*
1511          * We've overflowed the positive portion of an hrtime_t.
1512          */
1513         if (new < 0L) {
1514                 /*
1515                  * The new value is negative. Handle the case where the old
1516                  * value is positive or negative.
1517                  */
1518                 uint64_t n1;
1519                 uint64_t o1;
1520 
1521                 n1 = -new;
1522                 if (old > 0L)
1523                         return ((double)(n1 - old));
1524 
1525                 o1 = -old;
1526                 del = n1 - o1;
1527                 return ((double)del);
1528         }
1529 
1530         /*
1531          * Either we've just gone from being negative to positive *or* the last
1532          * entry was positive and the new entry is also positive but *less* than
1533          * the old entry. This implies we waited quite a few days on a very fast
1534          * system between displays.
1535          */
1536         if (old < 0L) {
1537                 uint64_t o2;
1538                 o2 = -old;
1539                 del = UINT64_MAX - o2;
1540         } else {
1541                 del = UINT64_MAX - old;
1542         }
1543         del += new;
1544         return ((double)del);
1545 }
1546 
1547 static void *
1548 smbstat_zalloc(size_t size)
1549 {
1550         void    *ptr;
1551 
1552         ptr = umem_zalloc(size, UMEM_DEFAULT);
1553         if (ptr == NULL)
1554                 smbstat_fail(1, gettext("out of memory"));
1555         return (ptr);
1556 }
1557 
1558 static void
1559 smbstat_free(void *ptr, size_t size)
1560 {
1561         umem_free(ptr, size);
1562 }