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
146typedef enum {
147	CPU_TICKS_IDLE = 0,
148	CPU_TICKS_USER,
149	CPU_TICKS_KERNEL,
150	CPU_TICKS_SENTINEL
151} cpu_state_idx_t;
152
153typedef 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
159typedef struct smbstat_srv_snapshot {
160	hrtime_t	ss_snaptime;
161	smbsrv_kstats_t	ss_data;
162} smbstat_srv_snapshot_t;
163
164typedef struct smbstat_wrk_snapshot {
165	uint64_t	ws_maxthreads;
166	uint64_t	ws_bnalloc;
167} smbstat_wrk_snapshot_t;
168
169typedef 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
180typedef 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
219static void smbstat_init(void);
220static void smbstat_fini(void);
221static void smbstat_kstat_snapshot(void);
222static void smbstat_kstat_process(void);
223static void smbstat_kstat_print(void);
224
225static void smbstat_print_counters(void);
226static void smbstat_print_throughput(void);
227static void smbstat_print_utilization(void);
228static void smbstat_print_requests(void);
229
230static void smbstat_cpu_init(void);
231static void smbstat_cpu_fini(void);
232static smbstat_cpu_snapshot_t *smbstat_cpu_current_snapshot(void);
233static smbstat_cpu_snapshot_t *smbstat_cpu_previous_snapshot(void);
234static void smbstat_cpu_snapshot(void);
235static void smbstat_cpu_process(void);
236
237static void smbstat_wrk_init(void);
238static void smbstat_wrk_fini(void);
239static void smbstat_wrk_snapshot(void);
240static void smbstat_wrk_process(void);
241static smbstat_wrk_snapshot_t *smbstat_wrk_current_snapshot(void);
242
243static void smbstat_srv_init(void);
244static void smbstat_srv_fini(void);
245static void smbstat_srv_snapshot(void);
246static void smbstat_srv_process(void);
247static void smbstat_srv_process_counters(smbstat_srv_snapshot_t *);
248static void smbstat_srv_process_throughput(smbstat_srv_snapshot_t *,
249    smbstat_srv_snapshot_t *);
250static void smbstat_srv_process_utilization(smbstat_srv_snapshot_t *,
251    smbstat_srv_snapshot_t *);
252static void smbstat_srv_process_requests(smbstat_srv_snapshot_t *,
253    smbstat_srv_snapshot_t *);
254static smbstat_srv_snapshot_t *smbstat_srv_current_snapshot(void);
255static smbstat_srv_snapshot_t *smbstat_srv_previous_snapshot(void);
256
257static void *smbstat_zalloc(size_t);
258static void smbstat_free(void *, size_t);
259static void smbstat_fail(int, char *, ...);
260static void smbstat_snapshot_inc_idx(void);
261static void smbstat_usage(FILE *, int);
262static uint_t smbstat_strtoi(char const *, char *);
263static double smbstat_hrtime_delta(hrtime_t, hrtime_t);
264static double smbstat_sub_64(uint64_t, uint64_t);
265static void smbstat_req_order(void);
266static double smbstat_zero(double);
267static void smbstat_termio_init(void);
268
269#pragma does_not_return(smbstat_fail, smbstat_usage)
270
271static char *smbstat_cpu_states[CPU_TICKS_SENTINEL] = {
272	"cpu_ticks_idle",
273	"cpu_ticks_user",
274	"cpu_ticks_kernel"
275};
276
277static boolean_t	smbstat_opt_a = B_FALSE;	/* all */
278static boolean_t	smbstat_opt_c = B_FALSE;	/* counters */
279static boolean_t	smbstat_opt_n = B_FALSE;	/* by name */
280static boolean_t	smbstat_opt_u = B_FALSE;	/* utilization */
281static boolean_t	smbstat_opt_t = B_FALSE;	/* throughput */
282static boolean_t	smbstat_opt_r = B_FALSE;	/* requests */
283static boolean_t	smbstat_opt_z = B_FALSE;	/* non-zero requests */
284
285static uint_t		smbstat_interval = 0;
286static long		smbstat_nrcpus = 0;
287static kstat_ctl_t	*smbstat_ksc = NULL;
288static kstat_t		*smbstat_srv_ksp = NULL;
289static kstat_t		*smbstat_wrk_ksp = NULL;
290static struct winsize	smbstat_ws;
291static uint16_t		smbstat_rows = 0;
292
293static int smbstat_snapshot_idx = 0;
294static smbstat_cpu_snapshot_t *smbstat_cpu_snapshots[SMBSTAT_SNAPSHOT_COUNT];
295static smbstat_srv_snapshot_t smbstat_srv_snapshots[SMBSTAT_SNAPSHOT_COUNT];
296static smbstat_wrk_snapshot_t smbstat_wrk_snapshots[SMBSTAT_SNAPSHOT_COUNT];
297static smbstat_srv_info_t smbstat_srv_info;
298
299/*
300 * main
301 */
302int
303main(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 */
391static void
392smbstat_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 */
408static void
409smbstat_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 */
422static void
423smbstat_kstat_snapshot(void)
424{
425	smbstat_cpu_snapshot();
426	smbstat_srv_snapshot();
427	smbstat_wrk_snapshot();
428}
429
430/*
431 * smbstat_kstat_process
432 */
433static void
434smbstat_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 */
446static void
447smbstat_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 */
461static void
462smbstat_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 */
488static void
489smbstat_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 */
512static void
513smbstat_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 */
549static void
550smbstat_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 */
584static void
585smbstat_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 */
600static void
601smbstat_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 */
615static smbstat_cpu_snapshot_t *
616smbstat_cpu_current_snapshot(void)
617{
618	return (smbstat_cpu_snapshots[smbstat_snapshot_idx]);
619}
620
621/*
622 * smbstat_cpu_previous_snapshot
623 */
624static smbstat_cpu_snapshot_t *
625smbstat_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 */
636static void
637smbstat_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 */
679static void
680smbstat_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 */
709static void
710smbstat_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
719static void
720smbstat_wrk_fini(void)
721{
722	smbstat_wrk_ksp = NULL;
723}
724
725/*
726 * smbstat_wrk_snapshot
727 */
728static void
729smbstat_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 */
756static void
757smbstat_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 */
772static smbstat_wrk_snapshot_t *
773smbstat_wrk_current_snapshot(void)
774{
775	return (&smbstat_wrk_snapshots[smbstat_snapshot_idx]);
776}
777
778/*
779 * smbstat_srv_init
780 */
781static void
782smbstat_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 */
793static void
794smbstat_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 */
804static void
805smbstat_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 */
825static void
826smbstat_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 */
858static void
859smbstat_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 */
875static void
876smbstat_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 */
927static void
928smbstat_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 */
1002static void
1003smbstat_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 */
1062static smbstat_srv_snapshot_t *
1063smbstat_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 */
1073static smbstat_srv_snapshot_t *
1074smbstat_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 */
1087static void
1088smbstat_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 */
1099static void
1100smbstat_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 */
1120static double
1121smbstat_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 */
1131static double
1132smbstat_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 */
1145static uint_t
1146smbstat_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 */
1163static void
1164smbstat_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 */
1192static void
1193smbstat_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 */
1205static int
1206smbstat_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 */
1222static void
1223smbstat_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 */
1248static double
1249smbstat_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
1292static void *
1293smbstat_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
1303static void
1304smbstat_free(void *ptr, size_t size)
1305{
1306	umem_free(ptr, size);
1307}
1308