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.host, hostname_g, sizeof (vl.host));
390 sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
391 sstrncpy (vl.plugin_instance, plugin_instance,
392 sizeof (vl.plugin_instance));
393 sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
395 for (size_t i = 0; i < values_num; i++)
396 {
397 vl.values = values + i;
398 sstrncpy (vl.type_instance, type_instances[i],
399 sizeof (vl.type_instance));
400 plugin_dispatch_values (&vl);
401 }
402 } /* void nfs_procedures_submit */
404 #if KERNEL_LINUX
405 static void nfs_submit_fields (int nfs_version, const char *instance,
406 char **fields, size_t fields_num, const char **proc_names)
407 {
408 char plugin_instance[DATA_MAX_NAME_LEN];
409 value_t values[fields_num];
411 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
412 nfs_version, instance);
414 for (size_t i = 0; i < fields_num; i++)
415 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
417 nfs_procedures_submit (plugin_instance, proc_names, values,
418 fields_num);
419 }
421 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
422 char **fields, size_t fields_num,
423 const char **proc_names, size_t proc_names_num)
424 {
425 if (fields_num != proc_names_num)
426 {
427 WARNING ("nfs plugin: Wrong number of fields for "
428 "NFSv%i %s statistics. Expected %zu, got %zu.",
429 nfs_version, instance,
430 proc_names_num, fields_num);
431 return (EINVAL);
432 }
434 nfs_submit_fields (nfs_version, instance, fields, fields_num,
435 proc_names);
437 return (0);
438 }
440 static int nfs_submit_nfs4_server (const char *instance, char **fields,
441 size_t fields_num)
442 {
443 static int suppress_warning = 0;
445 if (fields_num != NFS4_SERVER40_NUM_PROC &&
446 fields_num != NFS4_SERVER41_NUM_PROC)
447 {
448 if (!suppress_warning)
449 {
450 WARNING ("nfs plugin: Unexpected number of fields for "
451 "NFSv4 %s statistics: %zu. ",
452 instance, fields_num);
453 }
455 if (fields_num > NFS4_SERVER_MAX_PROC)
456 {
457 fields_num = NFS4_SERVER_MAX_PROC;
458 suppress_warning = 1;
459 }
460 else
461 {
462 return (EINVAL);
463 }
464 }
466 nfs_submit_fields (4, instance, fields,
467 nfs4_server40_procedures_names_num,
468 nfs4_server40_procedures_names);
470 if (fields_num >= NFS4_SERVER41_NUM_PROC)
471 {
472 fields += nfs4_server40_procedures_names_num;
474 nfs_submit_fields (4, instance, fields,
475 nfs4_server41_procedures_names_num,
476 nfs4_server41_procedures_names);
477 }
479 return (0);
480 }
482 static int nfs_submit_nfs4_client (const char *instance, char **fields,
483 size_t fields_num)
484 {
485 size_t proc40_names_num, proc41_names_num;
487 static int suppress_warning = 0;
489 switch (fields_num)
490 {
491 case 34:
492 case 35:
493 case 36:
494 case 37:
495 case 38:
496 /* 4.0-only configuration */
497 proc40_names_num = fields_num;
498 break;
499 case 40:
500 case 41:
501 proc40_names_num = 35;
502 break;
503 case 42:
504 case 44:
505 proc40_names_num = 36;
506 break;
507 case 46:
508 case 47:
509 case 51:
510 case 53:
511 proc40_names_num = 37;
512 break;
513 case 54:
514 proc40_names_num = 38;
515 break;
516 default:
517 if (!suppress_warning)
518 {
519 WARNING ("nfs plugin: Unexpected number of "
520 "fields for NFSv4 %s "
521 "statistics: %zu. ",
522 instance, fields_num);
523 }
525 if (fields_num > 34)
526 {
527 /* safe fallback to basic nfs40 procedures */
528 fields_num = 34;
529 proc40_names_num = 34;
531 suppress_warning = 1;
532 }
533 else
534 {
535 return (EINVAL);
536 }
537 }
539 nfs_submit_fields (4, instance, fields, proc40_names_num,
540 nfs4_client40_procedures_names);
542 if (fields_num > proc40_names_num)
543 {
544 proc41_names_num = fields_num - proc40_names_num;
545 fields += proc40_names_num;
547 nfs_submit_fields (4, instance, fields,proc41_names_num,
548 nfs4_client41_procedures_names);
549 }
551 return (0);
552 }
554 static void nfs_read_linux (FILE *fh, const char *inst)
555 {
556 char buffer[1024];
558 char *fields[64];
559 int fields_num = 0;
561 if (fh == NULL)
562 return;
564 while (fgets (buffer, sizeof (buffer), fh) != NULL)
565 {
566 fields_num = strsplit (buffer,
567 fields, STATIC_ARRAY_SIZE (fields));
569 if (fields_num < 3)
570 continue;
572 if (strcmp (fields[0], "proc2") == 0)
573 {
574 nfs_submit_fields_safe (/* version = */ 2, inst,
575 fields + 2, (size_t) (fields_num - 2),
576 nfs2_procedures_names,
577 nfs2_procedures_names_num);
578 }
579 else if (strncmp (fields[0], "proc3", 5) == 0)
580 {
581 nfs_submit_fields_safe (/* version = */ 3, inst,
582 fields + 2, (size_t) (fields_num - 2),
583 nfs3_procedures_names,
584 nfs3_procedures_names_num);
585 }
586 else if (strcmp (fields[0], "proc4ops") == 0)
587 {
588 if (inst[0] == 's')
589 nfs_submit_nfs4_server (inst, fields + 2,
590 (size_t) (fields_num - 2));
591 }
592 else if (strcmp (fields[0], "proc4") == 0)
593 {
594 if (inst[0] == 'c')
595 nfs_submit_nfs4_client (inst, fields + 2,
596 (size_t) (fields_num - 2));
597 }
598 } /* while (fgets) */
599 } /* void nfs_read_linux */
600 #endif /* KERNEL_LINUX */
602 #if HAVE_LIBKSTAT
603 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, const char *inst,
604 char const **proc_names, size_t proc_names_num)
605 {
606 char plugin_instance[DATA_MAX_NAME_LEN];
607 value_t values[proc_names_num];
609 if (ksp == NULL)
610 return (EINVAL);
612 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
613 nfs_version, inst);
615 kstat_read(kc, ksp, NULL);
616 for (size_t i = 0; i < proc_names_num; i++)
617 {
618 /* The name passed to kstat_data_lookup() doesn't have the
619 * "const" modifier, so we need to copy the name here. */
620 char name[32];
621 sstrncpy (name, proc_names[i], sizeof (name));
623 values[i].counter = (derive_t) get_kstat_value (ksp, name);
624 }
626 nfs_procedures_submit (plugin_instance, proc_names, values,
627 proc_names_num);
628 return (0);
629 }
630 #endif
632 #if KERNEL_LINUX
633 static int nfs_read (void)
634 {
635 FILE *fh;
637 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
638 {
639 nfs_read_linux (fh, "client");
640 fclose (fh);
641 }
643 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
644 {
645 nfs_read_linux (fh, "server");
646 fclose (fh);
647 }
649 return (0);
650 }
651 /* #endif KERNEL_LINUX */
653 #elif HAVE_LIBKSTAT
654 static int nfs_read (void)
655 {
656 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
657 nfs2_procedures_names, nfs2_procedures_names_num);
658 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
659 nfs2_procedures_names, nfs2_procedures_names_num);
660 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
661 nfs3_procedures_names, nfs3_procedures_names_num);
662 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
663 nfs3_procedures_names, nfs3_procedures_names_num);
664 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
665 nfs4_procedures_names, nfs4_procedures_names_num);
666 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
667 nfs4_procedures_names, nfs4_procedures_names_num);
669 return (0);
670 }
671 #endif /* HAVE_LIBKSTAT */
673 void module_register (void)
674 {
675 plugin_register_init ("nfs", nfs_init);
676 plugin_register_read ("nfs", nfs_read);
677 } /* void module_register */