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