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
main(int argc,char * argv[])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
smbstat_init(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
smbstat_fini(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
smbstat_kstat_snapshot(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
smbstat_kstat_process(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
smbstat_kstat_print(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
smbstat_print_counters(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
smbstat_print_throughput(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
smbstat_print_utilization(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
smbstat_print_requests(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
smbstat_cpu_init(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
smbstat_cpu_fini(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 *
smbstat_cpu_current_snapshot(void)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 *
smbstat_cpu_previous_snapshot(void)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
smbstat_cpu_snapshot(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
smbstat_cpu_process(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
smbstat_wrk_init(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
smbstat_wrk_fini(void)714 smbstat_wrk_fini(void)
715 {
716 smbstat_wrk_ksp = NULL;
717 }
718
719 /*
720 * smbstat_wrk_snapshot
721 */
722 static void
smbstat_wrk_snapshot(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
smbstat_wrk_process(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 *
smbstat_wrk_current_snapshot(void)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
smbstat_srv_init(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
smbstat_srv_fini(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
smbstat_srv_snapshot(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
smbstat_srv_process(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
smbstat_srv_process_counters(smbstat_srv_snapshot_t * curr)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
smbstat_srv_process_throughput(smbstat_srv_snapshot_t * curr,smbstat_srv_snapshot_t * prev)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
smbstat_srv_process_utilization(smbstat_srv_snapshot_t * curr,smbstat_srv_snapshot_t * prev)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
smbstat_srv_process_requests(smbstat_srv_snapshot_t * curr,smbstat_srv_snapshot_t * prev)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 *
smbstat_srv_current_snapshot(void)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 *
smbstat_srv_previous_snapshot(void)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
smbstat_usage(FILE * fd,int exit_code)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
smbstat_fail(int do_perror,char * message,...)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
smbstat_sub_64(uint64_t a,uint64_t b)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
smbstat_zero(double value)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
smbstat_strtoi(char const * val,char * errmsg)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
smbstat_termio_init(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
smbstat_snapshot_inc_idx(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
smbstat_req_cmp_name(const void * obj1,const void * obj2)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
smbstat_req_order(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
smbstat_hrtime_delta(hrtime_t old,hrtime_t new)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 *
smbstat_zalloc(size_t size)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
smbstat_free(void * ptr,size_t size)1298 smbstat_free(void *ptr, size_t size)
1299 {
1300 umem_free(ptr, size);
1301 }
1302