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