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