Home | History | Annotate | Download | only in nfsstat
      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 	/* alloc space for our temporary kstat */
    514 	safe_zalloc((void **)&ksum_kstat, sizeof (kstat_t), 0);
    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 		if (mik.mik_flags & MI_DIRECTIO)
   1239 			printf(",forcedirectio");
   1240 
   1241 		if (mik.mik_vers >= NFS_V4) {
   1242 			if (mik.mik_flags & MI4_MIRRORMOUNT)
   1243 				printf(",mirrormount");
   1244 		}
   1245 
   1246 		printf(",rsize=%d,wsize=%d,retrans=%d,timeo=%d",
   1247 		    mik.mik_curread, mik.mik_curwrite, mik.mik_retrans,
   1248 		    mik.mik_timeo);
   1249 		printf("\n");
   1250 		printf(" Attr cache:	acregmin=%d,acregmax=%d"
   1251 		    ",acdirmin=%d,acdirmax=%d\n", mik.mik_acregmin,
   1252 		    mik.mik_acregmax, mik.mik_acdirmin, mik.mik_acdirmax);
   1253 
   1254 		if (transport_flag) {
   1255 			printf(" Transport:	proto=rdma, plugin=%s\n",
   1256 			    mik.mik_proto);
   1257 		}
   1258 
   1259 #define	srtt_to_ms(x) x, (x * 2 + x / 2)
   1260 #define	dev_to_ms(x) x, (x * 5)
   1261 
   1262 		for (i = 0; i < NFS_CALLTYPES + 1; i++) {
   1263 			int j;
   1264 
   1265 			j = (i == NFS_CALLTYPES ? i - 1 : i);
   1266 			if (mik.mik_timers[j].srtt ||
   1267 			    mik.mik_timers[j].rtxcur) {
   1268 				printf(" %s:     srtt=%d (%dms), "
   1269 				    "dev=%d (%dms), cur=%u (%ums)\n",
   1270 				    timer_name[i],
   1271 				    srtt_to_ms(mik.mik_timers[i].srtt),
   1272 				    dev_to_ms(mik.mik_timers[i].deviate),
   1273 				    mik.mik_timers[i].rtxcur,
   1274 				    mik.mik_timers[i].rtxcur * 20);
   1275 			}
   1276 		}
   1277 
   1278 		if (strchr(mrp->my_path, ','))
   1279 			printf(
   1280 			    " Failover:	noresponse=%d,failover=%d,"
   1281 			    "remap=%d,currserver=%s\n",
   1282 			    mik.mik_noresponse, mik.mik_failover,
   1283 			    mik.mik_remap, mik.mik_curserver);
   1284 		printf("\n");
   1285 	}
   1286 }
   1287 
   1288 static char *mntopts[] = { MNTOPT_IGNORE, MNTOPT_DEV, NULL };
   1289 #define	IGNORE  0
   1290 #define	DEV	1
   1291 
   1292 /*
   1293  * Return 1 if "ignore" appears in the options string
   1294  */
   1295 static int
   1296 ignore(char *opts)
   1297 {
   1298 	char *value;
   1299 	char *s;
   1300 
   1301 	if (opts == NULL)
   1302 		return (0);
   1303 	s = strdup(opts);
   1304 	if (s == NULL)
   1305 		return (0);
   1306 	opts = s;
   1307 
   1308 	while (*opts != '\0') {
   1309 		if (getsubopt(&opts, mntopts, &value) == IGNORE) {
   1310 			free(s);
   1311 			return (1);
   1312 		}
   1313 	}
   1314 
   1315 	free(s);
   1316 	return (0);
   1317 }
   1318 
   1319 void
   1320 usage(void)
   1321 {
   1322 	fprintf(stderr, "Usage: nfsstat [-cnrsza [-v version] "
   1323 	    "[-T d|u] [interval [count]]\n");
   1324 	fprintf(stderr, "Usage: nfsstat -m [pathname..]\n");
   1325 	exit(1);
   1326 }
   1327 
   1328 void
   1329 fail(int do_perror, char *message, ...)
   1330 {
   1331 	va_list args;
   1332 
   1333 	va_start(args, message);
   1334 	fprintf(stderr, "nfsstat: ");
   1335 	vfprintf(stderr, message, args);
   1336 	va_end(args);
   1337 	if (do_perror)
   1338 		fprintf(stderr, ": %s", strerror(errno));
   1339 	fprintf(stderr, "\n");
   1340 	exit(1);
   1341 }
   1342 
   1343 kid_t
   1344 safe_kstat_read(kstat_ctl_t *kc, kstat_t *ksp, void *data)
   1345 {
   1346 	kid_t kstat_chain_id = kstat_read(kc, ksp, data);
   1347 
   1348 	if (kstat_chain_id == -1)
   1349 		fail(1, "kstat_read(%x, '%s') failed", kc, ksp->ks_name);
   1350 	return (kstat_chain_id);
   1351 }
   1352 
   1353 kid_t
   1354 safe_kstat_write(kstat_ctl_t *kc, kstat_t *ksp, void *data)
   1355 {
   1356 	kid_t kstat_chain_id = 0;
   1357 
   1358 	if (ksp->ks_data != NULL) {
   1359 		kstat_chain_id = kstat_write(kc, ksp, data);
   1360 
   1361 		if (kstat_chain_id == -1)
   1362 			fail(1, "kstat_write(%x, '%s') failed", kc,
   1363 			    ksp->ks_name);
   1364 	}
   1365 	return (kstat_chain_id);
   1366 }
   1367 
   1368 void
   1369 stats_timer(int interval)
   1370 {
   1371 	timer_t t_id;
   1372 	itimerspec_t time_struct;
   1373 	struct sigevent sig_struct;
   1374 	struct sigaction act;
   1375 
   1376 	bzero(&sig_struct, sizeof (struct sigevent));
   1377 	bzero(&act, sizeof (struct sigaction));
   1378 
   1379 	/* Create timer */
   1380 	sig_struct.sigev_notify = SIGEV_SIGNAL;
   1381 	sig_struct.sigev_signo = SIGUSR1;
   1382 	sig_struct.sigev_value.sival_int = 0;
   1383 
   1384 	if (timer_create(CLOCK_REALTIME, &sig_struct, &t_id) != 0) {
   1385 		fail(1, "Timer creation failed");
   1386 	}
   1387 
   1388 	act.sa_handler = handle_sig;
   1389 
   1390 	if (sigaction(SIGUSR1, &act, NULL) != 0) {
   1391 		fail(1, "Could not set up signal handler");
   1392 	}
   1393 
   1394 	time_struct.it_value.tv_sec = interval;
   1395 	time_struct.it_value.tv_nsec = 0;
   1396 	time_struct.it_interval.tv_sec = interval;
   1397 	time_struct.it_interval.tv_nsec = 0;
   1398 
   1399 	/* Arm timer */
   1400 	if ((timer_settime(t_id, 0, &time_struct, NULL)) != 0) {
   1401 		fail(1, "Setting timer failed");
   1402 	}
   1403 }
   1404 
   1405 void
   1406 handle_sig(int x)
   1407 {
   1408 }
   1409 
   1410 static void
   1411 nfsstat_kstat_copy(kstat_t *src, kstat_t *dst, int fr)
   1412 {
   1413 
   1414 	if (fr)
   1415 		free(dst->ks_data);
   1416 
   1417 	*dst = *src;
   1418 
   1419 	if (src->ks_data != NULL) {
   1420 		safe_zalloc(&dst->ks_data, src->ks_data_size, 0);
   1421 		(void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size);
   1422 	} else {
   1423 		dst->ks_data = NULL;
   1424 		dst->ks_data_size = 0;
   1425 	}
   1426 }
   1427 
   1428 /*
   1429  * "Safe" allocators - if we return we're guaranteed to have the desired space
   1430  * allocated and zero-filled. We exit via fail if we can't get the space.
   1431  */
   1432 void
   1433 safe_zalloc(void **ptr, uint_t size, int free_first)
   1434 {
   1435 	if (ptr == NULL)
   1436 		fail(1, "invalid pointer");
   1437 	if (free_first && *ptr != NULL)
   1438 		free(*ptr);
   1439 	if ((*ptr = (void *)malloc(size)) == NULL)
   1440 		fail(1, "malloc failed");
   1441 	(void) memset(*ptr, 0, size);
   1442 }
   1443 
   1444 static int
   1445 safe_strtoi(char const *val, char *errmsg)
   1446 {
   1447 	char *end;
   1448 	long tmp;
   1449 	errno = 0;
   1450 	tmp = strtol(val, &end, 10);
   1451 	if (*end != '\0' || errno)
   1452 		fail(0, "%s %s", errmsg, val);
   1453 	return ((int)tmp);
   1454 }
   1455