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