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 break;
510 case 46:
511 case 47:
512 case 51:
513 case 53:
514 proc40_names_num = 37;
515 break;
516 case 54:
517 proc40_names_num = 38;
518 break;
519 default:
520 if (!suppress_warning)
521 {
522 WARNING ("nfs plugin: Unexpected number of "
523 "fields for NFSv4 %s "
524 "statistics: %zu. ",
525 instance, fields_num);
526 }
528 if (fields_num > 34)
529 {
530 /* safe fallback to basic nfs40 procedures */
531 fields_num = 34;
532 proc40_names_num = 34;
534 suppress_warning = 1;
535 }
536 else
537 {
538 return (EINVAL);
539 }
540 }
542 nfs_submit_fields (4, instance, fields, proc40_names_num,
543 nfs4_client40_procedures_names);
545 if (fields_num > proc40_names_num)
546 {
547 proc41_names_num = fields_num - proc40_names_num;
548 fields += proc40_names_num;
550 nfs_submit_fields (4, instance, fields,proc41_names_num,
551 nfs4_client41_procedures_names);
552 }
554 return (0);
555 }
557 static void nfs_read_linux (FILE *fh, const char *inst)
558 {
559 char buffer[1024];
561 char *fields[64];
562 int fields_num = 0;
564 if (fh == NULL)
565 return;
567 while (fgets (buffer, sizeof (buffer), fh) != NULL)
568 {
569 fields_num = strsplit (buffer,
570 fields, STATIC_ARRAY_SIZE (fields));
572 if (fields_num < 3)
573 continue;
575 if (strcmp (fields[0], "proc2") == 0)
576 {
577 nfs_submit_fields_safe (/* version = */ 2, inst,
578 fields + 2, (size_t) (fields_num - 2),
579 nfs2_procedures_names,
580 nfs2_procedures_names_num);
581 }
582 else if (strncmp (fields[0], "proc3", 5) == 0)
583 {
584 nfs_submit_fields_safe (/* version = */ 3, inst,
585 fields + 2, (size_t) (fields_num - 2),
586 nfs3_procedures_names,
587 nfs3_procedures_names_num);
588 }
589 else if (strcmp (fields[0], "proc4ops") == 0)
590 {
591 if (inst[0] == 's')
592 nfs_submit_nfs4_server (inst, fields + 2,
593 (size_t) (fields_num - 2));
594 }
595 else if (strcmp (fields[0], "proc4") == 0)
596 {
597 if (inst[0] == 'c')
598 nfs_submit_nfs4_client (inst, fields + 2,
599 (size_t) (fields_num - 2));
600 }
601 } /* while (fgets) */
602 } /* void nfs_read_linux */
603 #endif /* KERNEL_LINUX */
605 #if HAVE_LIBKSTAT
606 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, const char *inst,
607 char const **proc_names, size_t proc_names_num)
608 {
609 char plugin_instance[DATA_MAX_NAME_LEN];
610 value_t values[proc_names_num];
611 size_t i;
613 if (ksp == NULL)
614 return (EINVAL);
616 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
617 nfs_version, inst);
619 kstat_read(kc, ksp, NULL);
620 for (i = 0; i < proc_names_num; i++)
621 {
622 /* The name passed to kstat_data_lookup() doesn't have the
623 * "const" modifier, so we need to copy the name here. */
624 char name[32];
625 sstrncpy (name, proc_names[i], sizeof (name));
627 values[i].counter = (derive_t) get_kstat_value (ksp, name);
628 }
630 nfs_procedures_submit (plugin_instance, proc_names, values,
631 proc_names_num);
632 return (0);
633 }
634 #endif
636 #if KERNEL_LINUX
637 static int nfs_read (void)
638 {
639 FILE *fh;
641 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
642 {
643 nfs_read_linux (fh, "client");
644 fclose (fh);
645 }
647 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
648 {
649 nfs_read_linux (fh, "server");
650 fclose (fh);
651 }
653 return (0);
654 }
655 /* #endif KERNEL_LINUX */
657 #elif HAVE_LIBKSTAT
658 static int nfs_read (void)
659 {
660 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
661 nfs2_procedures_names, nfs2_procedures_names_num);
662 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
663 nfs2_procedures_names, nfs2_procedures_names_num);
664 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
665 nfs3_procedures_names, nfs3_procedures_names_num);
666 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
667 nfs3_procedures_names, nfs3_procedures_names_num);
668 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
669 nfs4_procedures_names, nfs4_procedures_names_num);
670 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
671 nfs4_procedures_names, nfs4_procedures_names_num);
673 return (0);
674 }
675 #endif /* HAVE_LIBKSTAT */
677 void module_register (void)
678 {
679 plugin_register_init ("nfs", nfs_init);
680 plugin_register_read ("nfs", nfs_read);
681 } /* void module_register */