xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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 /* LINTLIBRARY */
23 /* PROTOLIB1 */
24 
25 /*
26  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
29  */
30 
31 /*
32  * nfsstat: Network File System statistics
33  *
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <kvm.h>
44 #include <kstat.h>
45 #include <sys/param.h>
46 #include <sys/types.h>
47 #include <sys/t_lock.h>
48 #include <sys/tiuser.h>
49 #include <sys/statvfs.h>
50 #include <sys/mntent.h>
51 #include <sys/mnttab.h>
52 #include <sys/sysmacros.h>
53 #include <sys/mkdev.h>
54 #include <rpc/types.h>
55 #include <rpc/xdr.h>
56 #include <rpc/auth.h>
57 #include <rpc/clnt.h>
58 #include <nfs/nfs.h>
59 #include <nfs/nfs_clnt.h>
60 #include <nfs/nfs_sec.h>
61 #include <inttypes.h>
62 #include <signal.h>
63 #include <time.h>
64 #include <sys/time.h>
65 #include <strings.h>
66 #include <ctype.h>
67 #include <locale.h>
68 
69 #include "statcommon.h"
70 
71 static kstat_ctl_t *kc = NULL;		/* libkstat cookie */
72 static kstat_t *rpc_clts_client_kstat, *rpc_clts_server_kstat;
73 static kstat_t *rpc_cots_client_kstat, *rpc_cots_server_kstat;
74 static kstat_t *rpc_rdma_client_kstat, *rpc_rdma_server_kstat;
75 static kstat_t *nfs_client_kstat, *nfs_server_v2_kstat, *nfs_server_v3_kstat;
76 static kstat_t *nfs4_client_kstat, *nfs_server_v4_kstat;
77 static kstat_t *rfsproccnt_v2_kstat, *rfsproccnt_v3_kstat, *rfsproccnt_v4_kstat;
78 static kstat_t *rfsreqcnt_v2_kstat, *rfsreqcnt_v3_kstat, *rfsreqcnt_v4_kstat;
79 static kstat_t *aclproccnt_v2_kstat, *aclproccnt_v3_kstat;
80 static kstat_t *aclreqcnt_v2_kstat, *aclreqcnt_v3_kstat;
81 static kstat_t *ksum_kstat;
82 
83 static void handle_sig(int);
84 static int getstats_rpc(void);
85 static int getstats_nfs(void);
86 static int getstats_rfsproc(int);
87 static int getstats_rfsreq(int);
88 static int getstats_aclproc(void);
89 static int getstats_aclreq(void);
90 static void putstats(void);
91 static void setup(void);
92 static void cr_print(int);
93 static void sr_print(int);
94 static void cn_print(int, int);
95 static void sn_print(int, int);
96 static void ca_print(int, int);
97 static void sa_print(int, int);
98 static void req_print(kstat_t *, kstat_t *, int, int, int);
99 static void req_print_v4(kstat_t *, kstat_t *, int, int);
100 static void stat_print(const char *, kstat_t *, kstat_t *, int, int);
101 static void nfsstat_kstat_sum(kstat_t *, kstat_t *, kstat_t *);
102 static void stats_timer(int);
103 static void safe_zalloc(void **, uint_t, int);
104 static int safe_strtoi(char const *, char *);
105 
106 
107 static void nfsstat_kstat_copy(kstat_t *, kstat_t *, int);
108 static kid_t safe_kstat_read(kstat_ctl_t *, kstat_t *, void *);
109 static kid_t safe_kstat_write(kstat_ctl_t *, kstat_t *, void *);
110 
111 static void usage(void);
112 static void mi_print(void);
113 static int ignore(char *);
114 static int interval;		/* interval between stats */
115 static int count;		/* number of iterations the stat is printed */
116 #define	MAX_COLUMNS	80
117 #define	MAX_PATHS	50	/* max paths that can be taken by -m */
118 
119 /*
120  * MI4_MIRRORMOUNT is canonically defined in nfs4_clnt.h, but we cannot
121  * include that file here.  Same with MI4_REFERRAL.
122  */
123 #define	MI4_MIRRORMOUNT 0x4000
124 #define	MI4_REFERRAL	0x8000
125 #define	NFS_V4		4
126 
127 static int req_width(kstat_t *, int);
128 static int stat_width(kstat_t *, int);
129 static char *path [MAX_PATHS] = {NULL};  /* array to store the multiple paths */
130 
131 /*
132  * Struct holds the previous kstat values so
133  * we can compute deltas when using the -i flag
134  */
135 typedef struct old_kstat
136 {
137 	kstat_t kst;
138 	int tot;
139 } old_kstat_t;
140 
141 static old_kstat_t old_rpc_clts_client_kstat, old_rpc_clts_server_kstat;
142 static old_kstat_t old_rpc_cots_client_kstat, old_rpc_cots_server_kstat;
143 static old_kstat_t old_rpc_rdma_client_kstat, old_rpc_rdma_server_kstat;
144 static old_kstat_t old_nfs_client_kstat, old_nfs_server_v2_kstat;
145 static old_kstat_t old_nfs_server_v3_kstat, old_ksum_kstat;
146 static old_kstat_t old_nfs4_client_kstat, old_nfs_server_v4_kstat;
147 static old_kstat_t old_rfsproccnt_v2_kstat, old_rfsproccnt_v3_kstat;
148 static old_kstat_t old_rfsproccnt_v4_kstat, old_rfsreqcnt_v2_kstat;
149 static old_kstat_t old_rfsreqcnt_v3_kstat, old_rfsreqcnt_v4_kstat;
150 static old_kstat_t old_aclproccnt_v2_kstat, old_aclproccnt_v3_kstat;
151 static old_kstat_t old_aclreqcnt_v2_kstat, old_aclreqcnt_v3_kstat;
152 
153 static uint_t timestamp_fmt = NODATE;
154 
155 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
156 #define	TEXT_DOMAIN "SYS_TEST"		/* Use this only if it isn't */
157 #endif
158 
159 int
160 main(int argc, char *argv[])
161 {
162 	int c, go_forever, j;
163 	int cflag = 0;		/* client stats */
164 	int sflag = 0;		/* server stats */
165 	int nflag = 0;		/* nfs stats */
166 	int rflag = 0;		/* rpc stats */
167 	int mflag = 0;		/* mount table stats */
168 	int aflag = 0;		/* print acl statistics */
169 	int vflag = 0;		/* version specified, 0 specifies all */
170 	int zflag = 0;		/* zero stats after printing */
171 	char *split_line = "*******************************************"
172 	    "*************************************";
173 
174 	interval = 0;
175 	count = 0;
176 	go_forever = 0;
177 
178 	(void) setlocale(LC_ALL, "");
179 	(void) textdomain(TEXT_DOMAIN);
180 
181 	while ((c = getopt(argc, argv, "cnrsmzav:T:")) != EOF) {
182 		switch (c) {
183 		case 'c':
184 			cflag++;
185 			break;
186 		case 'n':
187 			nflag++;
188 			break;
189 		case 'r':
190 			rflag++;
191 			break;
192 		case 's':
193 			sflag++;
194 			break;
195 		case 'm':
196 			mflag++;
197 			break;
198 		case 'z':
199 			if (geteuid())
200 				fail(0, "Must be root for z flag\n");
201 			zflag++;
202 			break;
203 		case 'a':
204 			aflag++;
205 			break;
206 		case 'v':
207 			vflag = atoi(optarg);
208 			if ((vflag < 2) || (vflag > 4))
209 				fail(0, "Invalid version number\n");
210 			break;
211 		case 'T':
212 			if (optarg) {
213 				if (*optarg == 'u')
214 					timestamp_fmt = UDATE;
215 				else if (*optarg == 'd')
216 					timestamp_fmt = DDATE;
217 				else
218 					usage();
219 			} else {
220 				usage();
221 			}
222 			break;
223 		case '?':
224 		default:
225 			usage();
226 		}
227 	}
228 
229 	if (((argc - optind) > 0) && !mflag) {
230 
231 		interval = safe_strtoi(argv[optind], "invalid interval");
232 		if (interval < 1)
233 			fail(0, "invalid interval\n");
234 		optind++;
235 
236 		if ((argc - optind) > 0) {
237 			count = safe_strtoi(argv[optind], "invalid count");
238 			if (count <= 0)
239 				fail(0, "invalid count\n");
240 		}
241 		optind++;
242 
243 		if ((argc - optind) > 0)
244 			usage();
245 
246 		/*
247 		 * no count number was set, so we will loop infinitely
248 		 * at interval specified
249 		 */
250 		if (!count)
251 			go_forever = 1;
252 		stats_timer(interval);
253 	} else if (mflag) {
254 
255 		if (cflag || rflag || sflag || zflag || nflag || aflag || vflag)
256 			fail(0,
257 			    "The -m flag may not be used with any other flags");
258 
259 		for (j = 0; (argc - optind > 0) && (j < (MAX_PATHS - 1)); j++) {
260 			path[j] =  argv[optind];
261 			if (*path[j] != '/')
262 				fail(0, "Please fully qualify your pathname "
263 				    "with a leading '/'");
264 			optind++;
265 		}
266 		path[j] = NULL;
267 		if (argc - optind > 0)
268 			fprintf(stderr, "Only the first 50 paths "
269 			    "will be searched for\n");
270 	}
271 
272 	setup();
273 
274 	do {
275 		if (mflag) {
276 			mi_print();
277 		} else {
278 			if (timestamp_fmt != NODATE)
279 				print_timestamp(timestamp_fmt);
280 
281 			if (sflag &&
282 			    (rpc_clts_server_kstat == NULL ||
283 			    nfs_server_v4_kstat == NULL)) {
284 				fprintf(stderr,
285 				    "nfsstat: kernel is not configured with "
286 				    "the server nfs and rpc code.\n");
287 			}
288 
289 			/* if s and nothing else, all 3 prints are called */
290 			if (sflag || (!sflag && !cflag)) {
291 				if (rflag || (!rflag && !nflag && !aflag))
292 					sr_print(zflag);
293 				if (nflag || (!rflag && !nflag && !aflag))
294 					sn_print(zflag, vflag);
295 				if (aflag || (!rflag && !nflag && !aflag))
296 					sa_print(zflag, vflag);
297 			}
298 			if (cflag &&
299 			    (rpc_clts_client_kstat == NULL ||
300 			    nfs_client_kstat == NULL)) {
301 				fprintf(stderr,
302 				    "nfsstat: kernel is not configured with"
303 				    " the client nfs and rpc code.\n");
304 			}
305 			if (cflag || (!sflag && !cflag)) {
306 				if (rflag || (!rflag && !nflag && !aflag))
307 					cr_print(zflag);
308 				if (nflag || (!rflag && !nflag && !aflag))
309 					cn_print(zflag, vflag);
310 				if (aflag || (!rflag && !nflag && !aflag))
311 					ca_print(zflag, vflag);
312 			}
313 		}
314 
315 		if (zflag)
316 			putstats();
317 		if (interval)
318 			printf("%s\n", split_line);
319 
320 		if (interval > 0)
321 			(void) pause();
322 	} while ((--count > 0) || go_forever);
323 
324 	kstat_close(kc);
325 	free(ksum_kstat);
326 	return (0);
327 }
328 
329 
330 static int
331 getstats_rpc(void)
332 {
333 	int field_width = 0;
334 
335 	if (rpc_clts_client_kstat != NULL) {
336 		safe_kstat_read(kc, rpc_clts_client_kstat, NULL);
337 		field_width = stat_width(rpc_clts_client_kstat, field_width);
338 	}
339 
340 	if (rpc_cots_client_kstat != NULL) {
341 		safe_kstat_read(kc, rpc_cots_client_kstat, NULL);
342 		field_width = stat_width(rpc_cots_client_kstat, field_width);
343 	}
344 
345 	if (rpc_rdma_client_kstat != NULL) {
346 		safe_kstat_read(kc, rpc_rdma_client_kstat, NULL);
347 		field_width = stat_width(rpc_rdma_client_kstat, field_width);
348 	}
349 
350 	if (rpc_clts_server_kstat != NULL) {
351 		safe_kstat_read(kc, rpc_clts_server_kstat, NULL);
352 		field_width =  stat_width(rpc_clts_server_kstat, field_width);
353 	}
354 	if (rpc_cots_server_kstat != NULL) {
355 		safe_kstat_read(kc, rpc_cots_server_kstat, NULL);
356 		field_width = stat_width(rpc_cots_server_kstat, field_width);
357 	}
358 	if (rpc_rdma_server_kstat != NULL) {
359 		safe_kstat_read(kc, rpc_rdma_server_kstat, NULL);
360 		field_width = stat_width(rpc_rdma_server_kstat, field_width);
361 	}
362 	return (field_width);
363 }
364 
365 static int
366 getstats_nfs(void)
367 {
368 	int field_width = 0;
369 
370 	if (nfs_client_kstat != NULL) {
371 		safe_kstat_read(kc, nfs_client_kstat, NULL);
372 		field_width = stat_width(nfs_client_kstat, field_width);
373 	}
374 	if (nfs4_client_kstat != NULL) {
375 		safe_kstat_read(kc, nfs4_client_kstat, NULL);
376 		field_width = stat_width(nfs4_client_kstat, field_width);
377 	}
378 	if (nfs_server_v2_kstat != NULL) {
379 		safe_kstat_read(kc, nfs_server_v2_kstat, NULL);
380 		field_width = stat_width(nfs_server_v2_kstat, field_width);
381 	}
382 	if (nfs_server_v3_kstat != NULL) {
383 		safe_kstat_read(kc, nfs_server_v3_kstat, NULL);
384 		field_width = stat_width(nfs_server_v3_kstat, field_width);
385 	}
386 	if (nfs_server_v4_kstat != NULL) {
387 		safe_kstat_read(kc, nfs_server_v4_kstat, NULL);
388 		field_width = stat_width(nfs_server_v4_kstat, field_width);
389 	}
390 	return (field_width);
391 }
392 
393 static int
394 getstats_rfsproc(int ver)
395 {
396 	int field_width = 0;
397 
398 	if ((ver == 2) && (rfsproccnt_v2_kstat != NULL)) {
399 		safe_kstat_read(kc, rfsproccnt_v2_kstat, NULL);
400 		field_width = req_width(rfsproccnt_v2_kstat, field_width);
401 	}
402 	if ((ver == 3) && (rfsproccnt_v3_kstat != NULL)) {
403 		safe_kstat_read(kc, rfsproccnt_v3_kstat, NULL);
404 		field_width = req_width(rfsproccnt_v3_kstat, field_width);
405 	}
406 	if ((ver == 4) && (rfsproccnt_v4_kstat != NULL)) {
407 		safe_kstat_read(kc, rfsproccnt_v4_kstat, NULL);
408 		field_width = req_width(rfsproccnt_v4_kstat, field_width);
409 	}
410 	return (field_width);
411 }
412 
413 static int
414 getstats_rfsreq(int ver)
415 {
416 	int field_width = 0;
417 	if ((ver == 2) && (rfsreqcnt_v2_kstat != NULL)) {
418 		safe_kstat_read(kc, rfsreqcnt_v2_kstat, NULL);
419 		field_width = req_width(rfsreqcnt_v2_kstat, field_width);
420 	}
421 	if ((ver == 3) && (rfsreqcnt_v3_kstat != NULL)) {
422 		safe_kstat_read(kc, rfsreqcnt_v3_kstat, NULL);
423 		field_width = req_width(rfsreqcnt_v3_kstat,  field_width);
424 	}
425 	if ((ver == 4) && (rfsreqcnt_v4_kstat != NULL)) {
426 		safe_kstat_read(kc, rfsreqcnt_v4_kstat, NULL);
427 		field_width = req_width(rfsreqcnt_v4_kstat, field_width);
428 	}
429 	return (field_width);
430 }
431 
432 static int
433 getstats_aclproc(void)
434 {
435 	int field_width = 0;
436 	if (aclproccnt_v2_kstat != NULL) {
437 		safe_kstat_read(kc, aclproccnt_v2_kstat, NULL);
438 		field_width = req_width(aclproccnt_v2_kstat, field_width);
439 	}
440 	if (aclproccnt_v3_kstat != NULL) {
441 		safe_kstat_read(kc, aclproccnt_v3_kstat, NULL);
442 		field_width = req_width(aclproccnt_v3_kstat, field_width);
443 	}
444 	return (field_width);
445 }
446 
447 static int
448 getstats_aclreq(void)
449 {
450 	int field_width = 0;
451 	if (aclreqcnt_v2_kstat != NULL) {
452 		safe_kstat_read(kc, aclreqcnt_v2_kstat, NULL);
453 		field_width = req_width(aclreqcnt_v2_kstat, field_width);
454 	}
455 	if (aclreqcnt_v3_kstat != NULL) {
456 		safe_kstat_read(kc, aclreqcnt_v3_kstat, NULL);
457 		field_width = req_width(aclreqcnt_v3_kstat, field_width);
458 	}
459 	return (field_width);
460 }
461 
462 static void
463 putstats(void)
464 {
465 	if (rpc_clts_client_kstat != NULL)
466 		safe_kstat_write(kc, rpc_clts_client_kstat, NULL);
467 	if (rpc_cots_client_kstat != NULL)
468 		safe_kstat_write(kc, rpc_cots_client_kstat, NULL);
469 	if (rpc_rdma_client_kstat != NULL)
470 		safe_kstat_write(kc, rpc_rdma_client_kstat, NULL);
471 	if (nfs_client_kstat != NULL)
472 		safe_kstat_write(kc, nfs_client_kstat, NULL);
473 	if (nfs4_client_kstat != NULL)
474 		safe_kstat_write(kc, nfs4_client_kstat, NULL);
475 	if (rpc_clts_server_kstat != NULL)
476 		safe_kstat_write(kc, rpc_clts_server_kstat, NULL);
477 	if (rpc_cots_server_kstat != NULL)
478 		safe_kstat_write(kc, rpc_cots_server_kstat, NULL);
479 	if (rpc_rdma_server_kstat != NULL)
480 		safe_kstat_write(kc, rpc_rdma_server_kstat, NULL);
481 	if (nfs_server_v2_kstat != NULL)
482 		safe_kstat_write(kc, nfs_server_v2_kstat, NULL);
483 	if (nfs_server_v3_kstat != NULL)
484 		safe_kstat_write(kc, nfs_server_v3_kstat, NULL);
485 	if (nfs_server_v4_kstat != NULL)
486 		safe_kstat_write(kc, nfs_server_v4_kstat, NULL);
487 	if (rfsproccnt_v2_kstat != NULL)
488 		safe_kstat_write(kc, rfsproccnt_v2_kstat, NULL);
489 	if (rfsproccnt_v3_kstat != NULL)
490 		safe_kstat_write(kc, rfsproccnt_v3_kstat, NULL);
491 	if (rfsproccnt_v4_kstat != NULL)
492 		safe_kstat_write(kc, rfsproccnt_v4_kstat, NULL);
493 	if (rfsreqcnt_v2_kstat != NULL)
494 		safe_kstat_write(kc, rfsreqcnt_v2_kstat, NULL);
495 	if (rfsreqcnt_v3_kstat != NULL)
496 		safe_kstat_write(kc, rfsreqcnt_v3_kstat, NULL);
497 	if (rfsreqcnt_v4_kstat != NULL)
498 		safe_kstat_write(kc, rfsreqcnt_v4_kstat, NULL);
499 	if (aclproccnt_v2_kstat != NULL)
500 		safe_kstat_write(kc, aclproccnt_v2_kstat, NULL);
501 	if (aclproccnt_v3_kstat != NULL)
502 		safe_kstat_write(kc, aclproccnt_v3_kstat, NULL);
503 	if (aclreqcnt_v2_kstat != NULL)
504 		safe_kstat_write(kc, aclreqcnt_v2_kstat, NULL);
505 	if (aclreqcnt_v3_kstat != NULL)
506 		safe_kstat_write(kc, aclreqcnt_v3_kstat, NULL);
507 }
508 
509 static void
510 setup(void)
511 {
512 	if ((kc = kstat_open()) == NULL)
513 		fail(1, "kstat_open(): can't open /dev/kstat");
514 
515 	/* alloc space for our temporary kstat */
516 	safe_zalloc((void **)&ksum_kstat, sizeof (kstat_t), 0);
517 	rpc_clts_client_kstat = kstat_lookup(kc, "unix", 0, "rpc_clts_client");
518 	rpc_clts_server_kstat = kstat_lookup(kc, "unix", 0, "rpc_clts_server");
519 	rpc_cots_client_kstat = kstat_lookup(kc, "unix", 0, "rpc_cots_client");
520 	rpc_cots_server_kstat = kstat_lookup(kc, "unix", 0, "rpc_cots_server");
521 	rpc_rdma_client_kstat = kstat_lookup(kc, "unix", 0, "rpc_rdma_client");
522 	rpc_rdma_server_kstat = kstat_lookup(kc, "unix", 0, "rpc_rdma_server");
523 	nfs_client_kstat = kstat_lookup(kc, "nfs", 0, "nfs_client");
524 	nfs4_client_kstat = kstat_lookup(kc, "nfs", 0, "nfs4_client");
525 	nfs_server_v2_kstat = kstat_lookup(kc, "nfs", 2, "nfs_server");
526 	nfs_server_v3_kstat = kstat_lookup(kc, "nfs", 3, "nfs_server");
527 	nfs_server_v4_kstat = kstat_lookup(kc, "nfs", 4, "nfs_server");
528 	rfsproccnt_v2_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v2");
529 	rfsproccnt_v3_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v3");
530 	rfsproccnt_v4_kstat = kstat_lookup(kc, "nfs", 0, "rfsproccnt_v4");
531 	rfsreqcnt_v2_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v2");
532 	rfsreqcnt_v3_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v3");
533 	rfsreqcnt_v4_kstat = kstat_lookup(kc, "nfs", 0, "rfsreqcnt_v4");
534 	aclproccnt_v2_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclproccnt_v2");
535 	aclproccnt_v3_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclproccnt_v3");
536 	aclreqcnt_v2_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclreqcnt_v2");
537 	aclreqcnt_v3_kstat = kstat_lookup(kc, "nfs_acl", 0, "aclreqcnt_v3");
538 	if (rpc_clts_client_kstat == NULL && rpc_cots_server_kstat == NULL &&
539 	    rfsproccnt_v2_kstat == NULL && rfsreqcnt_v3_kstat == NULL)
540 		fail(0, "Multiple kstat lookups failed."
541 		    "Your kernel module may not be loaded\n");
542 }
543 
544 static int
545 req_width(kstat_t *req, int field_width)
546 {
547 	int i, nreq, per, len;
548 	char fixlen[128];
549 	kstat_named_t *knp;
550 	uint64_t tot;
551 
552 	tot = 0;
553 	knp = KSTAT_NAMED_PTR(req);
554 	for (i = 0; i < req->ks_ndata; i++)
555 		tot += knp[i].value.ui64;
556 
557 	knp = kstat_data_lookup(req, "null");
558 	nreq = req->ks_ndata - (knp - KSTAT_NAMED_PTR(req));
559 
560 	for (i = 0; i < nreq; i++) {
561 		len = strlen(knp[i].name) + 1;
562 		if (field_width < len)
563 			field_width = len;
564 		if (tot)
565 			per = (int)(knp[i].value.ui64 * 100 / tot);
566 		else
567 			per = 0;
568 		(void) sprintf(fixlen, "%" PRIu64 " %d%%",
569 		    knp[i].value.ui64, per);
570 		len = strlen(fixlen) + 1;
571 		if (field_width < len)
572 			field_width = len;
573 	}
574 	return (field_width);
575 }
576 
577 static int
578 stat_width(kstat_t *req, int field_width)
579 {
580 	int i, nreq, len;
581 	char fixlen[128];
582 	kstat_named_t *knp;
583 
584 	knp = KSTAT_NAMED_PTR(req);
585 	nreq = req->ks_ndata;
586 
587 	for (i = 0; i < nreq; i++) {
588 		len = strlen(knp[i].name) + 1;
589 		if (field_width < len)
590 			field_width = len;
591 		(void) sprintf(fixlen, "%" PRIu64, knp[i].value.ui64);
592 		len = strlen(fixlen) + 1;
593 		if (field_width < len)
594 			field_width = len;
595 	}
596 	return (field_width);
597 }
598 
599 static void
600 cr_print(int zflag)
601 {
602 	int field_width;
603 
604 	field_width = getstats_rpc();
605 	if (field_width == 0)
606 		return;
607 
608 	stat_print("\nClient rpc:\nConnection oriented:",
609 	    rpc_cots_client_kstat,
610 	    &old_rpc_cots_client_kstat.kst, field_width, zflag);
611 	stat_print("Connectionless:", rpc_clts_client_kstat,
612 	    &old_rpc_clts_client_kstat.kst, field_width, zflag);
613 	stat_print("RDMA based:", rpc_rdma_client_kstat,
614 	    &old_rpc_rdma_client_kstat.kst, field_width, zflag);
615 }
616 
617 static void
618 sr_print(int zflag)
619 {
620 	int field_width;
621 
622 	field_width = getstats_rpc();
623 	if (field_width == 0)
624 		return;
625 
626 	stat_print("\nServer rpc:\nConnection oriented:", rpc_cots_server_kstat,
627 	    &old_rpc_cots_server_kstat.kst, field_width, zflag);
628 	stat_print("Connectionless:", rpc_clts_server_kstat,
629 	    &old_rpc_clts_server_kstat.kst, field_width, zflag);
630 	stat_print("RDMA based:", rpc_rdma_server_kstat,
631 	    &old_rpc_rdma_server_kstat.kst, field_width, zflag);
632 }
633 
634 static void
635 cn_print(int zflag, int vflag)
636 {
637 	int field_width;
638 
639 	field_width = getstats_nfs();
640 	if (field_width == 0)
641 		return;
642 
643 	if (vflag == 0) {
644 		nfsstat_kstat_sum(nfs_client_kstat, nfs4_client_kstat,
645 		    ksum_kstat);
646 		stat_print("\nClient nfs:", ksum_kstat, &old_ksum_kstat.kst,
647 		    field_width, zflag);
648 	}
649 
650 	if (vflag == 2 || vflag == 3) {
651 		stat_print("\nClient nfs:", nfs_client_kstat,
652 		    &old_nfs_client_kstat.kst, field_width, zflag);
653 	}
654 
655 	if (vflag == 4) {
656 		stat_print("\nClient nfs:", nfs4_client_kstat,
657 		    &old_nfs4_client_kstat.kst, field_width, zflag);
658 	}
659 
660 	if (vflag == 2 || vflag == 0) {
661 		field_width = getstats_rfsreq(2);
662 		req_print(rfsreqcnt_v2_kstat, &old_rfsreqcnt_v2_kstat.kst,
663 		    2, field_width, zflag);
664 	}
665 
666 	if (vflag == 3 || vflag == 0) {
667 		field_width = getstats_rfsreq(3);
668 		req_print(rfsreqcnt_v3_kstat, &old_rfsreqcnt_v3_kstat.kst, 3,
669 		    field_width, zflag);
670 	}
671 
672 	if (vflag == 4 || vflag == 0) {
673 		field_width = getstats_rfsreq(4);
674 		req_print_v4(rfsreqcnt_v4_kstat, &old_rfsreqcnt_v4_kstat.kst,
675 		    field_width, zflag);
676 	}
677 }
678 
679 static void
680 sn_print(int zflag, int vflag)
681 {
682 	int  field_width;
683 
684 	field_width = getstats_nfs();
685 	if (field_width == 0)
686 		return;
687 
688 	if (vflag == 2 || vflag == 0) {
689 		stat_print("\nServer NFSv2:", nfs_server_v2_kstat,
690 		    &old_nfs_server_v2_kstat.kst, field_width, zflag);
691 	}
692 
693 	if (vflag == 3 || vflag == 0) {
694 		stat_print("\nServer NFSv3:", nfs_server_v3_kstat,
695 		    &old_nfs_server_v3_kstat.kst, field_width, zflag);
696 	}
697 
698 	if (vflag == 4 || vflag == 0) {
699 		stat_print("\nServer NFSv4:", nfs_server_v4_kstat,
700 		    &old_nfs_server_v4_kstat.kst, field_width, zflag);
701 	}
702 
703 	if (vflag == 2 || vflag == 0) {
704 		field_width = getstats_rfsproc(2);
705 		req_print(rfsproccnt_v2_kstat, &old_rfsproccnt_v2_kstat.kst,
706 		    2, field_width, zflag);
707 	}
708 
709 	if (vflag == 3 || vflag == 0) {
710 		field_width = getstats_rfsproc(3);
711 		req_print(rfsproccnt_v3_kstat, &old_rfsproccnt_v3_kstat.kst,
712 		    3, field_width, zflag);
713 	}
714 
715 	if (vflag == 4 || vflag == 0) {
716 		field_width = getstats_rfsproc(4);
717 		req_print_v4(rfsproccnt_v4_kstat, &old_rfsproccnt_v4_kstat.kst,
718 		    field_width, zflag);
719 	}
720 }
721 
722 static void
723 ca_print(int zflag, int vflag)
724 {
725 	int  field_width;
726 
727 	field_width = getstats_aclreq();
728 	if (field_width == 0)
729 		return;
730 
731 	printf("\nClient nfs_acl:\n");
732 
733 	if (vflag == 2 || vflag == 0) {
734 		req_print(aclreqcnt_v2_kstat, &old_aclreqcnt_v2_kstat.kst, 2,
735 		    field_width, zflag);
736 	}
737 
738 	if (vflag == 3 || vflag == 0) {
739 		req_print(aclreqcnt_v3_kstat, &old_aclreqcnt_v3_kstat.kst,
740 		    3, field_width, zflag);
741 	}
742 }
743 
744 static void
745 sa_print(int zflag, int vflag)
746 {
747 	int  field_width;
748 
749 	field_width = getstats_aclproc();
750 	if (field_width == 0)
751 		return;
752 
753 	printf("\nServer nfs_acl:\n");
754 
755 	if (vflag == 2 || vflag == 0) {
756 		req_print(aclproccnt_v2_kstat, &old_aclproccnt_v2_kstat.kst,
757 		    2, field_width, zflag);
758 	}
759 
760 	if (vflag == 3 || vflag == 0) {
761 		req_print(aclproccnt_v3_kstat, &old_aclproccnt_v3_kstat.kst,
762 		    3, field_width, zflag);
763 	}
764 }
765 
766 #define	MIN(a, b)	((a) < (b) ? (a) : (b))
767 
768 static void
769 req_print(kstat_t *req, kstat_t *req_old, int ver, int field_width,
770     int zflag)
771 {
772 	int i, j, nreq, per, ncolumns;
773 	uint64_t tot, old_tot;
774 	char fixlen[128];
775 	kstat_named_t *knp;
776 	kstat_named_t *kptr;
777 	kstat_named_t *knp_old;
778 
779 	if (req == NULL)
780 		return;
781 
782 	if (field_width == 0)
783 		return;
784 
785 	ncolumns = (MAX_COLUMNS -1)/field_width;
786 	knp = kstat_data_lookup(req, "null");
787 	knp_old = KSTAT_NAMED_PTR(req_old);
788 
789 	kptr = KSTAT_NAMED_PTR(req);
790 	nreq = req->ks_ndata - (knp - KSTAT_NAMED_PTR(req));
791 
792 	tot = 0;
793 	old_tot = 0;
794 
795 	if (knp_old == NULL) {
796 		old_tot = 0;
797 	}
798 
799 	for (i = 0; i < req->ks_ndata; i++)
800 		tot += kptr[i].value.ui64;
801 
802 	if (interval && knp_old != NULL) {
803 		for (i = 0; i < req_old->ks_ndata; i++)
804 			old_tot += knp_old[i].value.ui64;
805 		tot -= old_tot;
806 	}
807 
808 	printf("Version %d: (%" PRIu64 " calls)\n", ver, tot);
809 
810 	for (i = 0; i < nreq; i += ncolumns) {
811 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
812 			printf("%-*s", field_width, knp[j].name);
813 		}
814 		printf("\n");
815 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
816 			if (tot && interval && knp_old != NULL)
817 				per = (int)((knp[j].value.ui64 -
818 				    knp_old[j].value.ui64) * 100 / tot);
819 			else if (tot)
820 				per = (int)(knp[j].value.ui64 * 100 / tot);
821 			else
822 				per = 0;
823 			(void) sprintf(fixlen, "%" PRIu64 " %d%% ",
824 			    ((interval && knp_old != NULL) ?
825 			    (knp[j].value.ui64 - knp_old[j].value.ui64)
826 			    : knp[j].value.ui64), per);
827 			printf("%-*s", field_width, fixlen);
828 		}
829 		printf("\n");
830 	}
831 	if (zflag) {
832 		for (i = 0; i < req->ks_ndata; i++)
833 			knp[i].value.ui64 = 0;
834 	}
835 	if (knp_old != NULL)
836 		nfsstat_kstat_copy(req, req_old, 1);
837 	else
838 		nfsstat_kstat_copy(req, req_old, 0);
839 }
840 
841 /*
842  * Separate version of the req_print() to deal with V4 and its use of
843  * procedures and operations.  It looks odd to have the counts for
844  * both of those lumped into the same set of statistics so this
845  * function (copy of req_print() does the separation and titles).
846  */
847 
848 #define	COUNT	2
849 
850 static void
851 req_print_v4(kstat_t *req, kstat_t *req_old, int field_width, int zflag)
852 {
853 	int i, j, nreq, per, ncolumns;
854 	uint64_t tot, tot_ops, old_tot, old_tot_ops;
855 	char fixlen[128];
856 	kstat_named_t *kptr;
857 	kstat_named_t *knp;
858 	kstat_named_t *kptr_old;
859 
860 	if (req == NULL)
861 		return;
862 
863 	if (field_width == 0)
864 		return;
865 
866 	ncolumns = (MAX_COLUMNS)/field_width;
867 	kptr = KSTAT_NAMED_PTR(req);
868 	kptr_old = KSTAT_NAMED_PTR(req_old);
869 
870 	if (kptr_old == NULL) {
871 		old_tot_ops = 0;
872 		old_tot = 0;
873 	} else {
874 		old_tot =  kptr_old[0].value.ui64 + kptr_old[1].value.ui64;
875 		for (i = 2, old_tot_ops = 0; i < req_old->ks_ndata; i++)
876 			old_tot_ops += kptr_old[i].value.ui64;
877 	}
878 
879 	/* Count the number of operations sent */
880 	for (i = 2, tot_ops = 0; i < req->ks_ndata; i++)
881 		tot_ops += kptr[i].value.ui64;
882 	/* For v4 NULL/COMPOUND are the only procedures */
883 	tot = kptr[0].value.ui64 + kptr[1].value.ui64;
884 
885 	if (interval) {
886 		tot -= old_tot;
887 		tot_ops -= old_tot_ops;
888 	}
889 
890 	printf("Version 4: (%" PRIu64 " calls)\n", tot);
891 
892 	knp = kstat_data_lookup(req, "null");
893 	nreq = req->ks_ndata - (knp - KSTAT_NAMED_PTR(req));
894 
895 	for (i = 0; i < COUNT; i += ncolumns) {
896 		for (j = i; j < MIN(i + ncolumns, 2); j++) {
897 			printf("%-*s", field_width, knp[j].name);
898 		}
899 		printf("\n");
900 		for (j = i; j < MIN(i + ncolumns, 2); j++) {
901 			if (tot && interval && kptr_old != NULL)
902 				per = (int)((knp[j].value.ui64 -
903 				    kptr_old[j].value.ui64) * 100 / tot);
904 			else if (tot)
905 				per = (int)(knp[j].value.ui64 * 100 / tot);
906 			else
907 				per = 0;
908 			(void) sprintf(fixlen, "%" PRIu64 " %d%% ",
909 			    ((interval && kptr_old != NULL) ?
910 			    (knp[j].value.ui64 - kptr_old[j].value.ui64)
911 			    : knp[j].value.ui64), per);
912 			printf("%-*s", field_width, fixlen);
913 		}
914 		printf("\n");
915 	}
916 
917 	printf("Version 4: (%" PRIu64 " operations)\n", tot_ops);
918 	for (i = 2; i < nreq; i += ncolumns) {
919 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
920 			printf("%-*s", field_width, knp[j].name);
921 		}
922 		printf("\n");
923 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
924 			if (tot_ops && interval && kptr_old != NULL)
925 				per = (int)((knp[j].value.ui64 -
926 				    kptr_old[j].value.ui64) * 100 / tot_ops);
927 			else if (tot_ops)
928 				per = (int)(knp[j].value.ui64 * 100 / tot_ops);
929 			else
930 				per = 0;
931 			(void) sprintf(fixlen, "%" PRIu64 " %d%% ",
932 			    ((interval && kptr_old != NULL) ?
933 			    (knp[j].value.ui64 - kptr_old[j].value.ui64)
934 			    : knp[j].value.ui64), per);
935 			printf("%-*s", field_width, fixlen);
936 		}
937 		printf("\n");
938 	}
939 	if (zflag) {
940 		for (i = 0; i < req->ks_ndata; i++)
941 			kptr[i].value.ui64 = 0;
942 	}
943 	if (kptr_old != NULL)
944 		nfsstat_kstat_copy(req, req_old, 1);
945 	else
946 		nfsstat_kstat_copy(req, req_old, 0);
947 }
948 
949 static void
950 stat_print(const char *title_string, kstat_t *req, kstat_t  *req_old,
951     int field_width, int zflag)
952 {
953 	int i, j, nreq, ncolumns;
954 	char fixlen[128];
955 	kstat_named_t *knp;
956 	kstat_named_t *knp_old;
957 
958 	if (req == NULL)
959 		return;
960 
961 	if (field_width == 0)
962 		return;
963 
964 	printf("%s\n", title_string);
965 	ncolumns = (MAX_COLUMNS -1)/field_width;
966 
967 	/* MEANS knp =  (kstat_named_t *)req->ks_data */
968 	knp = KSTAT_NAMED_PTR(req);
969 	nreq = req->ks_ndata;
970 	knp_old = KSTAT_NAMED_PTR(req_old);
971 
972 	for (i = 0; i < nreq; i += ncolumns) {
973 		/* prints out the titles of the columns */
974 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
975 			printf("%-*s", field_width, knp[j].name);
976 		}
977 		printf("\n");
978 		/* prints out the stat numbers */
979 		for (j = i; j < MIN(i + ncolumns, nreq); j++) {
980 			(void) sprintf(fixlen, "%" PRIu64 " ",
981 			    (interval && knp_old != NULL) ?
982 			    (knp[j].value.ui64 - knp_old[j].value.ui64)
983 			    : knp[j].value.ui64);
984 			printf("%-*s", field_width, fixlen);
985 		}
986 		printf("\n");
987 
988 	}
989 	if (zflag) {
990 		for (i = 0; i < req->ks_ndata; i++)
991 			knp[i].value.ui64 = 0;
992 	}
993 
994 	if (knp_old != NULL)
995 		nfsstat_kstat_copy(req, req_old, 1);
996 	else
997 		nfsstat_kstat_copy(req, req_old, 0);
998 }
999 
1000 static void
1001 nfsstat_kstat_sum(kstat_t *kstat1, kstat_t *kstat2, kstat_t *sum)
1002 {
1003 	int i;
1004 	kstat_named_t *knp1, *knp2, *knpsum;
1005 	if (kstat1 == NULL || kstat2 == NULL)
1006 		return;
1007 
1008 	knp1 = KSTAT_NAMED_PTR(kstat1);
1009 	knp2 = KSTAT_NAMED_PTR(kstat2);
1010 	if (sum->ks_data == NULL)
1011 		nfsstat_kstat_copy(kstat1, sum, 0);
1012 	knpsum = KSTAT_NAMED_PTR(sum);
1013 
1014 	for (i = 0; i < (kstat1->ks_ndata); i++)
1015 		knpsum[i].value.ui64 =  knp1[i].value.ui64 + knp2[i].value.ui64;
1016 }
1017 
1018 /*
1019  * my_dir and my_path could be pointers
1020  */
1021 struct myrec {
1022 	ulong_t my_fsid;
1023 	char my_dir[MAXPATHLEN];
1024 	char *my_path;
1025 	char *ig_path;
1026 	struct myrec *next;
1027 };
1028 
1029 /*
1030  * Print the mount table info
1031  */
1032 static void
1033 mi_print(void)
1034 {
1035 	FILE *mt;
1036 	struct extmnttab m;
1037 	struct myrec *list, *mrp, *pmrp;
1038 	char *flavor;
1039 	int ignored = 0;
1040 	seconfig_t nfs_sec;
1041 	kstat_t *ksp;
1042 	struct mntinfo_kstat mik;
1043 	int transport_flag = 0;
1044 	int path_count;
1045 	int found;
1046 	char *timer_name[] = {
1047 		"Lookups",
1048 		"Reads",
1049 		"Writes",
1050 		"All"
1051 	};
1052 
1053 	mt = fopen(MNTTAB, "r");
1054 	if (mt == NULL) {
1055 		perror(MNTTAB);
1056 		exit(0);
1057 	}
1058 
1059 	list = NULL;
1060 	resetmnttab(mt);
1061 
1062 	while (getextmntent(mt, &m, sizeof (struct extmnttab)) == 0) {
1063 		/* ignore non "nfs" and save the "ignore" entries */
1064 		if (strcmp(m.mnt_fstype, MNTTYPE_NFS) != 0)
1065 			continue;
1066 		/*
1067 		 * Check to see here if user gave a path(s) to
1068 		 * only show the mount point they wanted
1069 		 * Iterate through the list of paths the user gave and see
1070 		 * if any of them match our current nfs mount
1071 		 */
1072 		if (path[0] != NULL) {
1073 			found = 0;
1074 			for (path_count = 0; path[path_count] != NULL;
1075 			    path_count++) {
1076 				if (strcmp(path[path_count], m.mnt_mountp)
1077 				    == 0) {
1078 					found = 1;
1079 					break;
1080 				}
1081 			}
1082 			if (!found)
1083 				continue;
1084 		}
1085 
1086 		if ((mrp = malloc(sizeof (struct myrec))) == 0) {
1087 			fprintf(stderr, "nfsstat: not enough memory\n");
1088 			exit(1);
1089 		}
1090 		mrp->my_fsid = makedev(m.mnt_major, m.mnt_minor);
1091 		if (ignore(m.mnt_mntopts)) {
1092 			/*
1093 			 * ignored entries cannot be ignored for this
1094 			 * option. We have to display the info for this
1095 			 * nfs mount. The ignore is an indication
1096 			 * that the actual mount point is different and
1097 			 * something is in between the nfs mount.
1098 			 * So save the mount point now
1099 			 */
1100 			if ((mrp->ig_path = malloc(
1101 			    strlen(m.mnt_mountp) + 1)) == 0) {
1102 				fprintf(stderr, "nfsstat: not enough memory\n");
1103 				exit(1);
1104 			}
1105 			(void) strcpy(mrp->ig_path, m.mnt_mountp);
1106 			ignored++;
1107 		} else {
1108 			mrp->ig_path = 0;
1109 			(void) strcpy(mrp->my_dir, m.mnt_mountp);
1110 		}
1111 		if ((mrp->my_path = strdup(m.mnt_special)) == NULL) {
1112 			fprintf(stderr, "nfsstat: not enough memory\n");
1113 			exit(1);
1114 		}
1115 		mrp->next = list;
1116 		list = mrp;
1117 	}
1118 
1119 	(void) fclose(mt);
1120 
1121 	if (ignored) {
1122 		/*
1123 		 * Now ignored entries which do not have
1124 		 * the my_dir initialized are really ignored; This never
1125 		 * happens unless the mnttab is corrupted.
1126 		 */
1127 		for (pmrp = 0, mrp = list; mrp; mrp = mrp->next) {
1128 			if (mrp->ig_path == 0)
1129 				pmrp = mrp;
1130 			else if (pmrp)
1131 				pmrp->next = mrp->next;
1132 			else
1133 				list = mrp->next;
1134 		}
1135 	}
1136 
1137 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
1138 		int i;
1139 
1140 		if (ksp->ks_type != KSTAT_TYPE_RAW)
1141 			continue;
1142 		if (strcmp(ksp->ks_module, "nfs") != 0)
1143 			continue;
1144 		if (strcmp(ksp->ks_name, "mntinfo") != 0)
1145 			continue;
1146 
1147 		for (mrp = list; mrp; mrp = mrp->next) {
1148 			if ((mrp->my_fsid & MAXMIN) == ksp->ks_instance)
1149 				break;
1150 		}
1151 		if (mrp == 0)
1152 			continue;
1153 
1154 		if (safe_kstat_read(kc, ksp, &mik) == -1)
1155 			continue;
1156 
1157 		printf("%s from %s\n", mrp->my_dir, mrp->my_path);
1158 
1159 		/*
1160 		 * for printing rdma transport and provider string.
1161 		 * This way we avoid modifying the kernel mntinfo_kstat
1162 		 * struct for protofmly.
1163 		 */
1164 		if (strcmp(mik.mik_proto, "ibtf") == 0) {
1165 			printf(" Flags:		vers=%u,proto=rdma",
1166 			    mik.mik_vers);
1167 			transport_flag = 1;
1168 		} else {
1169 			printf(" Flags:		vers=%u,proto=%s",
1170 			    mik.mik_vers, mik.mik_proto);
1171 			transport_flag = 0;
1172 		}
1173 
1174 		/*
1175 		 *  get the secmode name from /etc/nfssec.conf.
1176 		 */
1177 		if (!nfs_getseconfig_bynumber(mik.mik_secmod, &nfs_sec)) {
1178 			flavor = nfs_sec.sc_name;
1179 		} else
1180 			flavor = NULL;
1181 
1182 		if (flavor != NULL)
1183 			printf(",sec=%s", flavor);
1184 		else
1185 			printf(",sec#=%d", mik.mik_secmod);
1186 
1187 		printf(",%s", (mik.mik_flags & MI_HARD) ? "hard" : "soft");
1188 		if (mik.mik_flags & MI_PRINTED)
1189 			printf(",printed");
1190 		printf(",%s", (mik.mik_flags & MI_INT) ? "intr" : "nointr");
1191 		if (mik.mik_flags & MI_DOWN)
1192 			printf(",down");
1193 		if (mik.mik_flags & MI_NOAC)
1194 			printf(",noac");
1195 		if (mik.mik_flags & MI_NOCTO)
1196 			printf(",nocto");
1197 		if (mik.mik_flags & MI_DYNAMIC)
1198 			printf(",dynamic");
1199 		if (mik.mik_flags & MI_LLOCK)
1200 			printf(",llock");
1201 		if (mik.mik_flags & MI_GRPID)
1202 			printf(",grpid");
1203 		if (mik.mik_flags & MI_RPCTIMESYNC)
1204 			printf(",rpctimesync");
1205 		if (mik.mik_flags & MI_LINK)
1206 			printf(",link");
1207 		if (mik.mik_flags & MI_SYMLINK)
1208 			printf(",symlink");
1209 		if (mik.mik_vers < NFS_V4 && mik.mik_flags & MI_READDIRONLY)
1210 			printf(",readdironly");
1211 		if (mik.mik_flags & MI_ACL)
1212 			printf(",acl");
1213 		if (mik.mik_flags & MI_DIRECTIO)
1214 			printf(",forcedirectio");
1215 
1216 		if (mik.mik_vers >= NFS_V4) {
1217 			if (mik.mik_flags & MI4_MIRRORMOUNT)
1218 				printf(",mirrormount");
1219 			if (mik.mik_flags & MI4_REFERRAL)
1220 				printf(",referral");
1221 		}
1222 
1223 		printf(",rsize=%d,wsize=%d,retrans=%d,timeo=%d",
1224 		    mik.mik_curread, mik.mik_curwrite, mik.mik_retrans,
1225 		    mik.mik_timeo);
1226 		printf("\n");
1227 		printf(" Attr cache:	acregmin=%d,acregmax=%d"
1228 		    ",acdirmin=%d,acdirmax=%d\n", mik.mik_acregmin,
1229 		    mik.mik_acregmax, mik.mik_acdirmin, mik.mik_acdirmax);
1230 
1231 		if (transport_flag) {
1232 			printf(" Transport:	proto=rdma, plugin=%s\n",
1233 			    mik.mik_proto);
1234 		}
1235 
1236 #define	srtt_to_ms(x) x, (x * 2 + x / 2)
1237 #define	dev_to_ms(x) x, (x * 5)
1238 
1239 		for (i = 0; i < NFS_CALLTYPES + 1; i++) {
1240 			int j;
1241 
1242 			j = (i == NFS_CALLTYPES ? i - 1 : i);
1243 			if (mik.mik_timers[j].srtt ||
1244 			    mik.mik_timers[j].rtxcur) {
1245 				printf(" %s:     srtt=%d (%dms), "
1246 				    "dev=%d (%dms), cur=%u (%ums)\n",
1247 				    timer_name[i],
1248 				    srtt_to_ms(mik.mik_timers[i].srtt),
1249 				    dev_to_ms(mik.mik_timers[i].deviate),
1250 				    mik.mik_timers[i].rtxcur,
1251 				    mik.mik_timers[i].rtxcur * 20);
1252 			}
1253 		}
1254 
1255 		if (strchr(mrp->my_path, ','))
1256 			printf(
1257 			    " Failover:	noresponse=%d,failover=%d,"
1258 			    "remap=%d,currserver=%s\n",
1259 			    mik.mik_noresponse, mik.mik_failover,
1260 			    mik.mik_remap, mik.mik_curserver);
1261 		printf("\n");
1262 	}
1263 }
1264 
1265 static char *mntopts[] = { MNTOPT_IGNORE, MNTOPT_DEV, NULL };
1266 #define	IGNORE  0
1267 #define	DEV	1
1268 
1269 /*
1270  * Return 1 if "ignore" appears in the options string
1271  */
1272 static int
1273 ignore(char *opts)
1274 {
1275 	char *value;
1276 	char *s;
1277 
1278 	if (opts == NULL)
1279 		return (0);
1280 	s = strdup(opts);
1281 	if (s == NULL)
1282 		return (0);
1283 	opts = s;
1284 
1285 	while (*opts != '\0') {
1286 		if (getsubopt(&opts, mntopts, &value) == IGNORE) {
1287 			free(s);
1288 			return (1);
1289 		}
1290 	}
1291 
1292 	free(s);
1293 	return (0);
1294 }
1295 
1296 void
1297 usage(void)
1298 {
1299 	fprintf(stderr, "Usage: nfsstat [-cnrsza [-v version] "
1300 	    "[-T d|u] [interval [count]]\n");
1301 	fprintf(stderr, "Usage: nfsstat -m [pathname..]\n");
1302 	exit(1);
1303 }
1304 
1305 void
1306 fail(int do_perror, char *message, ...)
1307 {
1308 	va_list args;
1309 
1310 	va_start(args, message);
1311 	fprintf(stderr, "nfsstat: ");
1312 	vfprintf(stderr, message, args);
1313 	va_end(args);
1314 	if (do_perror)
1315 		fprintf(stderr, ": %s", strerror(errno));
1316 	fprintf(stderr, "\n");
1317 	exit(1);
1318 }
1319 
1320 kid_t
1321 safe_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data)
1322 {
1323 	kid_t kstat_chain_id = kstat_read(kc, ksp, data);
1324 
1325 	if (kstat_chain_id == -1)
1326 		fail(1, "kstat_read(%x, '%s') failed", kc, ksp->ks_name);
1327 	return (kstat_chain_id);
1328 }
1329 
1330 kid_t
1331 safe_kstat_write(kstat_ctl_t *kc, kstat_t *ksp, void *data)
1332 {
1333 	kid_t kstat_chain_id = 0;
1334 
1335 	if (ksp->ks_data != NULL) {
1336 		kstat_chain_id = kstat_write(kc, ksp, data);
1337 
1338 		if (kstat_chain_id == -1)
1339 			fail(1, "kstat_write(%x, '%s') failed", kc,
1340 			    ksp->ks_name);
1341 	}
1342 	return (kstat_chain_id);
1343 }
1344 
1345 void
1346 stats_timer(int interval)
1347 {
1348 	timer_t t_id;
1349 	itimerspec_t time_struct;
1350 	struct sigevent sig_struct;
1351 	struct sigaction act;
1352 
1353 	bzero(&sig_struct, sizeof (struct sigevent));
1354 	bzero(&act, sizeof (struct sigaction));
1355 
1356 	/* Create timer */
1357 	sig_struct.sigev_notify = SIGEV_SIGNAL;
1358 	sig_struct.sigev_signo = SIGUSR1;
1359 	sig_struct.sigev_value.sival_int = 0;
1360 
1361 	if (timer_create(CLOCK_REALTIME, &sig_struct, &t_id) != 0) {
1362 		fail(1, "Timer creation failed");
1363 	}
1364 
1365 	act.sa_handler = handle_sig;
1366 
1367 	if (sigaction(SIGUSR1, &act, NULL) != 0) {
1368 		fail(1, "Could not set up signal handler");
1369 	}
1370 
1371 	time_struct.it_value.tv_sec = interval;
1372 	time_struct.it_value.tv_nsec = 0;
1373 	time_struct.it_interval.tv_sec = interval;
1374 	time_struct.it_interval.tv_nsec = 0;
1375 
1376 	/* Arm timer */
1377 	if ((timer_settime(t_id, 0, &time_struct, NULL)) != 0) {
1378 		fail(1, "Setting timer failed");
1379 	}
1380 }
1381 
1382 void
1383 handle_sig(int x)
1384 {
1385 }
1386 
1387 static void
1388 nfsstat_kstat_copy(kstat_t *src, kstat_t *dst, int fr)
1389 {
1390 
1391 	if (fr)
1392 		free(dst->ks_data);
1393 
1394 	*dst = *src;
1395 
1396 	if (src->ks_data != NULL) {
1397 		safe_zalloc(&dst->ks_data, src->ks_data_size, 0);
1398 		(void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
1399 	} else {
1400 		dst->ks_data = NULL;
1401 		dst->ks_data_size = 0;
1402 	}
1403 }
1404 
1405 /*
1406  * "Safe" allocators - if we return we're guaranteed to have the desired space
1407  * allocated and zero-filled. We exit via fail if we can't get the space.
1408  */
1409 void
1410 safe_zalloc(void **ptr, uint_t size, int free_first)
1411 {
1412 	if (ptr == NULL)
1413 		fail(1, "invalid pointer");
1414 	if (free_first && *ptr != NULL)
1415 		free(*ptr);
1416 	if ((*ptr = (void *)malloc(size)) == NULL)
1417 		fail(1, "malloc failed");
1418 	(void) memset(*ptr, 0, size);
1419 }
1420 
1421 static int
1422 safe_strtoi(char const *val, char *errmsg)
1423 {
1424 	char *end;
1425 	long tmp;
1426 	errno = 0;
1427 	tmp = strtol(val, &end, 10);
1428 	if (*end != '\0' || errno)
1429 		fail(0, "%s %s", errmsg, val);
1430 	return ((int)tmp);
1431 }
1432