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