2 * nfsstat.c Output NFS statistics
4 * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
11 #define NFSSVCSTAT "/proc/net/rpc/nfsd"
12 #define NFSCLTSTAT "/proc/net/rpc/nfs"
14 #define MOUNTSFILE "/proc/mounts"
26 static unsigned int svcv2info[20]; /* NFSv2 call counts ([0] == 18) */
27 static unsigned int cltv2info[20]; /* NFSv2 call counts ([0] == 18) */
28 static unsigned int svcv3info[24]; /* NFSv3 call counts ([0] == 22) */
29 static unsigned int cltv3info[24]; /* NFSv3 call counts ([0] == 22) */
30 static unsigned int svcv4info[4]; /* NFSv4 call counts ([0] == 2) */
31 static unsigned int cltv4info[34]; /* NFSv4 call counts ([0] == 32) */
32 static unsigned int svcv4opinfo[42];/* NFSv4 call counts ([0] == 40) */
33 static unsigned int svcnetinfo[5]; /* 0 # of received packets
38 static unsigned int cltnetinfo[5]; /* 0 # of received packets
44 static unsigned int svcrpcinfo[6]; /* 0 total # of RPC calls
45 * 1 total # of bad calls
47 * 3 authentication failed
50 static unsigned int cltrpcinfo[4]; /* 0 total # of RPC calls
51 * 1 retransmitted calls
55 static unsigned int svcrcinfo[9]; /* 0 repcache hits
58 * (for pre-2.4 kernels:)
61 * 5 noncached non-directories
62 * 6 noncached directories
66 static unsigned int svcfhinfo[7]; /* (for kernels >= 2.4.0)
70 * 3 noncached directories
71 * 4 noncached non-directories
72 * leave hole to relocate stale for order
76 static const char * nfsv2name[18] = {
77 "null", "getattr", "setattr", "root", "lookup", "readlink",
78 "read", "wrcache", "write", "create", "remove", "rename",
79 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"
82 static const char * nfsv3name[22] = {
83 "null", "getattr", "setattr", "lookup", "access", "readlink",
84 "read", "write", "create", "mkdir", "symlink", "mknod",
85 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
86 "fsstat", "fsinfo", "pathconf", "commit"
89 static const char * nfssvrv4name[2] = {
94 static const char * nfscltv4name[32] = {
95 "null", "read", "write", "commit", "open", "open_conf",
96 "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
97 "setclntid", "confirm", "lock",
98 "lockt", "locku", "access", "getattr", "lookup", "lookup_root",
99 "remove", "rename", "link", "symlink", "create", "pathconf",
100 "statfs", "readlink", "readdir", "server_caps", "delegreturn",
103 static const char * nfssvrv4opname[40] = {
104 "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
105 "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
106 "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
107 "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
108 "putrootfh", "read", "readdir", "readlink", "remove", "rename",
109 "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
110 "setcltidconf", "verify", "write", "rellockowner"
113 typedef struct statinfo {
116 unsigned int * valptr;
119 #define STRUCTSIZE(x) sizeof(x)/sizeof(*x)
121 static statinfo svcinfo[] = {
122 { "net", STRUCTSIZE(svcnetinfo), svcnetinfo },
123 { "rpc", STRUCTSIZE(svcrpcinfo), svcrpcinfo },
124 { "rc", STRUCTSIZE(svcrcinfo), svcrcinfo },
125 { "fh", STRUCTSIZE(svcfhinfo), svcfhinfo },
126 { "proc2", STRUCTSIZE(svcv2info), svcv2info },
127 { "proc3", STRUCTSIZE(svcv3info), svcv3info },
128 { "proc4", STRUCTSIZE(svcv4info), svcv4info },
129 { "proc4ops", STRUCTSIZE(svcv4opinfo),svcv4opinfo},
133 static statinfo cltinfo[] = {
134 { "net", STRUCTSIZE(cltnetinfo), cltnetinfo },
135 { "rpc", STRUCTSIZE(cltrpcinfo), cltrpcinfo },
136 { "proc2", STRUCTSIZE(cltv2info), cltv2info },
137 { "proc3", STRUCTSIZE(cltv3info), cltv3info },
138 { "proc4", STRUCTSIZE(cltv4info), cltv4info },
142 static void print_numbers(const char *, unsigned int *,
144 static void print_callstats(const char *, const char **,
145 unsigned int *, unsigned int);
146 static int parse_statfile(const char *, struct statinfo *);
148 static statinfo *get_stat_info(const char *, struct statinfo *);
150 static int mounts(const char *);
152 #define PRNT_CALLS 0x0001
153 #define PRNT_RPC 0x0002
154 #define PRNT_NET 0x0004
155 #define PRNT_FH 0x0008
156 #define PRNT_RC 0x0010
157 #define PRNT_AUTO 0x1000
158 #define PRNT_V2 0x2000
159 #define PRNT_V3 0x4000
160 #define PRNT_V4 0x8000
161 #define PRNT_ALL 0x0fff
169 void usage(char *name)
171 printf("Usage: %s [OPTION]...\n\
173 -m, --mounted\t\tShow statistics on mounted NFS filesystems\n\
174 -c, --client\t\tShow NFS client statistics\n\
175 -s, --server\t\tShow NFS server statistics\n\
176 -2\t\t\tShow NFS version 2 statistics\n\
177 -3\t\t\tShow NFS version 3 statistics\n\
178 -4\t\t\tShow NFS version 4 statistics\n\
179 -o [facility]\t\tShow statistics on particular facilities.\n\
180 nfs\tNFS protocol information\n\
181 rpc\tGeneral RPC information\n\
182 net\tNetwork layer statistics\n\
183 fh\t\tUsage information on the server's file handle cache\n\
184 rc\t\tUsage information on the server's request reply cache\n\
185 all\tSelect all of the above\n\
186 -v, --verbose, --all\tSame as '-o all'\n\
187 -r, --rpc\t\tShow RPC statistics\n\
188 -n, --nfs\t\tShow NFS statistics\n\
189 --version\t\tShow program version\n\
190 --help\t\tWhat you just did\n\
195 static struct option longopts[] =
197 { "acl", 0, 0, 'a' },
198 { "all", 0, 0, 'v' },
199 { "auto", 0, 0, '\3' },
200 { "client", 0, 0, 'c' },
201 { "mounts", 0, 0, 'm' },
202 { "nfs", 0, 0, 'n' },
203 { "rpc", 0, 0, 'r' },
204 { "server", 0, 0, 's' },
205 { "verbose", 0, 0, 'v' },
206 { "zero", 0, 0, 'z' },
207 { "help", 0, 0, '\1' },
208 { "version", 0, 0, '\2' },
213 main(int argc, char **argv)
224 if ((progname = strrchr(argv[0], '/')))
229 while ((c = getopt_long(argc, argv, "234acmno:vrsz\1\2", longopts, NULL)) != EOF) {
232 fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
238 opt_prt |= PRNT_CALLS;
241 if (!strcmp(optarg, "nfs"))
242 opt_prt |= PRNT_CALLS;
243 else if (!strcmp(optarg, "rpc"))
245 else if (!strcmp(optarg, "net"))
247 else if (!strcmp(optarg, "rc"))
249 else if (!strcmp(optarg, "fh"))
251 else if (!strcmp(optarg, "all"))
252 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
254 fprintf(stderr, "nfsstat: unknown category: "
262 opt_prt |= versions[c - '2'];
268 opt_prt |= PRNT_AUTO;
277 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
278 "not yet supported\n");
281 return mounts(MOUNTSFILE);
286 fprintf(stdout, "nfsstat: " VERSION "\n");
289 printf("Try `%s --help' for more information.\n", progname);
295 opt_srv = opt_clt = 1;
298 if (!(opt_srv + opt_clt))
299 opt_srv = opt_clt = 1;
300 if (!(opt_prt & 0xfff)) {
301 opt_prt |= PRNT_CALLS + PRNT_RPC;
303 if (!(opt_prt & 0xe000)) {
304 opt_prt |= PRNT_AUTO;
306 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
308 "You requested file handle or request cache "
309 "statistics while using the -c option.\n"
310 "This information is available only for the NFS "
315 srv_info = parse_statfile(NFSSVCSTAT, svcinfo);
316 if (srv_info == 0 && opt_clt == 0) {
317 fprintf(stderr, "Warning: No Server Stats (%s: %m).\n", NFSSVCSTAT);
325 clt_info = parse_statfile(NFSCLTSTAT, cltinfo);
326 if (opt_srv == 0 && clt_info == 0) {
327 fprintf(stderr, "Warning: No Client Stats (%s: %m).\n", NFSCLTSTAT);
335 if (opt_prt & PRNT_NET) {
337 "Server packet stats:\n"
338 "packets udp tcp tcpconn\n",
343 if (opt_prt & PRNT_RPC) {
345 "Server rpc stats:\n"
346 "calls badcalls badauth badclnt xdrcall\n",
351 if (opt_prt & PRNT_RC) {
353 "Server reply cache:\n"
354 "hits misses nocache\n",
361 * 2.2 puts all fh-related info after the 'rc' header
362 * 2.4 puts all fh-related info after the 'fh' header, but relocates
363 * 'stale' to the start and swaps dir and nondir :-(
364 * We preseve the 2.2 order
366 if (opt_prt & PRNT_FH) {
367 if (get_stat_info("fh", svcinfo)) { /* >= 2.4 */
368 int t = svcfhinfo[3];
369 svcfhinfo[3]=svcfhinfo[4];
372 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
375 "Server file handle cache:\n"
376 "lookup anon ncachedir ncachedir stale\n",
380 "Server file handle cache:\n"
381 "lookup anon ncachedir ncachedir stale\n",
385 if (opt_prt & PRNT_CALLS) {
386 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && svcv2info[0] && svcv2info[svcv2info[0]+1] != svcv2info[0]))
389 nfsv2name, svcv2info + 1, sizeof(nfsv2name)/sizeof(char *)
391 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && svcv3info[0] && svcv3info[svcv3info[0]+1] != svcv3info[0]))
394 nfsv3name, svcv3info + 1, sizeof(nfsv3name)/sizeof(char *)
396 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && svcv4info[0] && svcv4info[svcv4info[0]+1] != svcv4info[0])) {
399 nfssvrv4name, svcv4info + 1, sizeof(nfssvrv4name)/sizeof(char *)
402 "Server nfs v4 operations:\n",
403 nfssvrv4opname, svcv4opinfo + 1, sizeof(nfssvrv4opname)/sizeof(char *)
410 if (opt_prt & PRNT_NET) {
412 "Client packet stats:\n"
413 "packets udp tcp tcpconn\n",
418 if (opt_prt & PRNT_RPC) {
420 "Client rpc stats:\n"
421 "calls retrans authrefrsh\n",
426 if (opt_prt & PRNT_CALLS) {
427 if ((opt_prt & PRNT_V2) || ((opt_prt & PRNT_AUTO) && cltv2info[0] && cltv2info[cltv2info[0]+1] != cltv2info[0]))
430 nfsv2name, cltv2info + 1, sizeof(nfsv2name)/sizeof(char *)
432 if ((opt_prt & PRNT_V3) || ((opt_prt & PRNT_AUTO) && cltv3info[0] && cltv3info[cltv3info[0]+1] != cltv3info[0]))
435 nfsv3name, cltv3info + 1, sizeof(nfsv3name)/sizeof(char *)
437 if ((opt_prt & PRNT_V4) || ((opt_prt & PRNT_AUTO) && cltv4info[0] && cltv4info[cltv4info[0]+1] != cltv4info[0]))
440 nfscltv4name, cltv4info + 1, sizeof(nfscltv4name)/sizeof(char *)
449 get_stat_info(const char *sp, struct statinfo *statp)
453 for (ip = statp; ip->tag; ip++) {
454 if (!strcmp(sp, ip->tag))
462 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
467 for (i = 0; i < nr; i++)
468 printf("%s%-8d", i? " " : "", info[i]);
473 print_callstats(const char *hdr, const char **names,
474 unsigned int *info, unsigned int nr)
476 unsigned long long total;
477 unsigned long long pct;
481 for (i = 0, total = 0; i < nr; i++)
485 for (i = 0; i < nr; i += 6) {
486 for (j = 0; j < 6 && i + j < nr; j++)
487 printf("%-13s", names[i+j]);
489 for (j = 0; j < 6 && i + j < nr; j++) {
490 pct = ((unsigned long long) info[i+j]*100)/total;
491 printf("%-8d%3llu%% ", info[i+j], pct);
500 parse_statfile(const char *name, struct statinfo *statp)
502 char buffer[4096], *next;
505 /* Being unable to read e.g. the nfsd stats file shouldn't
506 * be a fatal error -- it usually means the module isn't loaded.
508 if ((fp = fopen(name, "r")) == NULL) {
509 // fprintf(stderr, "Warning: %s: %m\n", name);
513 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
515 char *sp, *line = buffer;
517 unsigned int total = 0;
519 if ((next = strchr(line, '\n')) != NULL)
521 if (!(sp = strtok(line, " \t")))
524 ip = get_stat_info(sp, statp);
530 for (i = 0; i < cnt; i++) {
531 if (!(sp = strtok(NULL, " \t")))
533 ip->valptr[i] = atoi(sp);
534 total += ip->valptr[i];
536 ip->valptr[i] = total;
544 mounts(const char *name)
546 char buffer[4096], *next;
549 /* Being unable to read e.g. the nfsd stats file shouldn't
550 * be a fatal error -- it usually means the module isn't loaded.
552 if ((fp = fopen(name, "r")) == NULL) {
553 fprintf(stderr, "Warning: %s: %m\n", name);
557 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
559 char *device, *mount, *type, *flags;
561 if ((next = strchr(line, '\n')) != NULL)
564 if (!(device = strtok(line, " \t")))
567 if (!(mount = strtok(NULL, " \t")))
570 if (!(type = strtok(NULL, " \t")))
573 if (strcmp(type, "nfs")) {
577 if (!(flags = strtok(NULL, " \t")))
580 printf("%s from %s\n", mount, device);
581 printf(" Flags:\t%s\n", flags);