nfsstat.c revision 1160694128cd3980cc06abe31af529a887efd310
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* LINTLIBRARY */
/* PROTOLIB1 */
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* nfsstat: Network File System statistics
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <kvm.h>
#include <kstat.h>
#include <sys/sysmacros.h>
#include <nfs/nfs_clnt.h>
#include <inttypes.h>
#include <signal.h>
#include <time.h>
#include <strings.h>
#include <ctype.h>
static kstat_t *ksum_kstat;
static void handle_sig(int);
static int getstats_rpc(void);
static int getstats_nfs(void);
static int getstats_rfsproc(int);
static int getstats_rfsreq(int);
static int getstats_aclproc(void);
static int getstats_aclreq(void);
static void putstats(void);
static void setup(void);
static void cr_print(int);
static void sr_print(int);
static void cn_print(int, int);
static void sn_print(int, int);
static void ca_print(int, int);
static void sa_print(int, int);
static void stats_timer(int);
static void safe_zalloc(void **, uint_t, int);
static int safe_strtoi(char const *, char *);
static void fail(int, char *, ...);
static void usage(void);
static void mi_print(void);
static int ignore(char *);
static int interval; /* interval between stats */
static int count; /* number of iterations the stat is printed */
#define MAX_COLUMNS 80
static int stat_width(kstat_t *, int);
/*
* Struct holds the previous kstat values so
* we can compute deltas when using the -i flag
*/
typedef struct old_kstat
{
int tot;
} old_kstat_t;
int
{
int c, go_forever, j;
int cflag = 0; /* client stats */
int sflag = 0; /* server stats */
int nflag = 0; /* nfs stats */
int rflag = 0; /* rpc stats */
int mflag = 0; /* mount table stats */
int aflag = 0; /* print acl statistics */
int vflag = 0; /* version specified, 0 specifies all */
int zflag = 0; /* zero stats after printing */
char *split_line = "*******************************************"
"*************************************";
interval = 0;
count = 0;
go_forever = 0;
switch (c) {
case 'c':
cflag++;
break;
case 'n':
nflag++;
break;
case 'r':
rflag++;
break;
case 's':
sflag++;
break;
case 'm':
mflag++;
break;
case 'z':
if (geteuid())
fail(0, "Must be root for z flag\n");
zflag++;
break;
case 'a':
aflag++;
break;
case 'v':
fail(0, "Invalid version number\n");
break;
case '?':
default:
usage();
}
}
if (interval < 1)
fail(0, "invalid interval\n");
optind++;
fail(0, "invalid count\n");
}
optind++;
usage();
/*
* no count number was set, so we will loop infinitely
* at interval specified
*/
if (!count)
go_forever = 1;
} else if (mflag) {
fail(0, "The -m flag may not be used with any other flags");
if (*path[j] != '/')
fail(0, "Please fully qualify your pathname "
"with a leading '/'");
optind++;
}
"will be searched for\n");
}
setup();
do {
if (mflag) {
mi_print();
} else {
if (sflag &&
(rpc_clts_server_kstat == NULL ||
nfs_server_v4_kstat == NULL)) {
"nfsstat: kernel is not configured with "
"the server nfs and rpc code.\n");
}
/* if s and nothing else, all 3 prints are called */
}
if (cflag &&
(rpc_clts_client_kstat == NULL ||
nfs_client_kstat == NULL)) {
"nfsstat: kernel is not configured with"
" the client nfs and rpc code.\n");
}
}
}
if (zflag)
putstats();
if (interval)
if (interval > 0)
(void) pause();
} while ((--count > 0) || go_forever);
return (0);
}
static int
getstats_rpc(void)
{
int field_width = 0;
if (rpc_clts_client_kstat != NULL) {
}
if (rpc_cots_client_kstat != NULL) {
}
if (rpc_rdma_client_kstat != NULL) {
}
if (rpc_clts_server_kstat != NULL) {
}
if (rpc_cots_server_kstat != NULL) {
}
if (rpc_rdma_server_kstat != NULL) {
}
return (field_width);
}
static int
getstats_nfs(void)
{
int field_width = 0;
if (nfs_client_kstat != NULL) {
}
if (nfs4_client_kstat != NULL) {
}
if (nfs_server_v2_kstat != NULL) {
}
if (nfs_server_v3_kstat != NULL) {
}
if (nfs_server_v4_kstat != NULL) {
}
return (field_width);
}
static int
getstats_rfsproc(int ver)
{
int field_width = 0;
}
}
}
return (field_width);
}
static int
getstats_rfsreq(int ver)
{
int field_width = 0;
}
}
}
return (field_width);
}
static int
getstats_aclproc(void)
{
int field_width = 0;
if (aclproccnt_v2_kstat != NULL) {
}
if (aclproccnt_v3_kstat != NULL) {
}
return (field_width);
}
static int
getstats_aclreq(void)
{
int field_width = 0;
if (aclreqcnt_v2_kstat != NULL) {
}
if (aclreqcnt_v3_kstat != NULL) {
}
return (field_width);
}
static void
putstats(void)
{
if (rpc_clts_client_kstat != NULL)
if (rpc_cots_client_kstat != NULL)
if (rpc_rdma_client_kstat != NULL)
if (nfs_client_kstat != NULL)
if (nfs4_client_kstat != NULL)
if (rpc_clts_server_kstat != NULL)
if (rpc_cots_server_kstat != NULL)
if (rpc_rdma_server_kstat != NULL)
if (nfs_server_v2_kstat != NULL)
if (nfs_server_v3_kstat != NULL)
if (nfs_server_v4_kstat != NULL)
if (rfsproccnt_v2_kstat != NULL)
if (rfsproccnt_v3_kstat != NULL)
if (rfsproccnt_v4_kstat != NULL)
if (rfsreqcnt_v2_kstat != NULL)
if (rfsreqcnt_v3_kstat != NULL)
if (rfsreqcnt_v4_kstat != NULL)
if (aclproccnt_v2_kstat != NULL)
if (aclproccnt_v3_kstat != NULL)
if (aclreqcnt_v2_kstat != NULL)
if (aclreqcnt_v3_kstat != NULL)
}
static void
setup(void)
{
/* malloc space for our temporary kstat */
fail(0, "Multiple kstat lookups failed."
"Your kernal module may not be loaded\n");
}
static int
{
char fixlen[128];
tot = 0;
for (i = 0; i < nreq; i++) {
if (field_width < len)
field_width = len;
if (tot)
else
per = 0;
if (field_width < len)
field_width = len;
}
return (field_width);
}
static int
{
char fixlen[128];
for (i = 0; i < nreq; i++) {
if (field_width < len)
field_width = len;
if (field_width < len)
field_width = len;
}
return (field_width);
}
static void
{
int field_width;
field_width = getstats_rpc();
stat_print("\nClient rpc:\nConnection oriented:",
zflag);
zflag);
zflag);
}
static void
{
int field_width;
field_width = getstats_rpc();
zflag);
zflag);
zflag);
}
static void
{
int field_width;
field_width = getstats_nfs();
if (vflag == 0) {
field_width, zflag);
}
field_width, zflag);
}
if (vflag == 4) {
field_width, zflag);
}
}
field_width, zflag);
}
field_width, zflag);
}
}
static void
{
int field_width;
field_width = getstats_nfs();
field_width, zflag);
}
field_width, zflag);
}
field_width, zflag);
}
}
}
field_width, zflag);
}
}
static void
{
int field_width;
printf("\nClient nfs_acl:\n");
field_width, zflag);
}
}
}
static void
{
int field_width;
printf("\nServer nfs_acl:\n");
}
}
}
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static void
int zflag)
{
char fixlen[128];
return;
tot = 0;
old_tot = 0;
old_tot = 0;
}
}
}
printf("\n");
else if (tot)
else
per = 0;
}
if (zflag) {
}
printf("\n");
else
}
}
/*
* Separate version of the req_print() to deal with V4 and its use of
* procedures and operations. It looks odd to have the counts for
* both of those lumped into the same set of statistics so this
* function (copy of req_print() does the separation and titles).
*/
#define COUNT 2
static void
{
char fixlen[128];
return;
old_tot_ops = 0;
old_tot = 0;
} else {
}
/* Count the number of operations sent */
if (interval) {
tot_ops -= old_tot_ops;
}
}
printf("\n");
else if (tot)
else
per = 0;
}
printf("\n");
}
}
printf("\n");
else if (tot_ops)
else
per = 0;
}
printf("\n");
}
if (zflag) {
}
else
}
static void
int field_width, int zflag)
{
char fixlen[128];
return;
/* MEANS knp = (kstat_named_t *)req->ks_data */
/* prints out the titles of the columns */
}
printf("\n");
/* prints out the stat numbers */
}
printf("\n");
}
if (zflag) {
}
else
}
static void
{
int i;
return;
}
/*
* my_dir and my_path could be pointers
*/
struct myrec {
char my_dir[MAXPATHLEN];
char *my_path;
char *ig_path;
};
/*
* Print the mount table info
*/
static void
mi_print(void)
{
struct extmnttab m;
char *flavor;
int ignored = 0;
struct mntinfo_kstat mik;
int transport_flag = 0;
int path_count;
int found;
char *timer_name[] = {
"Lookups",
"Reads",
"Writes",
"All"
};
exit(0);
}
/* ignore non "nfs" and save the "ignore" entries */
continue;
/*
* Check to see here if user gave a path(s) to
* only show the mount point they wanted
* Iterate through the list of paths the user gave and see
* if any of them match the our current nfs mount
*/
found = 0;
path_count++) {
== 0) {
found = 1;
break;
}
}
if (!found)
continue;
}
exit(1);
}
if (ignore(m.mnt_mntopts)) {
/*
* ignored entries cannot be ignored for this
* option. We have to display the info for this
* nfs mount. The ignore is an indication
* that the actual mount point is different and
* something is in between the nfs mount.
* So save the mount point now
*/
exit(1);
}
ignored++;
} else {
}
exit(1);
}
}
/*
* If something got ignored, go to the beginning of the mnttab
* and look for the cachefs entries since they are the one
* causing this. The mount point saved for the ignored entries
* is matched against the special to get the actual mount point.
* We are interested in the acutal mount point so that the output
* look nice too.
*/
if (ignored) {
/* ignore non "cachefs" */
continue;
continue;
m.mnt_mountp);
}
}
}
/*
* Now ignored entries which do not have
* the my_dir initialized are really ignored; This never
* happens unless the mnttab is corrupted.
*/
else if (pmrp)
else
}
}
int i;
continue;
continue;
continue;
break;
}
if (mrp == 0)
continue;
continue;
/*
* for printing rdma transport and provider string.
* This way we avoid modifying the kernel mntinfo_kstat
* struct for protofmly.
*/
printf(" Flags: vers=%u,proto=rdma",
transport_flag = 1;
} else {
printf(" Flags: vers=%u,proto=%s",
transport_flag = 0;
}
/*
* get the secmode name from /etc/nfssec.conf.
*/
} else
else
printf(",printed");
printf(",down");
printf(",noac");
printf(",nocto");
printf(",dynamic");
printf(",llock");
printf(",grpid");
printf(",rpctimesync");
printf(",link");
printf(",symlink");
printf(",readdironly");
printf(",acl");
printf(",rsize=%d,wsize=%d,retrans=%d,timeo=%d",
printf("\n");
printf(" Attr cache: acregmin=%d,acregmax=%d"
if (transport_flag) {
printf(" Transport: proto=rdma, plugin=%s\n",
}
#define dev_to_ms(x) x, (x * 5)
for (i = 0; i < NFS_CALLTYPES + 1; i++) {
int j;
j = (i == NFS_CALLTYPES ? i - 1 : i);
" %s: srtt=%d (%dms), dev=%d (%dms), cur=%u (%ums)\n",
timer_name[i],
}
}
" Failover: noresponse=%d,failover=%d,"
"remap=%d,currserver=%s\n",
printf("\n");
}
}
#define IGNORE 0
#define DEV 1
/*
* Return 1 if "ignore" appears in the options string
*/
static int
{
char *value;
char *s;
return (0);
if (s == NULL)
return (0);
opts = s;
while (*opts != '\0') {
free(s);
return (1);
}
}
free(s);
return (0);
}
void
usage(void)
{
"[interval [count]]\n");
exit(1);
}
static void
{
if (do_perror)
exit(1);
}
{
if (kstat_chain_id == -1)
return (kstat_chain_id);
}
{
kid_t kstat_chain_id = 0;
if (kstat_chain_id == -1)
}
return (kstat_chain_id);
}
void
stats_timer(int interval)
{
struct sigevent sig_struct;
/* Create timer */
}
}
/* Arm timer */
}
}
void
handle_sig(int x)
{
}
static void
{
if (fr)
} else {
dst->ks_data_size = 0;
}
}
/*
* "Safe" allocators - if we return we're guaranteed
* to have the desired space. We exit via fail
* if we can't get the space.
*/
void
{
}
static int
{
char *end;
long tmp;
errno = 0;
return ((int)tmp);
}