ee2d93b2f6e43f6e843c7c245032d124055433c9
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 kstat_t *ksp_chain = NULL;
351 nfs2_ksp_client = NULL;
352 nfs2_ksp_server = NULL;
353 nfs3_ksp_client = NULL;
354 nfs3_ksp_server = NULL;
355 nfs4_ksp_client = NULL;
356 nfs4_ksp_server = NULL;
358 if (kc == NULL)
359 return (-1);
361 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
362 ksp_chain = ksp_chain->ks_next)
363 {
364 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
365 continue;
366 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
367 nfs2_ksp_server = ksp_chain;
368 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
369 nfs3_ksp_server = ksp_chain;
370 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
371 nfs4_ksp_server = ksp_chain;
372 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
373 nfs2_ksp_client = ksp_chain;
374 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
375 nfs3_ksp_client = ksp_chain;
376 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
377 nfs4_ksp_client = ksp_chain;
378 }
380 return (0);
381 } /* int nfs_init */
382 #endif
384 static void nfs_procedures_submit (const char *plugin_instance,
385 const char **type_instances,
386 value_t *values, size_t values_num)
387 {
388 value_list_t vl = VALUE_LIST_INIT;
389 size_t i;
391 vl.values_len = 1;
392 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
393 sstrncpy (vl.plugin, "nfs", sizeof (vl.plugin));
394 sstrncpy (vl.plugin_instance, plugin_instance,
395 sizeof (vl.plugin_instance));
396 sstrncpy (vl.type, "nfs_procedure", sizeof (vl.type));
398 for (i = 0; i < values_num; i++)
399 {
400 vl.values = values + i;
401 sstrncpy (vl.type_instance, type_instances[i],
402 sizeof (vl.type_instance));
403 plugin_dispatch_values (&vl);
404 }
405 } /* void nfs_procedures_submit */
407 #if KERNEL_LINUX
408 static void nfs_submit_fields (int nfs_version, const char *instance,
409 char **fields, size_t fields_num, const char **proc_names)
410 {
411 char plugin_instance[DATA_MAX_NAME_LEN];
412 value_t values[fields_num];
413 size_t i;
415 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
416 nfs_version, instance);
418 for (i = 0; i < fields_num; i++)
419 (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
421 nfs_procedures_submit (plugin_instance, proc_names, values,
422 fields_num);
423 }
425 static int nfs_submit_fields_safe (int nfs_version, const char *instance,
426 char **fields, size_t fields_num,
427 const char **proc_names, size_t proc_names_num)
428 {
429 if (fields_num != proc_names_num)
430 {
431 WARNING ("nfs plugin: Wrong number of fields for "
432 "NFSv%i %s statistics. Expected %zu, got %zu.",
433 nfs_version, instance,
434 proc_names_num, fields_num);
435 return (EINVAL);
436 }
438 nfs_submit_fields (nfs_version, instance, fields, fields_num,
439 proc_names);
441 return (0);
442 }
444 static int nfs_submit_nfs4_server (const char *instance, char **fields,
445 size_t fields_num)
446 {
447 static int suppress_warning = 0;
449 if (fields_num != NFS4_SERVER40_NUM_PROC &&
450 fields_num != NFS4_SERVER41_NUM_PROC)
451 {
452 if (!suppress_warning)
453 {
454 WARNING ("nfs plugin: Unexpected number of fields for "
455 "NFSv4 %s statistics: %zu. ",
456 instance, fields_num);
457 }
459 if (fields_num > NFS4_SERVER_MAX_PROC)
460 {
461 fields_num = NFS4_SERVER_MAX_PROC;
462 suppress_warning = 1;
463 }
464 else
465 {
466 return (EINVAL);
467 }
468 }
470 nfs_submit_fields (4, instance, fields,
471 nfs4_server40_procedures_names_num,
472 nfs4_server40_procedures_names);
474 if (fields_num >= NFS4_SERVER41_NUM_PROC)
475 {
476 fields += nfs4_server40_procedures_names_num;
478 nfs_submit_fields (4, instance, fields,
479 nfs4_server41_procedures_names_num,
480 nfs4_server41_procedures_names);
481 }
483 return (0);
484 }
486 static int nfs_submit_nfs4_client (const char *instance, char **fields,
487 size_t fields_num)
488 {
489 size_t proc40_names_num, proc41_names_num;
491 static int suppress_warning = 0;
493 switch (fields_num)
494 {
495 case 34:
496 case 35:
497 case 36:
498 case 37:
499 case 38:
500 /* 4.0-only configuration */
501 proc40_names_num = fields_num;
502 break;
503 case 40:
504 case 41:
505 proc40_names_num = 35;
506 break;
507 case 42:
508 case 44:
509 proc40_names_num = 36;
510 break;
511 case 46:
512 case 47:
513 case 51:
514 case 53:
515 proc40_names_num = 37;
516 break;
517 case 54:
518 proc40_names_num = 38;
519 break;
520 default:
521 if (!suppress_warning)
522 {
523 WARNING ("nfs plugin: Unexpected number of "
524 "fields for NFSv4 %s "
525 "statistics: %zu. ",
526 instance, fields_num);
527 }
529 if (fields_num > 34)
530 {
531 /* safe fallback to basic nfs40 procedures */
532 fields_num = 34;
533 proc40_names_num = 34;
535 suppress_warning = 1;
536 }
537 else
538 {
539 return (EINVAL);
540 }
541 }
543 nfs_submit_fields (4, instance, fields, proc40_names_num,
544 nfs4_client40_procedures_names);
546 if (fields_num > proc40_names_num)
547 {
548 proc41_names_num = fields_num - proc40_names_num;
549 fields += proc40_names_num;
551 nfs_submit_fields (4, instance, fields,proc41_names_num,
552 nfs4_client41_procedures_names);
553 }
555 return (0);
556 }
558 static void nfs_read_linux (FILE *fh, const char *inst)
559 {
560 char buffer[1024];
562 char *fields[64];
563 int fields_num = 0;
565 if (fh == NULL)
566 return;
568 while (fgets (buffer, sizeof (buffer), fh) != NULL)
569 {
570 fields_num = strsplit (buffer,
571 fields, STATIC_ARRAY_SIZE (fields));
573 if (fields_num < 3)
574 continue;
576 if (strcmp (fields[0], "proc2") == 0)
577 {
578 nfs_submit_fields_safe (/* version = */ 2, inst,
579 fields + 2, (size_t) (fields_num - 2),
580 nfs2_procedures_names,
581 nfs2_procedures_names_num);
582 }
583 else if (strncmp (fields[0], "proc3", 5) == 0)
584 {
585 nfs_submit_fields_safe (/* version = */ 3, inst,
586 fields + 2, (size_t) (fields_num - 2),
587 nfs3_procedures_names,
588 nfs3_procedures_names_num);
589 }
590 else if (strcmp (fields[0], "proc4ops") == 0)
591 {
592 if (inst[0] == 's')
593 nfs_submit_nfs4_server (inst, fields + 2,
594 (size_t) (fields_num - 2));
595 }
596 else if (strcmp (fields[0], "proc4") == 0)
597 {
598 if (inst[0] == 'c')
599 nfs_submit_nfs4_client (inst, fields + 2,
600 (size_t) (fields_num - 2));
601 }
602 } /* while (fgets) */
603 } /* void nfs_read_linux */
604 #endif /* KERNEL_LINUX */
606 #if HAVE_LIBKSTAT
607 static int nfs_read_kstat (kstat_t *ksp, int nfs_version, const char *inst,
608 char const **proc_names, size_t proc_names_num)
609 {
610 char plugin_instance[DATA_MAX_NAME_LEN];
611 value_t values[proc_names_num];
612 size_t i;
614 if (ksp == NULL)
615 return (EINVAL);
617 ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
618 nfs_version, inst);
620 kstat_read(kc, ksp, NULL);
621 for (i = 0; i < proc_names_num; i++)
622 {
623 /* The name passed to kstat_data_lookup() doesn't have the
624 * "const" modifier, so we need to copy the name here. */
625 char name[32];
626 sstrncpy (name, proc_names[i], sizeof (name));
628 values[i].counter = (derive_t) get_kstat_value (ksp, name);
629 }
631 nfs_procedures_submit (plugin_instance, proc_names, values,
632 proc_names_num);
633 return (0);
634 }
635 #endif
637 #if KERNEL_LINUX
638 static int nfs_read (void)
639 {
640 FILE *fh;
642 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
643 {
644 nfs_read_linux (fh, "client");
645 fclose (fh);
646 }
648 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
649 {
650 nfs_read_linux (fh, "server");
651 fclose (fh);
652 }
654 return (0);
655 }
656 /* #endif KERNEL_LINUX */
658 #elif HAVE_LIBKSTAT
659 static int nfs_read (void)
660 {
661 nfs_read_kstat (nfs2_ksp_client, /* version = */ 2, "client",
662 nfs2_procedures_names, nfs2_procedures_names_num);
663 nfs_read_kstat (nfs2_ksp_server, /* version = */ 2, "server",
664 nfs2_procedures_names, nfs2_procedures_names_num);
665 nfs_read_kstat (nfs3_ksp_client, /* version = */ 3, "client",
666 nfs3_procedures_names, nfs3_procedures_names_num);
667 nfs_read_kstat (nfs3_ksp_server, /* version = */ 3, "server",
668 nfs3_procedures_names, nfs3_procedures_names_num);
669 nfs_read_kstat (nfs4_ksp_client, /* version = */ 4, "client",
670 nfs4_procedures_names, nfs4_procedures_names_num);
671 nfs_read_kstat (nfs4_ksp_server, /* version = */ 4, "server",
672 nfs4_procedures_names, nfs4_procedures_names_num);
674 return (0);
675 }
676 #endif /* HAVE_LIBKSTAT */
678 void module_register (void)
679 {
680 plugin_register_init ("nfs", nfs_init);
681 plugin_register_read ("nfs", nfs_read);
682 } /* void module_register */