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.plugin, "nfs", sizeof(vl.plugin));
343 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
344 sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
346 for (size_t i = 0; i < values_num; i++) {
347 vl.values = values + i;
348 sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
349 plugin_dispatch_values(&vl);
350 }
351 } /* void nfs_procedures_submit */
353 #if KERNEL_LINUX
354 static void nfs_submit_fields(int nfs_version, const char *instance,
355 char **fields, size_t fields_num,
356 const char **proc_names) {
357 char plugin_instance[DATA_MAX_NAME_LEN];
358 value_t values[fields_num];
360 ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
361 instance);
363 for (size_t i = 0; i < fields_num; i++)
364 (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
366 nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
367 }
369 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
370 char **fields, size_t fields_num,
371 const char **proc_names,
372 size_t proc_names_num) {
373 if (fields_num != proc_names_num) {
374 WARNING("nfs plugin: Wrong number of fields for "
375 "NFSv%i %s statistics. Expected %zu, got %zu.",
376 nfs_version, instance, proc_names_num, fields_num);
377 return (EINVAL);
378 }
380 nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
382 return (0);
383 }
385 static int nfs_submit_nfs4_server(const char *instance, char **fields,
386 size_t fields_num) {
387 static int suppress_warning = 0;
389 if (fields_num != NFS4_SERVER40_NUM_PROC &&
390 fields_num != NFS4_SERVER41_NUM_PROC) {
391 if (!suppress_warning) {
392 WARNING("nfs plugin: Unexpected number of fields for "
393 "NFSv4 %s statistics: %zu. ",
394 instance, fields_num);
395 }
397 if (fields_num > NFS4_SERVER_MAX_PROC) {
398 fields_num = NFS4_SERVER_MAX_PROC;
399 suppress_warning = 1;
400 } else {
401 return (EINVAL);
402 }
403 }
405 nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
406 nfs4_server40_procedures_names);
408 if (fields_num >= NFS4_SERVER41_NUM_PROC) {
409 fields += nfs4_server40_procedures_names_num;
411 nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
412 nfs4_server41_procedures_names);
413 }
415 return (0);
416 }
418 static int nfs_submit_nfs4_client(const char *instance, char **fields,
419 size_t fields_num) {
420 size_t proc40_names_num, proc41_names_num;
422 static int suppress_warning = 0;
424 switch (fields_num) {
425 case 34:
426 case 35:
427 case 36:
428 case 37:
429 case 38:
430 /* 4.0-only configuration */
431 proc40_names_num = fields_num;
432 break;
433 case 40:
434 case 41:
435 proc40_names_num = 35;
436 break;
437 case 42:
438 case 44:
439 proc40_names_num = 36;
440 break;
441 case 46:
442 case 47:
443 case 51:
444 case 53:
445 proc40_names_num = 37;
446 break;
447 case 54:
448 proc40_names_num = 38;
449 break;
450 default:
451 if (!suppress_warning) {
452 WARNING("nfs plugin: Unexpected number of "
453 "fields for NFSv4 %s "
454 "statistics: %zu. ",
455 instance, fields_num);
456 }
458 if (fields_num > 34) {
459 /* safe fallback to basic nfs40 procedures */
460 fields_num = 34;
461 proc40_names_num = 34;
463 suppress_warning = 1;
464 } else {
465 return (EINVAL);
466 }
467 }
469 nfs_submit_fields(4, instance, fields, proc40_names_num,
470 nfs4_client40_procedures_names);
472 if (fields_num > proc40_names_num) {
473 proc41_names_num = fields_num - proc40_names_num;
474 fields += proc40_names_num;
476 nfs_submit_fields(4, instance, fields, proc41_names_num,
477 nfs4_client41_procedures_names);
478 }
480 return (0);
481 }
483 static void nfs_read_linux(FILE *fh, const char *inst) {
484 char buffer[1024];
486 char *fields[64];
487 int fields_num = 0;
489 if (fh == NULL)
490 return;
492 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
493 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
495 if (fields_num < 3)
496 continue;
498 if (strcmp(fields[0], "proc2") == 0) {
499 nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
500 (size_t)(fields_num - 2), nfs2_procedures_names,
501 nfs2_procedures_names_num);
502 } else if (strncmp(fields[0], "proc3", 5) == 0) {
503 nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
504 (size_t)(fields_num - 2), nfs3_procedures_names,
505 nfs3_procedures_names_num);
506 } else if (strcmp(fields[0], "proc4ops") == 0) {
507 if (inst[0] == 's')
508 nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
509 } else if (strcmp(fields[0], "proc4") == 0) {
510 if (inst[0] == 'c')
511 nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
512 }
513 } /* while (fgets) */
514 } /* void nfs_read_linux */
515 #endif /* KERNEL_LINUX */
517 #if HAVE_LIBKSTAT
518 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
519 char const **proc_names, size_t proc_names_num) {
520 char plugin_instance[DATA_MAX_NAME_LEN];
521 value_t values[proc_names_num];
523 if (ksp == NULL)
524 return (EINVAL);
526 ssnprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
527 inst);
529 kstat_read(kc, ksp, NULL);
530 for (size_t i = 0; i < proc_names_num; i++) {
531 /* The name passed to kstat_data_lookup() doesn't have the
532 * "const" modifier, so we need to copy the name here. */
533 char name[32];
534 sstrncpy(name, proc_names[i], sizeof(name));
536 values[i].counter = (derive_t)get_kstat_value(ksp, name);
537 }
539 nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
540 return (0);
541 }
542 #endif
544 #if KERNEL_LINUX
545 static int nfs_read(void) {
546 FILE *fh;
548 if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
549 nfs_read_linux(fh, "client");
550 fclose(fh);
551 }
553 if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
554 nfs_read_linux(fh, "server");
555 fclose(fh);
556 }
558 return (0);
559 }
560 /* #endif KERNEL_LINUX */
562 #elif HAVE_LIBKSTAT
563 static int nfs_read(void) {
564 nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
565 nfs2_procedures_names, nfs2_procedures_names_num);
566 nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
567 nfs2_procedures_names, nfs2_procedures_names_num);
568 nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
569 nfs3_procedures_names, nfs3_procedures_names_num);
570 nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
571 nfs3_procedures_names, nfs3_procedures_names_num);
572 nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
573 nfs4_procedures_names, nfs4_procedures_names_num);
574 nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
575 nfs4_procedures_names, nfs4_procedures_names_num);
577 return (0);
578 }
579 #endif /* HAVE_LIBKSTAT */
581 void module_register(void) {
582 plugin_register_init("nfs", nfs_init);
583 plugin_register_read("nfs", nfs_read);
584 } /* void module_register */