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