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