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"
26 #include "common.h"
27 #include "plugin.h"
29 #if HAVE_KSTAT_H
30 #include <kstat.h>
31 #endif
33 /*
34 see /proc/net/rpc/nfs
35 see http://www.missioncriticallinux.com/orph/NFS-Statistics
37 net x x x x
38 rpc_stat.netcnt Not used; always zero.
39 rpc_stat.netudpcnt Not used; always zero.
40 rpc_stat.nettcpcnt Not used; always zero.
41 rpc_stat.nettcpconn Not used; always zero.
43 rpc x x x
44 rpc_stat.rpccnt The number of RPC calls.
45 rpc_stat.rpcretrans The number of retransmitted RPC calls.
46 rpc_stat.rpcauthrefresh The number of credential refreshes.
48 proc2 x x x...
49 proc3 x x x...
51 Procedure NFS Version NFS Version 3
52 Number Procedures Procedures
54 0 null null
55 1 getattr getattr
56 2 setattr setattr
57 3 root lookup
58 4 lookup access
59 5 readlink readlink
60 6 read read
61 7 wrcache write
62 8 write create
63 9 create mkdir
64 10 remove symlink
65 11 rename mknod
66 12 link remove
67 13 symlink rmdir
68 14 mkdir rename
69 15 rmdir link
70 16 readdir readdir
71 17 fsstat readdirplus
72 18 fsstat
73 19 fsinfo
74 20 pathconf
75 21 commit
76 */
78 static const char *nfs2_procedures_names[] =
79 {
80 "null",
81 "getattr",
82 "setattr",
83 "root",
84 "lookup",
85 "readlink",
86 "read",
87 "wrcache",
88 "write",
89 "create",
90 "remove",
91 "rename",
92 "link",
93 "symlink",
94 "mkdir",
95 "rmdir",
96 "readdir",
97 "fsstat"
98 };
99 static size_t nfs2_procedures_names_num = STATIC_ARRAY_SIZE (nfs2_procedures_names);
101 static const char *nfs3_procedures_names[] =
102 {
103 "null",
104 "getattr",
105 "setattr",
106 "lookup",
107 "access",
108 "readlink",
109 "read",
110 "write",
111 "create",
112 "mkdir",
113 "symlink",
114 "mknod",
115 "remove",
116 "rmdir",
117 "rename",
118 "link",
119 "readdir",
120 "readdirplus",
121 "fsstat",
122 "fsinfo",
123 "pathconf",
124 "commit"
125 };
126 static size_t nfs3_procedures_names_num = STATIC_ARRAY_SIZE (nfs3_procedures_names);
128 #if HAVE_LIBKSTAT
129 static const char *nfs4_procedures_names[] =
130 {
131 "null",
132 "compound",
133 "reserved",
134 "access",
135 "close",
136 "commit",
137 "create",
138 "delegpurge",
139 "delegreturn",
140 "getattr",
141 "getfh",
142 "link",
143 "lock",
144 "lockt",
145 "locku",
146 "lookup",
147 "lookupp",
148 "nverify",
149 "open",
150 "openattr",
151 "open_confirm",
152 "open_downgrade",
153 "putfh",
154 "putpubfh",
155 "putrootfh",
156 "read",
157 "readdir",
158 "readlink",
159 "remove",
160 "rename",
161 "renew",
162 "restorefh",
163 "savefh",
164 "secinfo",
165 "setattr",
166 "setclientid",
167 "setclientid_confirm",
168 "verify",
169 "write"
170 };
171 static size_t nfs4_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_procedures_names);
172 #endif
174 #if KERNEL_LINUX
175 static const char *nfs4_server40_procedures_names[] =
176 {
177 "null",
178 "compound",
179 "reserved",
180 "access",
181 "close",
182 "commit",
183 "create",
184 "delegpurge",
185 "delegreturn",
186 "getattr",
187 "getfh",
188 "link",
189 "lock",
190 "lockt",
191 "locku",
192 "lookup",
193 "lookupp",
194 "nverify",
195 "open",
196 "openattr",
197 "open_confirm",
198 "open_downgrade",
199 "putfh",
200 "putpubfh",
201 "putrootfh",
202 "read",
203 "readdir",
204 "readlink",
205 "remove",
206 "rename",
207 "renew",
208 "restorefh",
209 "savefh",
210 "secinfo",
211 "setattr",
212 "setclientid",
213 "setcltid_confirm",
214 "verify",
215 "write",
216 "release_lockowner"
217 };
219 static size_t nfs4_server40_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server40_procedures_names);
221 static const char *nfs4_server41_procedures_names[] =
222 {
223 "backchannel_ctl",
224 "bind_conn_to_session",
225 "exchange_id",
226 "create_session",
227 "destroy_session",
228 "free_stateid",
229 "get_dir_delegation",
230 "getdeviceinfo",
231 "getdevicelist",
232 "layoutcommit",
233 "layoutget",
234 "layoutreturn",
235 "secinfo_no_name",
236 "sequence",
237 "set_ssv",
238 "test_stateid",
239 "want_delegation",
240 "destroy_clientid",
241 "reclaim_complete",
242 };
244 static size_t nfs4_server41_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server41_procedures_names);
246 #define NFS4_SERVER40_NUM_PROC ( \
247 STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) )
249 #define NFS4_SERVER41_NUM_PROC ( \
250 STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) + \
251 STATIC_ARRAY_SIZE (nfs4_server41_procedures_names) )
253 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
255 static const char *nfs4_client40_procedures_names[] =
256 {
257 "null",
258 "read",
259 "write",
260 "commit",
261 "open",
262 "open_confirm",
263 "open_noattr",
264 "open_downgrade",
265 "close",
266 "setattr",
267 "fsinfo",
268 "renew",
269 "setclientid",
270 "setclientid_confirm",
271 "lock",
272 "lockt",
273 "locku",
274 "access",
275 "getattr",
276 "lookup",
277 "lookupp",
278 "remove",
279 "rename",
280 "link",
281 "symlink",
282 "create",
283 "pathconf",
284 "statfs",
285 "readlink",
286 "readdir",
287 "server_caps",
288 "delegreturn",
289 "getacl",
290 "setacl",
291 "fs_locations", /* |35| 2.6.18 */
292 "release_lockowner", /* |42| 2.6.36 */
293 "secinfo", /* |46| 2.6.39 */
294 "fsid_present" /* |54| 3.13 */
295 };
297 static const char *nfs4_client41_procedures_names[] =
298 {
299 "exchange_id", /* |40| 2.6.30 */
300 "create_session", /* |40| 2.6.30 */
301 "destroy_session", /* |40| 2.6.30 */
302 "sequence", /* |40| 2.6.30 */
303 "get_lease_time", /* |40| 2.6.30 */
304 "reclaim_complete", /* |41| 2.6.33 */
305 "layoutget", /* |44| 2.6.37 */
306 "getdeviceinfo", /* |44| 2.6.37 */
307 "layoutcommit", /* |46| 2.6.39 */
308 "layoutreturn", /* |47| 3.0 */
309 "secinfo_no_name", /* |51| 3.1 */
310 "test_stateid", /* |51| 3.1 */
311 "free_stateid", /* |51| 3.1 */
312 "getdevicelist", /* |51| 3.1 */
313 "bind_conn_to_session", /* |53| 3.5 */
314 "destroy_clientid" /* |53| 3.5 */
315 };
317 #define NFS4_CLIENT40_NUM_PROC ( \
318 STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) )
320 #define NFS4_CLIENT41_NUM_PROC ( \
321 STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) + \
322 STATIC_ARRAY_SIZE (nfs4_client41_procedures_names) )
324 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
326 #endif
328 #if HAVE_LIBKSTAT
329 extern kstat_ctl_t *kc;
330 static kstat_t *nfs2_ksp_client;
331 static kstat_t *nfs2_ksp_server;
332 static kstat_t *nfs3_ksp_client;
333 static kstat_t *nfs3_ksp_server;
334 static kstat_t *nfs4_ksp_client;
335 static kstat_t *nfs4_ksp_server;
336 #endif
338 #if KERNEL_LINUX
339 static int nfs_init (void)
340 {
341 return (0);
342 }
343 /* #endif KERNEL_LINUX */
345 #elif HAVE_LIBKSTAT
346 static int nfs_init (void)
347 {
348 kstat_t *ksp_chain = NULL;
350 nfs2_ksp_client = NULL;
351 nfs2_ksp_server = NULL;
352 nfs3_ksp_client = NULL;
353 nfs3_ksp_server = NULL;
354 nfs4_ksp_client = NULL;
355 nfs4_ksp_server = NULL;
357 if (kc == NULL)
358 return (-1);
360 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
361 ksp_chain = ksp_chain->ks_next)
362 {
363 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
364 continue;
365 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
366 nfs2_ksp_server = ksp_chain;
367 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
368 nfs3_ksp_server = ksp_chain;
369 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
370 nfs4_ksp_server = ksp_chain;
371 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
372 nfs2_ksp_client = ksp_chain;
373 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
374 nfs3_ksp_client = ksp_chain;
375 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
376 nfs4_ksp_client = ksp_chain;
377 }
379 return (0);
380 } /* int nfs_init */
381 #endif
383 static void nfs_procedures_submit (const char *plugin_instance,
384 const char **type_instances,
385 value_t *values, size_t values_num)
386 {
387 value_list_t vl = VALUE_LIST_INIT;
388 size_t i;
390 vl.values_len = 1;
391 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
392 sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
393 sstrncpy (vl.plugin_instance, plugin_instance,
394 sizeof (vl.plugin_instance));
395 sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
397 for (i = 0; i < values_num; i++)
398 {
399 vl.values = values + i;
400 sstrncpy (vl.type_instance, type_instances[i],
401 sizeof (vl.type_instance));
402 plugin_dispatch_values (&vl);
403 }
404 } /* void nfs_procedures_submit */
406 #if KERNEL_LINUX
407 static void nfs_submit_fields (int nfs_version, const char *instance,
408 char **fields, size_t fields_num, const char **proc_names)
409 {
410 char plugin_instance[DATA_MAX_NAME_LEN];
411 value_t values[fields_num];
412 size_t i;
414 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
415 nfs_version, instance);
417 for (i = 0; i < fields_num; i++)
418 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
420 nfs_procedures_submit (plugin_instance, proc_names, values,
421 fields_num);
422 }
424 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
425 char **fields, size_t fields_num,
426 const char **proc_names, size_t proc_names_num)
427 {
428 if (fields_num != proc_names_num)
429 {
430 WARNING ("nfs plugin: Wrong number of fields for "
431 "NFSv%i %s statistics. Expected %zu, got %zu.",
432 nfs_version, instance,
433 proc_names_num, fields_num);
434 return (EINVAL);
435 }
437 nfs_submit_fields (nfs_version, instance, fields, fields_num,
438 proc_names);
440 return (0);
441 }
443 static int nfs_submit_nfs4_server (const char *instance, char **fields,
444 size_t fields_num)
445 {
446 static int suppress_warning = 0;
448 if (fields_num != NFS4_SERVER40_NUM_PROC &&
449 fields_num != NFS4_SERVER41_NUM_PROC)
450 {
451 if (!suppress_warning)
452 {
453 WARNING ("nfs plugin: Unexpected number of fields for "
454 "NFSv4 %s statistics: %zu. ",
455 instance, fields_num);
456 }
458 if (fields_num > NFS4_SERVER_MAX_PROC)
459 {
460 fields_num = NFS4_SERVER_MAX_PROC;
461 suppress_warning = 1;
462 }
463 else
464 {
465 return (EINVAL);
466 }
467 }
469 nfs_submit_fields (4, instance, fields,
470 nfs4_server40_procedures_names_num,
471 nfs4_server40_procedures_names);
473 if (fields_num >= NFS4_SERVER41_NUM_PROC)
474 {
475 fields += nfs4_server40_procedures_names_num;
477 nfs_submit_fields (4, instance, fields,
478 nfs4_server41_procedures_names_num,
479 nfs4_server41_procedures_names);
480 }
482 return (0);
483 }
485 static int nfs_submit_nfs4_client (const char *instance, char **fields,
486 size_t fields_num)
487 {
488 size_t proc40_names_num, proc41_names_num;
490 static int suppress_warning = 0;
492 switch (fields_num)
493 {
494 case 34:
495 case 35:
496 case 36:
497 case 37:
498 case 38:
499 /* 4.0-only configuration */
500 proc40_names_num = fields_num;
501 break;
502 case 40:
503 case 41:
504 proc40_names_num = 35;
505 break;
506 case 42:
507 case 44:
508 proc40_names_num = 36;
509 case 46:
510 case 47:
511 case 51:
512 case 53:
513 proc40_names_num = 37;
514 break;
515 case 54:
516 proc40_names_num = 38;
517 break;
518 default:
519 if (!suppress_warning)
520 {
521 WARNING ("nfs plugin: Unexpected number of "
522 "fields for NFSv4 %s "
523 "statistics: %zu. ",
524 instance, fields_num);
525 }
527 if (fields_num > 34)
528 {
529 /* safe fallback to basic nfs40 procedures */
530 fields_num = 34;
531 proc40_names_num = 34;
533 suppress_warning = 1;
534 }
535 else
536 {
537 return (EINVAL);
538 }
539 }
541 nfs_submit_fields (4, instance, fields, proc40_names_num,
542 nfs4_client40_procedures_names);
544 if (fields_num > proc40_names_num)
545 {
546 proc41_names_num = fields_num - proc40_names_num;
547 fields += proc40_names_num;
549 nfs_submit_fields (4, instance, fields,proc41_names_num,
550 nfs4_client41_procedures_names);
551 }
553 return (0);
554 }
556 static void nfs_read_linux (FILE *fh, char *inst)
557 {
558 char buffer[1024];
560 char *fields[64];
561 int fields_num = 0;
563 if (fh == NULL)
564 return;
566 while (fgets (buffer, sizeof (buffer), fh) != NULL)
567 {
568 fields_num = strsplit (buffer,
569 fields, STATIC_ARRAY_SIZE (fields));
571 if (fields_num < 3)
572 continue;
574 if (strcmp (fields[0], "proc2") == 0)
575 {
576 nfs_submit_fields_safe (/* version = */ 2, inst,
577 fields + 2, (size_t) (fields_num - 2),
578 nfs2_procedures_names,
579 nfs2_procedures_names_num);
580 }
581 else if (strncmp (fields[0], "proc3", 5) == 0)
582 {
583 nfs_submit_fields_safe (/* version = */ 3, inst,
584 fields + 2, (size_t) (fields_num - 2),
585 nfs3_procedures_names,
586 nfs3_procedures_names_num);
587 }
588 else if (strcmp (fields[0], "proc4ops") == 0)
589 {
590 if (inst[0] == 's')
591 nfs_submit_nfs4_server (inst, fields + 2,
592 (size_t) (fields_num - 2));
593 }
594 else if (strcmp (fields[0], "proc4") == 0)
595 {
596 if (inst[0] == 'c')
597 nfs_submit_nfs4_client (inst, fields + 2,
598 (size_t) (fields_num - 2));
599 }
600 } /* while (fgets) */
601 } /* void nfs_read_linux */
602 #endif /* KERNEL_LINUX */
604 #if HAVE_LIBKSTAT
605 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
606 char const **proc_names, size_t proc_names_num)
607 {
608 char plugin_instance[DATA_MAX_NAME_LEN];
609 value_t values[proc_names_num];
610 size_t i;
612 if (ksp == NULL)
613 return (EINVAL);
615 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
616 nfs_version, inst);
618 kstat_read(kc, ksp, NULL);
619 for (i = 0; i < proc_names_num; i++)
620 {
621 /* The name passed to kstat_data_lookup() doesn't have the
622 * "const" modifier, so we need to copy the name here. */
623 char name[32];
624 sstrncpy (name, proc_names[i], sizeof (name));
626 values[i].counter = (derive_t) get_kstat_value (ksp, name);
627 }
629 nfs_procedures_submit (plugin_instance, proc_names, values,
630 proc_names_num);
631 return (0);
632 }
633 #endif
635 #if KERNEL_LINUX
636 static int nfs_read (void)
637 {
638 FILE *fh;
640 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
641 {
642 nfs_read_linux (fh, "client");
643 fclose (fh);
644 }
646 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
647 {
648 nfs_read_linux (fh, "server");
649 fclose (fh);
650 }
652 return (0);
653 }
654 /* #endif KERNEL_LINUX */
656 #elif HAVE_LIBKSTAT
657 static int nfs_read (void)
658 {
659 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
660 nfs2_procedures_names, nfs2_procedures_names_num);
661 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
662 nfs2_procedures_names, nfs2_procedures_names_num);
663 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
664 nfs3_procedures_names, nfs3_procedures_names_num);
665 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
666 nfs3_procedures_names, nfs3_procedures_names_num);
667 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
668 nfs4_procedures_names, nfs4_procedures_names_num);
669 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
670 nfs4_procedures_names, nfs4_procedures_names_num);
672 return (0);
673 }
674 #endif /* HAVE_LIBKSTAT */
676 void module_register (void)
677 {
678 plugin_register_init ("nfs", nfs_init);
679 plugin_register_read ("nfs", nfs_read);
680 } /* void module_register */