2 * nfsstat.c Output NFS statistics
4 * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
9 #define NFSSVCSTAT "/proc/net/rpc/nfsd"
10 #define NFSCLTSTAT "/proc/net/rpc/nfs"
12 #define MOUNTSFILE "/proc/mounts"
23 static unsigned int svcv2info[20]; /* NFSv2 call counts ([0] == 18) */
24 static unsigned int cltv2info[20]; /* NFSv2 call counts ([0] == 18) */
25 static unsigned int svcv3info[24]; /* NFSv3 call counts ([0] == 22) */
26 static unsigned int cltv3info[24]; /* NFSv3 call counts ([0] == 22) */
27 static unsigned int svcv4info[4]; /* NFSv4 call counts ([0] == 2) */
28 static unsigned int cltv4info[34]; /* NFSv4 call counts ([0] == 32) */
29 static unsigned int svcnetinfo[5]; /* 0 # of received packets
34 static unsigned int cltnetinfo[5]; /* 0 # of received packets
40 static unsigned int svcrpcinfo[6]; /* 0 total # of RPC calls
41 * 1 total # of bad calls
43 * 3 authentication failed
46 static unsigned int cltrpcinfo[4]; /* 0 total # of RPC calls
47 * 1 retransmitted calls
51 static unsigned int svcrcinfo[9]; /* 0 repcache hits
54 * (for pre-2.4 kernels:)
57 * 5 noncached non-directories
58 * 6 noncached directories
62 static unsigned int svcfhinfo[7]; /* (for kernels >= 2.4.0)
66 * 3 noncached directories
67 * 4 noncached non-directories
68 * leave hole to relocate stale for order
72 static const char * nfsv2name[18] = {
73 "null", "getattr", "setattr", "root", "lookup", "readlink",
74 "read", "wrcache", "write", "create", "remove", "rename",
75 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"
78 static const char * nfsv3name[22] = {
79 "null", "getattr", "setattr", "lookup", "access", "readlink",
80 "read", "write", "create", "mkdir", "symlink", "mknod",
81 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
82 "fsstat", "fsinfo", "pathconf", "commit"
85 static const char * nfssvrv4name[2] = {
90 static const char * nfscltv4name[32] = {
91 "null", "read", "write", "commit", "open", "open_conf",
92 "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
93 "setclntid", "confirm", "lock",
94 "lockt", "locku", "access", "getattr", "lookup", "lookup_root",
95 "remove", "rename", "link", "symlink", "create", "pathconf",
96 "statfs", "readlink", "readdir", "server_caps", "delegreturn",
99 typedef struct statinfo {
102 unsigned int * valptr;
105 #define STRUCTSIZE(x) sizeof(x)/sizeof(*x)
107 static statinfo svcinfo[] = {
108 { "net", STRUCTSIZE(svcnetinfo), svcnetinfo },
109 { "rpc", STRUCTSIZE(svcrpcinfo), svcrpcinfo },
110 { "rc", STRUCTSIZE(svcrcinfo), svcrcinfo },
111 { "fh", STRUCTSIZE(svcfhinfo), svcfhinfo },
112 { "proc2", STRUCTSIZE(svcv2info), svcv2info },
113 { "proc3", STRUCTSIZE(svcv3info), svcv3info },
114 { "proc4", STRUCTSIZE(svcv4info), svcv4info },
118 static statinfo cltinfo[] = {
119 { "net", STRUCTSIZE(cltnetinfo), cltnetinfo },
120 { "rpc", STRUCTSIZE(cltrpcinfo), cltrpcinfo },
121 { "proc2", STRUCTSIZE(cltv2info), cltv2info },
122 { "proc3", STRUCTSIZE(cltv3info), cltv3info },
123 { "proc4", STRUCTSIZE(cltv4info), cltv4info },
127 static void print_numbers(const char *, unsigned int *,
129 static void print_callstats(const char *, const char **,
130 unsigned int *, unsigned int);
131 static int parse_statfile(const char *, struct statinfo *);
133 static statinfo *get_stat_info(const char *, struct statinfo *);
135 static int mounts(const char *);
137 #define PRNT_CALLS 0x0001
138 #define PRNT_RPC 0x0002
139 #define PRNT_NET 0x0004
140 #define PRNT_FH 0x0008
141 #define PRNT_RC 0x0010
142 #define PRNT_ALL 0x0fff
145 main(int argc, char **argv)
155 while ((c = getopt(argc, argv, "acmno:rsz")) != -1) {
164 opt_prt |= PRNT_CALLS;
167 if (!strcmp(optarg, "nfs"))
168 opt_prt |= PRNT_CALLS;
169 else if (!strcmp(optarg, "rpc"))
171 else if (!strcmp(optarg, "net"))
173 else if (!strcmp(optarg, "rc"))
175 else if (!strcmp(optarg, "fh"))
177 else if (!strcmp(optarg, "all"))
178 opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH;
180 fprintf(stderr, "nfsstat: unknown category: "
192 fprintf(stderr, "nfsstat: zeroing of nfs statistics "
193 "not yet supported\n");
196 return mounts(MOUNTSFILE);
201 opt_srv = opt_clt = 1;
204 if (!(opt_srv + opt_clt))
205 opt_srv = opt_clt = 1;
207 opt_prt = PRNT_CALLS + PRNT_RPC;
208 if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
210 "You requested file handle or request cache "
211 "statistics while using the -c option.\n"
212 "This information is available only for the NFS "
217 srv_info = parse_statfile(NFSSVCSTAT, svcinfo);
218 if (srv_info == 0 && opt_clt == 0) {
219 fprintf(stderr, "Warning: No Server Stats (%s: %m).\n", NFSSVCSTAT);
227 clt_info = parse_statfile(NFSCLTSTAT, cltinfo);
228 if (opt_srv == 0 && clt_info == 0) {
229 fprintf(stderr, "Warning: No Client Stats (%s: %m).\n", NFSCLTSTAT);
237 if (opt_prt & PRNT_NET) {
239 "Server packet stats:\n"
240 "packets udp tcp tcpconn\n",
245 if (opt_prt & PRNT_RPC) {
247 "Server rpc stats:\n"
248 "calls badcalls badauth badclnt xdrcall\n",
253 if (opt_prt & PRNT_RC) {
255 "Server reply cache:\n"
256 "hits misses nocache\n",
263 * 2.2 puts all fh-related info after the 'rc' header
264 * 2.4 puts all fh-related info after the 'fh' header, but relocates
265 * 'stale' to the start and swaps dir and nondir :-(
266 * We preseve the 2.2 order
268 if (opt_prt & PRNT_FH) {
269 if (get_stat_info("fh", svcinfo)) { /* >= 2.4 */
270 int t = svcfhinfo[3];
271 svcfhinfo[3]=svcfhinfo[4];
274 svcfhinfo[5]=svcfhinfo[0]; /* relocate 'stale' */
277 "Server file handle cache:\n"
278 "lookup anon ncachedir ncachedir stale\n",
282 "Server file handle cache:\n"
283 "lookup anon ncachedir ncachedir stale\n",
287 if (opt_prt & PRNT_CALLS) {
290 nfsv2name, svcv2info + 1, sizeof(nfsv2name)/sizeof(char *)
295 nfsv3name, svcv3info + 1, sizeof(nfsv3name)/sizeof(char *)
300 nfssvrv4name, svcv4info + 1, sizeof(nfssvrv4name)/sizeof(char *)
306 if (opt_prt & PRNT_NET) {
308 "Client packet stats:\n"
309 "packets udp tcp tcpconn\n",
314 if (opt_prt & PRNT_RPC) {
316 "Client rpc stats:\n"
317 "calls retrans authrefrsh\n",
322 if (opt_prt & PRNT_CALLS) {
325 nfsv2name, cltv2info + 1, sizeof(nfsv2name)/sizeof(char *)
330 nfsv3name, cltv3info + 1, sizeof(nfsv3name)/sizeof(char *)
335 nfscltv4name, cltv4info + 1, sizeof(nfscltv4name)/sizeof(char *)
344 get_stat_info(const char *sp, struct statinfo *statp)
348 for (ip = statp; ip->tag; ip++) {
349 if (!strcmp(sp, ip->tag))
357 print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
362 for (i = 0; i < nr; i++)
363 printf("%s%-8d", i? " " : "", info[i]);
368 print_callstats(const char *hdr, const char **names,
369 unsigned int *info, unsigned int nr)
371 unsigned long long total;
372 unsigned long long pct;
376 for (i = 0, total = 0; i < nr; i++)
380 for (i = 0; i < nr; i += 6) {
381 for (j = 0; j < 6 && i + j < nr; j++)
382 printf("%-13s", names[i+j]);
384 for (j = 0; j < 6 && i + j < nr; j++) {
385 pct = ((unsigned long long) info[i+j]*100)/total;
386 printf("%-8d%3llu%% ", info[i+j], pct);
395 parse_statfile(const char *name, struct statinfo *statp)
397 char buffer[4096], *next;
400 /* Being unable to read e.g. the nfsd stats file shouldn't
401 * be a fatal error -- it usually means the module isn't loaded.
403 if ((fp = fopen(name, "r")) == NULL) {
404 // fprintf(stderr, "Warning: %s: %m\n", name);
408 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
410 char *sp, *line = buffer;
412 unsigned int total = 0;
414 if ((next = strchr(line, '\n')) != NULL)
416 if (!(sp = strtok(line, " \t")))
419 ip = get_stat_info(sp, statp);
425 for (i = 0; i < cnt; i++) {
426 if (!(sp = strtok(NULL, " \t")))
428 ip->valptr[i] = atoi(sp);
429 total += ip->valptr[i];
431 ip->valptr[i] = total;
439 mounts(const char *name)
441 char buffer[4096], *next;
444 /* Being unable to read e.g. the nfsd stats file shouldn't
445 * be a fatal error -- it usually means the module isn't loaded.
447 if ((fp = fopen(name, "r")) == NULL) {
448 fprintf(stderr, "Warning: %s: %m\n", name);
452 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
454 char *device, *mount, *type, *flags;
456 if ((next = strchr(line, '\n')) != NULL)
459 if (!(device = strtok(line, " \t")))
462 if (!(mount = strtok(NULL, " \t")))
465 if (!(type = strtok(NULL, " \t")))
468 if (strcmp(type, "nfs")) {
472 if (!(flags = strtok(NULL, " \t")))
475 printf("%s from %s\n", mount, device);
476 printf(" Flags:\t%s\n", flags);