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