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 "null", "getattr", "setattr", "root", "lookup", "readlink",
81 "read", "wrcache", "write", "create", "remove", "rename",
82 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"};
83 static size_t nfs2_procedures_names_num =
84 STATIC_ARRAY_SIZE(nfs2_procedures_names);
86 static const char *nfs3_procedures_names[] = {
87 "null", "getattr", "setattr", "lookup", "access", "readlink",
88 "read", "write", "create", "mkdir", "symlink", "mknod",
89 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
90 "fsstat", "fsinfo", "pathconf", "commit"};
91 static size_t nfs3_procedures_names_num =
92 STATIC_ARRAY_SIZE(nfs3_procedures_names);
94 #if HAVE_LIBKSTAT
95 static const char *nfs4_procedures_names[] = {"null",
96 "compound",
97 "reserved",
98 "access",
99 "close",
100 "commit",
101 "create",
102 "delegpurge",
103 "delegreturn",
104 "getattr",
105 "getfh",
106 "link",
107 "lock",
108 "lockt",
109 "locku",
110 "lookup",
111 "lookupp",
112 "nverify",
113 "open",
114 "openattr",
115 "open_confirm",
116 "open_downgrade",
117 "putfh",
118 "putpubfh",
119 "putrootfh",
120 "read",
121 "readdir",
122 "readlink",
123 "remove",
124 "rename",
125 "renew",
126 "restorefh",
127 "savefh",
128 "secinfo",
129 "setattr",
130 "setclientid",
131 "setclientid_confirm",
132 "verify",
133 "write"};
134 static size_t nfs4_procedures_names_num =
135 STATIC_ARRAY_SIZE(nfs4_procedures_names);
136 #endif
138 #if KERNEL_LINUX
139 static const char *nfs4_server40_procedures_names[] = {"null",
140 "compound",
141 "reserved",
142 "access",
143 "close",
144 "commit",
145 "create",
146 "delegpurge",
147 "delegreturn",
148 "getattr",
149 "getfh",
150 "link",
151 "lock",
152 "lockt",
153 "locku",
154 "lookup",
155 "lookupp",
156 "nverify",
157 "open",
158 "openattr",
159 "open_confirm",
160 "open_downgrade",
161 "putfh",
162 "putpubfh",
163 "putrootfh",
164 "read",
165 "readdir",
166 "readlink",
167 "remove",
168 "rename",
169 "renew",
170 "restorefh",
171 "savefh",
172 "secinfo",
173 "setattr",
174 "setclientid",
175 "setcltid_confirm",
176 "verify",
177 "write",
178 "release_lockowner"};
180 static size_t nfs4_server40_procedures_names_num =
181 STATIC_ARRAY_SIZE(nfs4_server40_procedures_names);
183 static const char *nfs4_server41_procedures_names[] = {
184 "backchannel_ctl",
185 "bind_conn_to_session",
186 "exchange_id",
187 "create_session",
188 "destroy_session",
189 "free_stateid",
190 "get_dir_delegation",
191 "getdeviceinfo",
192 "getdevicelist",
193 "layoutcommit",
194 "layoutget",
195 "layoutreturn",
196 "secinfo_no_name",
197 "sequence",
198 "set_ssv",
199 "test_stateid",
200 "want_delegation",
201 "destroy_clientid",
202 "reclaim_complete",
203 };
205 static size_t nfs4_server41_procedures_names_num =
206 STATIC_ARRAY_SIZE(nfs4_server41_procedures_names);
208 #define NFS4_SERVER40_NUM_PROC \
209 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
211 #define NFS4_SERVER41_NUM_PROC \
212 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) + \
213 STATIC_ARRAY_SIZE(nfs4_server41_procedures_names))
215 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
217 static const char *nfs4_client40_procedures_names[] = {
218 "null",
219 "read",
220 "write",
221 "commit",
222 "open",
223 "open_confirm",
224 "open_noattr",
225 "open_downgrade",
226 "close",
227 "setattr",
228 "fsinfo",
229 "renew",
230 "setclientid",
231 "setclientid_confirm",
232 "lock",
233 "lockt",
234 "locku",
235 "access",
236 "getattr",
237 "lookup",
238 "lookupp",
239 "remove",
240 "rename",
241 "link",
242 "symlink",
243 "create",
244 "pathconf",
245 "statfs",
246 "readlink",
247 "readdir",
248 "server_caps",
249 "delegreturn",
250 "getacl",
251 "setacl",
252 "fs_locations", /* |35| 2.6.18 */
253 "release_lockowner", /* |42| 2.6.36 */
254 "secinfo", /* |46| 2.6.39 */
255 "fsid_present" /* |54| 3.13 */
256 };
258 static const char *nfs4_client41_procedures_names[] = {
259 "exchange_id", /* |40| 2.6.30 */
260 "create_session", /* |40| 2.6.30 */
261 "destroy_session", /* |40| 2.6.30 */
262 "sequence", /* |40| 2.6.30 */
263 "get_lease_time", /* |40| 2.6.30 */
264 "reclaim_complete", /* |41| 2.6.33 */
265 "layoutget", /* |44| 2.6.37 */
266 "getdeviceinfo", /* |44| 2.6.37 */
267 "layoutcommit", /* |46| 2.6.39 */
268 "layoutreturn", /* |47| 3.0 */
269 "secinfo_no_name", /* |51| 3.1 */
270 "test_stateid", /* |51| 3.1 */
271 "free_stateid", /* |51| 3.1 */
272 "getdevicelist", /* |51| 3.1 */
273 "bind_conn_to_session", /* |53| 3.5 */
274 "destroy_clientid" /* |53| 3.5 */
275 };
277 #define NFS4_CLIENT40_NUM_PROC \
278 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
280 #define NFS4_CLIENT41_NUM_PROC \
281 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) + \
282 STATIC_ARRAY_SIZE(nfs4_client41_procedures_names))
284 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
286 #endif
288 #if HAVE_LIBKSTAT
289 extern kstat_ctl_t *kc;
290 static kstat_t *nfs2_ksp_client;
291 static kstat_t *nfs2_ksp_server;
292 static kstat_t *nfs3_ksp_client;
293 static kstat_t *nfs3_ksp_server;
294 static kstat_t *nfs4_ksp_client;
295 static kstat_t *nfs4_ksp_server;
296 #endif
298 #if KERNEL_LINUX
299 static int nfs_init(void) { return (0); }
300 /* #endif KERNEL_LINUX */
302 #elif HAVE_LIBKSTAT
303 static int nfs_init(void) {
304 nfs2_ksp_client = NULL;
305 nfs2_ksp_server = NULL;
306 nfs3_ksp_client = NULL;
307 nfs3_ksp_server = NULL;
308 nfs4_ksp_client = NULL;
309 nfs4_ksp_server = NULL;
311 if (kc == NULL)
312 return (-1);
314 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
315 ksp_chain = ksp_chain->ks_next) {
316 if (strncmp(ksp_chain->ks_module, "nfs", 3) != 0)
317 continue;
318 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
319 nfs2_ksp_server = ksp_chain;
320 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
321 nfs3_ksp_server = ksp_chain;
322 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
323 nfs4_ksp_server = ksp_chain;
324 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
325 nfs2_ksp_client = ksp_chain;
326 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
327 nfs3_ksp_client = ksp_chain;
328 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
329 nfs4_ksp_client = ksp_chain;
330 }
332 return (0);
333 } /* int nfs_init */
334 #endif
336 static void nfs_procedures_submit(const char *plugin_instance,
337 const char **type_instances, value_t *values,
338 size_t values_num) {
339 value_list_t vl = VALUE_LIST_INIT;
341 vl.values_len = 1;
342 sstrncpy(vl.host, hostname_g, sizeof(vl.host));
343 sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
344 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
345 sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
347 for (size_t i = 0; i < values_num; i++) {
348 vl.values = values + i;
349 sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
350 plugin_dispatch_values(&vl);
351 }
352 } /* void nfs_procedures_submit */
354 #if KERNEL_LINUX
355 static void nfs_submit_fields(int nfs_version, const char *instance,
356 char **fields, size_t fields_num,
357 const char **proc_names) {
358 char plugin_instance[DATA_MAX_NAME_LEN];
359 value_t values[fields_num];
361 ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
362 instance);
364 for (size_t i = 0; i < fields_num; i++)
365 (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
367 nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
368 }
370 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
371 char **fields, size_t fields_num,
372 const char **proc_names,
373 size_t proc_names_num) {
374 if (fields_num != proc_names_num) {
375 WARNING("nfs plugin: Wrong number of fields for "
376 "NFSv%i %s statistics. Expected %zu, got %zu.",
377 nfs_version, instance, proc_names_num, fields_num);
378 return (EINVAL);
379 }
381 nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
383 return (0);
384 }
386 static int nfs_submit_nfs4_server(const char *instance, char **fields,
387 size_t fields_num) {
388 static int suppress_warning = 0;
390 if (fields_num != NFS4_SERVER40_NUM_PROC &&
391 fields_num != NFS4_SERVER41_NUM_PROC) {
392 if (!suppress_warning) {
393 WARNING("nfs plugin: Unexpected number of fields for "
394 "NFSv4 %s statistics: %zu. ",
395 instance, fields_num);
396 }
398 if (fields_num > NFS4_SERVER_MAX_PROC) {
399 fields_num = NFS4_SERVER_MAX_PROC;
400 suppress_warning = 1;
401 } else {
402 return (EINVAL);
403 }
404 }
406 nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
407 nfs4_server40_procedures_names);
409 if (fields_num >= NFS4_SERVER41_NUM_PROC) {
410 fields += nfs4_server40_procedures_names_num;
412 nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
413 nfs4_server41_procedures_names);
414 }
416 return (0);
417 }
419 static int nfs_submit_nfs4_client(const char *instance, char **fields,
420 size_t fields_num) {
421 size_t proc40_names_num, proc41_names_num;
423 static int suppress_warning = 0;
425 switch (fields_num) {
426 case 34:
427 case 35:
428 case 36:
429 case 37:
430 case 38:
431 /* 4.0-only configuration */
432 proc40_names_num = fields_num;
433 break;
434 case 40:
435 case 41:
436 proc40_names_num = 35;
437 break;
438 case 42:
439 case 44:
440 proc40_names_num = 36;
441 break;
442 case 46:
443 case 47:
444 case 51:
445 case 53:
446 proc40_names_num = 37;
447 break;
448 case 54:
449 proc40_names_num = 38;
450 break;
451 default:
452 if (!suppress_warning) {
453 WARNING("nfs plugin: Unexpected number of "
454 "fields for NFSv4 %s "
455 "statistics: %zu. ",
456 instance, fields_num);
457 }
459 if (fields_num > 34) {
460 /* safe fallback to basic nfs40 procedures */
461 fields_num = 34;
462 proc40_names_num = 34;
464 suppress_warning = 1;
465 } else {
466 return (EINVAL);
467 }
468 }
470 nfs_submit_fields(4, instance, fields, proc40_names_num,
471 nfs4_client40_procedures_names);
473 if (fields_num > proc40_names_num) {
474 proc41_names_num = fields_num - proc40_names_num;
475 fields += proc40_names_num;
477 nfs_submit_fields(4, instance, fields, proc41_names_num,
478 nfs4_client41_procedures_names);
479 }
481 return (0);
482 }
484 static void nfs_read_linux(FILE *fh, const char *inst) {
485 char buffer[1024];
487 char *fields[64];
488 int fields_num = 0;
490 if (fh == NULL)
491 return;
493 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
494 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
496 if (fields_num < 3)
497 continue;
499 if (strcmp(fields[0], "proc2") == 0) {
500 nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
501 (size_t)(fields_num - 2), nfs2_procedures_names,
502 nfs2_procedures_names_num);
503 } else if (strncmp(fields[0], "proc3", 5) == 0) {
504 nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
505 (size_t)(fields_num - 2), nfs3_procedures_names,
506 nfs3_procedures_names_num);
507 } else if (strcmp(fields[0], "proc4ops") == 0) {
508 if (inst[0] == 's')
509 nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
510 } else if (strcmp(fields[0], "proc4") == 0) {
511 if (inst[0] == 'c')
512 nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
513 }
514 } /* while (fgets) */
515 } /* void nfs_read_linux */
516 #endif /* KERNEL_LINUX */
518 #if HAVE_LIBKSTAT
519 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
520 char const **proc_names, size_t proc_names_num) {
521 char plugin_instance[DATA_MAX_NAME_LEN];
522 value_t values[proc_names_num];
524 if (ksp == NULL)
525 return (EINVAL);
527 ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
528 inst);
530 kstat_read(kc, ksp, NULL);
531 for (size_t i = 0; i < proc_names_num; i++) {
532 /* The name passed to kstat_data_lookup() doesn't have the
533 * "const" modifier, so we need to copy the name here. */
534 char name[32];
535 sstrncpy(name, proc_names[i], sizeof(name));
537 values[i].counter = (derive_t)get_kstat_value(ksp, name);
538 }
540 nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
541 return (0);
542 }
543 #endif
545 #if KERNEL_LINUX
546 static int nfs_read(void) {
547 FILE *fh;
549 if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
550 nfs_read_linux(fh, "client");
551 fclose(fh);
552 }
554 if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
555 nfs_read_linux(fh, "server");
556 fclose(fh);
557 }
559 return (0);
560 }
561 /* #endif KERNEL_LINUX */
563 #elif HAVE_LIBKSTAT
564 static int nfs_read(void) {
565 nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
566 nfs2_procedures_names, nfs2_procedures_names_num);
567 nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
568 nfs2_procedures_names, nfs2_procedures_names_num);
569 nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
570 nfs3_procedures_names, nfs3_procedures_names_num);
571 nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
572 nfs3_procedures_names, nfs3_procedures_names_num);
573 nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
574 nfs4_procedures_names, nfs4_procedures_names_num);
575 nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
576 nfs4_procedures_names, nfs4_procedures_names_num);
578 return (0);
579 }
580 #endif /* HAVE_LIBKSTAT */
582 void module_register(void) {
583 plugin_register_init("nfs", nfs_init);
584 plugin_register_read("nfs", nfs_read);
585 } /* void module_register */