1 /**
2 * collectd - src/nfs.c
3 * Copyright (C) 2005,2006 Jason Pepas
4 * Copyright (C) 2012,2013 Florian Forster
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; only version 2 of the License is applicable.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Jason Pepas <cell at ices.utexas.edu>
21 * Florian octo Forster <octo at collectd.org>
22 * Cosmin Ioiart <cioiart at gmail.com>
23 **/
25 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
30 #if HAVE_KSTAT_H
31 #include <kstat.h>
32 #endif
34 /*
35 see /proc/net/rpc/nfs
36 see http://www.missioncriticallinux.com/orph/NFS-Statistics
38 net x x x x
39 rpc_stat.netcnt Not used; always zero.
40 rpc_stat.netudpcnt Not used; always zero.
41 rpc_stat.nettcpcnt Not used; always zero.
42 rpc_stat.nettcpconn Not used; always zero.
44 rpc x x x
45 rpc_stat.rpccnt The number of RPC calls.
46 rpc_stat.rpcretrans The number of retransmitted RPC calls.
47 rpc_stat.rpcauthrefresh The number of credential refreshes.
49 proc2 x x x...
50 proc3 x x x...
52 Procedure NFS Version NFS Version 3
53 Number Procedures Procedures
55 0 null null
56 1 getattr getattr
57 2 setattr setattr
58 3 root lookup
59 4 lookup access
60 5 readlink readlink
61 6 read read
62 7 wrcache write
63 8 write create
64 9 create mkdir
65 10 remove symlink
66 11 rename mknod
67 12 link remove
68 13 symlink rmdir
69 14 mkdir rename
70 15 rmdir link
71 16 readdir readdir
72 17 fsstat readdirplus
73 18 fsstat
74 19 fsinfo
75 20 pathconf
76 21 commit
77 */
79 static const char *nfs2_procedures_names[] =
80 {
81 "null",
82 "getattr",
83 "setattr",
84 "root",
85 "lookup",
86 "readlink",
87 "read",
88 "wrcache",
89 "write",
90 "create",
91 "remove",
92 "rename",
93 "link",
94 "symlink",
95 "mkdir",
96 "rmdir",
97 "readdir",
98 "fsstat"
99 };
100 static size_t nfs2_procedures_names_num = STATIC_ARRAY_SIZE (nfs2_procedures_names);
102 static const char *nfs3_procedures_names[] =
103 {
104 "null",
105 "getattr",
106 "setattr",
107 "lookup",
108 "access",
109 "readlink",
110 "read",
111 "write",
112 "create",
113 "mkdir",
114 "symlink",
115 "mknod",
116 "remove",
117 "rmdir",
118 "rename",
119 "link",
120 "readdir",
121 "readdirplus",
122 "fsstat",
123 "fsinfo",
124 "pathconf",
125 "commit"
126 };
127 static size_t nfs3_procedures_names_num = STATIC_ARRAY_SIZE (nfs3_procedures_names);
129 #if HAVE_LIBKSTAT
130 static const char *nfs4_procedures_names[] =
131 {
132 "null",
133 "compound",
134 "reserved",
135 "access",
136 "close",
137 "commit",
138 "create",
139 "delegpurge",
140 "delegreturn",
141 "getattr",
142 "getfh",
143 "link",
144 "lock",
145 "lockt",
146 "locku",
147 "lookup",
148 "lookupp",
149 "nverify",
150 "open",
151 "openattr",
152 "open_confirm",
153 "open_downgrade",
154 "putfh",
155 "putpubfh",
156 "putrootfh",
157 "read",
158 "readdir",
159 "readlink",
160 "remove",
161 "rename",
162 "renew",
163 "restorefh",
164 "savefh",
165 "secinfo",
166 "setattr",
167 "setclientid",
168 "setclientid_confirm",
169 "verify",
170 "write"
171 };
172 static size_t nfs4_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_procedures_names);
173 #endif
175 #if KERNEL_LINUX
176 static const char *nfs4_server40_procedures_names[] =
177 {
178 "null",
179 "compound",
180 "reserved",
181 "access",
182 "close",
183 "commit",
184 "create",
185 "delegpurge",
186 "delegreturn",
187 "getattr",
188 "getfh",
189 "link",
190 "lock",
191 "lockt",
192 "locku",
193 "lookup",
194 "lookupp",
195 "nverify",
196 "open",
197 "openattr",
198 "open_confirm",
199 "open_downgrade",
200 "putfh",
201 "putpubfh",
202 "putrootfh",
203 "read",
204 "readdir",
205 "readlink",
206 "remove",
207 "rename",
208 "renew",
209 "restorefh",
210 "savefh",
211 "secinfo",
212 "setattr",
213 "setclientid",
214 "setcltid_confirm",
215 "verify",
216 "write",
217 "release_lockowner"
218 };
220 static size_t nfs4_server40_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server40_procedures_names);
222 static const char *nfs4_server41_procedures_names[] =
223 {
224 "backchannel_ctl",
225 "bind_conn_to_session",
226 "exchange_id",
227 "create_session",
228 "destroy_session",
229 "free_stateid",
230 "get_dir_delegation",
231 "getdeviceinfo",
232 "getdevicelist",
233 "layoutcommit",
234 "layoutget",
235 "layoutreturn",
236 "secinfo_no_name",
237 "sequence",
238 "set_ssv",
239 "test_stateid",
240 "want_delegation",
241 "destroy_clientid",
242 "reclaim_complete",
243 };
245 static size_t nfs4_server41_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server41_procedures_names);
247 #define NFS4_SERVER40_NUM_PROC ( \
248 STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) )
250 #define NFS4_SERVER41_NUM_PROC ( \
251 STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) + \
252 STATIC_ARRAY_SIZE (nfs4_server41_procedures_names) )
254 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
256 static const char *nfs4_client40_procedures_names[] =
257 {
258 "null",
259 "read",
260 "write",
261 "commit",
262 "open",
263 "open_confirm",
264 "open_noattr",
265 "open_downgrade",
266 "close",
267 "setattr",
268 "fsinfo",
269 "renew",
270 "setclientid",
271 "setclientid_confirm",
272 "lock",
273 "lockt",
274 "locku",
275 "access",
276 "getattr",
277 "lookup",
278 "lookupp",
279 "remove",
280 "rename",
281 "link",
282 "symlink",
283 "create",
284 "pathconf",
285 "statfs",
286 "readlink",
287 "readdir",
288 "server_caps",
289 "delegreturn",
290 "getacl",
291 "setacl",
292 "fs_locations", /* |35| 2.6.18 */
293 "release_lockowner", /* |42| 2.6.36 */
294 "secinfo", /* |46| 2.6.39 */
295 "fsid_present" /* |54| 3.13 */
296 };
298 static const char *nfs4_client41_procedures_names[] =
299 {
300 "exchange_id", /* |40| 2.6.30 */
301 "create_session", /* |40| 2.6.30 */
302 "destroy_session", /* |40| 2.6.30 */
303 "sequence", /* |40| 2.6.30 */
304 "get_lease_time", /* |40| 2.6.30 */
305 "reclaim_complete", /* |41| 2.6.33 */
306 "layoutget", /* |44| 2.6.37 */
307 "getdeviceinfo", /* |44| 2.6.37 */
308 "layoutcommit", /* |46| 2.6.39 */
309 "layoutreturn", /* |47| 3.0 */
310 "secinfo_no_name", /* |51| 3.1 */
311 "test_stateid", /* |51| 3.1 */
312 "free_stateid", /* |51| 3.1 */
313 "getdevicelist", /* |51| 3.1 */
314 "bind_conn_to_session", /* |53| 3.5 */
315 "destroy_clientid" /* |53| 3.5 */
316 };
318 #define NFS4_CLIENT40_NUM_PROC ( \
319 STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) )
321 #define NFS4_CLIENT41_NUM_PROC ( \
322 STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) + \
323 STATIC_ARRAY_SIZE (nfs4_client41_procedures_names) )
325 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
327 #endif
329 #if HAVE_LIBKSTAT
330 extern kstat_ctl_t *kc;
331 static kstat_t *nfs2_ksp_client;
332 static kstat_t *nfs2_ksp_server;
333 static kstat_t *nfs3_ksp_client;
334 static kstat_t *nfs3_ksp_server;
335 static kstat_t *nfs4_ksp_client;
336 static kstat_t *nfs4_ksp_server;
337 #endif
339 #if KERNEL_LINUX
340 static int nfs_init (void)
341 {
342 return (0);
343 }
344 /* #endif KERNEL_LINUX */
346 #elif HAVE_LIBKSTAT
347 static int nfs_init (void)
348 {
349 nfs2_ksp_client = NULL;
350 nfs2_ksp_server = NULL;
351 nfs3_ksp_client = NULL;
352 nfs3_ksp_server = NULL;
353 nfs4_ksp_client = NULL;
354 nfs4_ksp_server = NULL;
356 if (kc == NULL)
357 return (-1);
359 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
360 ksp_chain = ksp_chain->ks_next)
361 {
362 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
363 continue;
364 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
365 nfs2_ksp_server = ksp_chain;
366 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
367 nfs3_ksp_server = ksp_chain;
368 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
369 nfs4_ksp_server = ksp_chain;
370 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
371 nfs2_ksp_client = ksp_chain;
372 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
373 nfs3_ksp_client = ksp_chain;
374 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
375 nfs4_ksp_client = ksp_chain;
376 }
378 return (0);
379 } /* int nfs_init */
380 #endif
382 static void nfs_procedures_submit (const char *plugin_instance,
383 const char **type_instances,
384 value_t *values, size_t values_num)
385 {
386 value_list_t vl = VALUE_LIST_INIT;
388 vl.values_len = 1;
389 sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
390 sstrncpy (vl.plugin_instance, plugin_instance,
391 sizeof (vl.plugin_instance));
392 sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
394 for (size_t i = 0; i < values_num; i++)
395 {
396 vl.values = values + i;
397 sstrncpy (vl.type_instance, type_instances[i],
398 sizeof (vl.type_instance));
399 plugin_dispatch_values (&vl);
400 }
401 } /* void nfs_procedures_submit */
403 #if KERNEL_LINUX
404 static void nfs_submit_fields (int nfs_version, const char *instance,
405 char **fields, size_t fields_num, const char **proc_names)
406 {
407 char plugin_instance[DATA_MAX_NAME_LEN];
408 value_t values[fields_num];
410 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
411 nfs_version, instance);
413 for (size_t i = 0; i < fields_num; i++)
414 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
416 nfs_procedures_submit (plugin_instance, proc_names, values,
417 fields_num);
418 }
420 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
421 char **fields, size_t fields_num,
422 const char **proc_names, size_t proc_names_num)
423 {
424 if (fields_num != proc_names_num)
425 {
426 WARNING ("nfs plugin: Wrong number of fields for "
427 "NFSv%i %s statistics. Expected %zu, got %zu.",
428 nfs_version, instance,
429 proc_names_num, fields_num);
430 return (EINVAL);
431 }
433 nfs_submit_fields (nfs_version, instance, fields, fields_num,
434 proc_names);
436 return (0);
437 }
439 static int nfs_submit_nfs4_server (const char *instance, char **fields,
440 size_t fields_num)
441 {
442 static int suppress_warning = 0;
444 if (fields_num != NFS4_SERVER40_NUM_PROC &&
445 fields_num != NFS4_SERVER41_NUM_PROC)
446 {
447 if (!suppress_warning)
448 {
449 WARNING ("nfs plugin: Unexpected number of fields for "
450 "NFSv4 %s statistics: %zu. ",
451 instance, fields_num);
452 }
454 if (fields_num > NFS4_SERVER_MAX_PROC)
455 {
456 fields_num = NFS4_SERVER_MAX_PROC;
457 suppress_warning = 1;
458 }
459 else
460 {
461 return (EINVAL);
462 }
463 }
465 nfs_submit_fields (4, instance, fields,
466 nfs4_server40_procedures_names_num,
467 nfs4_server40_procedures_names);
469 if (fields_num >= NFS4_SERVER41_NUM_PROC)
470 {
471 fields += nfs4_server40_procedures_names_num;
473 nfs_submit_fields (4, instance, fields,
474 nfs4_server41_procedures_names_num,
475 nfs4_server41_procedures_names);
476 }
478 return (0);
479 }
481 static int nfs_submit_nfs4_client (const char *instance, char **fields,
482 size_t fields_num)
483 {
484 size_t proc40_names_num, proc41_names_num;
486 static int suppress_warning = 0;
488 switch (fields_num)
489 {
490 case 34:
491 case 35:
492 case 36:
493 case 37:
494 case 38:
495 /* 4.0-only configuration */
496 proc40_names_num = fields_num;
497 break;
498 case 40:
499 case 41:
500 proc40_names_num = 35;
501 break;
502 case 42:
503 case 44:
504 proc40_names_num = 36;
505 break;
506 case 46:
507 case 47:
508 case 51:
509 case 53:
510 proc40_names_num = 37;
511 break;
512 case 54:
513 proc40_names_num = 38;
514 break;
515 default:
516 if (!suppress_warning)
517 {
518 WARNING ("nfs plugin: Unexpected number of "
519 "fields for NFSv4 %s "
520 "statistics: %zu. ",
521 instance, fields_num);
522 }
524 if (fields_num > 34)
525 {
526 /* safe fallback to basic nfs40 procedures */
527 fields_num = 34;
528 proc40_names_num = 34;
530 suppress_warning = 1;
531 }
532 else
533 {
534 return (EINVAL);
535 }
536 }
538 nfs_submit_fields (4, instance, fields, proc40_names_num,
539 nfs4_client40_procedures_names);
541 if (fields_num > proc40_names_num)
542 {
543 proc41_names_num = fields_num - proc40_names_num;
544 fields += proc40_names_num;
546 nfs_submit_fields (4, instance, fields,proc41_names_num,
547 nfs4_client41_procedures_names);
548 }
550 return (0);
551 }
553 static void nfs_read_linux (FILE *fh, const char *inst)
554 {
555 char buffer[1024];
557 char *fields[64];
558 int fields_num = 0;
560 if (fh == NULL)
561 return;
563 while (fgets (buffer, sizeof (buffer), fh) != NULL)
564 {
565 fields_num = strsplit (buffer,
566 fields, STATIC_ARRAY_SIZE (fields));
568 if (fields_num < 3)
569 continue;
571 if (strcmp (fields[0], "proc2") == 0)
572 {
573 nfs_submit_fields_safe (/* version = */ 2, inst,
574 fields + 2, (size_t) (fields_num - 2),
575 nfs2_procedures_names,
576 nfs2_procedures_names_num);
577 }
578 else if (strncmp (fields[0], "proc3", 5) == 0)
579 {
580 nfs_submit_fields_safe (/* version = */ 3, inst,
581 fields + 2, (size_t) (fields_num - 2),
582 nfs3_procedures_names,
583 nfs3_procedures_names_num);
584 }
585 else if (strcmp (fields[0], "proc4ops") == 0)
586 {
587 if (inst[0] == 's')
588 nfs_submit_nfs4_server (inst, fields + 2,
589 (size_t) (fields_num - 2));
590 }
591 else if (strcmp (fields[0], "proc4") == 0)
592 {
593 if (inst[0] == 'c')
594 nfs_submit_nfs4_client (inst, fields + 2,
595 (size_t) (fields_num - 2));
596 }
597 } /* while (fgets) */
598 } /* void nfs_read_linux */
599 #endif /* KERNEL_LINUX */
601 #if HAVE_LIBKSTAT
602 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, const char *inst,
603 char const **proc_names, size_t proc_names_num)
604 {
605 char plugin_instance[DATA_MAX_NAME_LEN];
606 value_t values[proc_names_num];
608 if (ksp == NULL)
609 return (EINVAL);
611 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
612 nfs_version, inst);
614 kstat_read(kc, ksp, NULL);
615 for (size_t i = 0; i < proc_names_num; i++)
616 {
617 /* The name passed to kstat_data_lookup() doesn't have the
618 * "const" modifier, so we need to copy the name here. */
619 char name[32];
620 sstrncpy (name, proc_names[i], sizeof (name));
622 values[i].counter = (derive_t) get_kstat_value (ksp, name);
623 }
625 nfs_procedures_submit (plugin_instance, proc_names, values,
626 proc_names_num);
627 return (0);
628 }
629 #endif
631 #if KERNEL_LINUX
632 static int nfs_read (void)
633 {
634 FILE *fh;
636 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
637 {
638 nfs_read_linux (fh, "client");
639 fclose (fh);
640 }
642 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
643 {
644 nfs_read_linux (fh, "server");
645 fclose (fh);
646 }
648 return (0);
649 }
650 /* #endif KERNEL_LINUX */
652 #elif HAVE_LIBKSTAT
653 static int nfs_read (void)
654 {
655 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
656 nfs2_procedures_names, nfs2_procedures_names_num);
657 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
658 nfs2_procedures_names, nfs2_procedures_names_num);
659 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
660 nfs3_procedures_names, nfs3_procedures_names_num);
661 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
662 nfs3_procedures_names, nfs3_procedures_names_num);
663 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
664 nfs4_procedures_names, nfs4_procedures_names_num);
665 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
666 nfs4_procedures_names, nfs4_procedures_names_num);
668 return (0);
669 }
670 #endif /* HAVE_LIBKSTAT */
672 void module_register (void)
673 {
674 plugin_register_init ("nfs", nfs_init);
675 plugin_register_read ("nfs", nfs_read);
676 } /* void module_register */