ac211655576166fc94531b4abbd6f3fe6eff49b2
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| 3.6.30 */
300 "create_session", /* |40| 3.6.30 */
301 "destroy_session", /* |40| 3.6.30 */
302 "sequence", /* |40| 3.6.30 */
303 "get_lease_time", /* |40| 3.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 proc40_names_num = 34;
496 break;
497 case 35:
498 proc40_names_num = 35;
499 break;
500 case 40:
501 proc40_names_num = 35;
502 break;
503 case 41:
504 proc40_names_num = 35;
505 break;
506 case 42:
507 proc40_names_num = 36;
508 break;
509 case 44:
510 proc40_names_num = 36;
511 break;
512 case 46:
513 proc40_names_num = 36;
514 break;
515 case 47:
516 proc40_names_num = 36;
517 break;
518 case 51:
519 proc40_names_num = 36;
520 break;
521 case 53:
522 proc40_names_num = 36;
523 break;
524 case 54:
525 proc40_names_num = 37;
526 break;
527 default:
528 if (!suppress_warning)
529 {
530 WARNING ("nfs plugin: Unexpected number of "
531 "fields for NFSv4 %s "
532 "statistics: %zu. ",
533 instance, fields_num);
534 }
536 if (fields_num > 34)
537 {
538 /* safe fallback to basic nfs40 procedures */
539 fields_num = 34;
540 proc40_names_num = 34;
542 suppress_warning = 1;
543 }
544 else
545 {
546 return (EINVAL);
547 }
548 }
550 nfs_submit_fields (4, instance, fields, proc40_names_num,
551 nfs4_client40_procedures_names);
553 if (fields_num > proc40_names_num)
554 {
555 proc41_names_num = fields_num - proc40_names_num;
556 fields += proc40_names_num;
558 nfs_submit_fields (4, instance, fields,proc41_names_num,
559 nfs4_client41_procedures_names);
560 }
562 return (0);
563 }
565 static void nfs_read_linux (FILE *fh, char *inst)
566 {
567 char buffer[1024];
569 char *fields[64];
570 int fields_num = 0;
572 if (fh == NULL)
573 return;
575 while (fgets (buffer, sizeof (buffer), fh) != NULL)
576 {
577 fields_num = strsplit (buffer,
578 fields, STATIC_ARRAY_SIZE (fields));
580 if (fields_num < 3)
581 continue;
583 if (strcmp (fields[0], "proc2") == 0)
584 {
585 nfs_submit_fields_safe (/* version = */ 2, inst,
586 fields + 2, (size_t) (fields_num - 2),
587 nfs2_procedures_names,
588 nfs2_procedures_names_num);
589 }
590 else if (strncmp (fields[0], "proc3", 5) == 0)
591 {
592 nfs_submit_fields_safe (/* version = */ 3, inst,
593 fields + 2, (size_t) (fields_num - 2),
594 nfs3_procedures_names,
595 nfs3_procedures_names_num);
596 }
597 else if (strcmp (fields[0], "proc4ops") == 0)
598 {
599 if (inst[0] == 's')
600 nfs_submit_nfs4_server (inst, fields + 2,
601 (size_t) (fields_num - 2));
602 }
603 else if (strcmp (fields[0], "proc4") == 0)
604 {
605 if (inst[0] == 'c')
606 nfs_submit_nfs4_client (inst, fields + 2,
607 (size_t) (fields_num - 2));
608 }
609 } /* while (fgets) */
610 } /* void nfs_read_linux */
611 #endif /* KERNEL_LINUX */
613 #if HAVE_LIBKSTAT
614 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, char *inst,
615 char const **proc_names, size_t proc_names_num)
616 {
617 char plugin_instance[DATA_MAX_NAME_LEN];
618 value_t values[proc_names_num];
619 size_t i;
621 if (ksp == NULL)
622 return (EINVAL);
624 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
625 nfs_version, inst);
627 kstat_read(kc, ksp, NULL);
628 for (i = 0; i < proc_names_num; i++)
629 {
630 /* The name passed to kstat_data_lookup() doesn't have the
631 * "const" modifier, so we need to copy the name here. */
632 char name[32];
633 sstrncpy (name, proc_names[i], sizeof (name));
635 values[i].counter = (derive_t) get_kstat_value (ksp, name);
636 }
638 nfs_procedures_submit (plugin_instance, proc_names, values,
639 proc_names_num);
640 return (0);
641 }
642 #endif
644 #if KERNEL_LINUX
645 static int nfs_read (void)
646 {
647 FILE *fh;
649 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
650 {
651 nfs_read_linux (fh, "client");
652 fclose (fh);
653 }
655 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
656 {
657 nfs_read_linux (fh, "server");
658 fclose (fh);
659 }
661 return (0);
662 }
663 /* #endif KERNEL_LINUX */
665 #elif HAVE_LIBKSTAT
666 static int nfs_read (void)
667 {
668 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
669 nfs2_procedures_names, nfs2_procedures_names_num);
670 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
671 nfs2_procedures_names, nfs2_procedures_names_num);
672 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
673 nfs3_procedures_names, nfs3_procedures_names_num);
674 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
675 nfs3_procedures_names, nfs3_procedures_names_num);
676 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
677 nfs4_procedures_names, nfs4_procedures_names_num);
678 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
679 nfs4_procedures_names, nfs4_procedures_names_num);
681 return (0);
682 }
683 #endif /* HAVE_LIBKSTAT */
685 void module_register (void)
686 {
687 plugin_register_init ("nfs", nfs_init);
688 plugin_register_read ("nfs", nfs_read);
689 } /* void module_register */