3 * Copyright (C) 2005,2006 Jason Pepas
4 * Copyright (C) 2012,2013 Florian Forster
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.
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.
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
20 * Jason Pepas <cell at ices.utexas.edu>
21 * Florian octo Forster <octo at collectd.org>
22 * Cosmin Ioiart <cioiart at gmail.com>
34 static const char *config_keys[] = {"ReportV2", "ReportV3", "ReportV4"};
35 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
36 static _Bool report_v2 = 1;
37 static _Bool report_v3 = 1;
38 static _Bool report_v4 = 1;
42 see http://www.missioncriticallinux.com/orph/NFS-Statistics
45 rpc_stat.netcnt Not used; always zero.
46 rpc_stat.netudpcnt Not used; always zero.
47 rpc_stat.nettcpcnt Not used; always zero.
48 rpc_stat.nettcpconn Not used; always zero.
51 rpc_stat.rpccnt The number of RPC calls.
52 rpc_stat.rpcretrans The number of retransmitted RPC calls.
53 rpc_stat.rpcauthrefresh The number of credential refreshes.
58 Procedure NFS Version NFS Version 3
59 Number Procedures Procedures
85 static const char *nfs2_procedures_names[] = {
86 "null", "getattr", "setattr", "root", "lookup", "readlink",
87 "read", "wrcache", "write", "create", "remove", "rename",
88 "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"};
89 static size_t nfs2_procedures_names_num =
90 STATIC_ARRAY_SIZE(nfs2_procedures_names);
92 static const char *nfs3_procedures_names[] = {
93 "null", "getattr", "setattr", "lookup", "access", "readlink",
94 "read", "write", "create", "mkdir", "symlink", "mknod",
95 "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
96 "fsstat", "fsinfo", "pathconf", "commit"};
97 static size_t nfs3_procedures_names_num =
98 STATIC_ARRAY_SIZE(nfs3_procedures_names);
101 static const char *nfs4_procedures_names[] = {"null",
137 "setclientid_confirm",
140 static size_t nfs4_procedures_names_num =
141 STATIC_ARRAY_SIZE(nfs4_procedures_names);
145 static const char *nfs4_server40_procedures_names[] = {"null",
184 "release_lockowner"};
186 static size_t nfs4_server40_procedures_names_num =
187 STATIC_ARRAY_SIZE(nfs4_server40_procedures_names);
189 static const char *nfs4_server4x_procedures_names[] = {
192 "bind_conn_to_session",
197 "get_dir_delegation",
211 "allocate", /* 3.18 */
213 "copy_notify", /* 3.18 */
214 "deallocate", /* 3.18 */
215 "ioadvise", /* 3.18 */
216 "layouterror", /* 3.18 */
217 "layoutstats", /* 3.18 */
218 "offloadcancel", /* 3.18 */
219 "offloadstatus", /* 3.18 */
220 "readplus", /* 3.18 */
222 "write_same", /* 3.18 */
226 #define NFS4_SERVER40_NUM_PROC \
227 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
229 #define NFS4_SERVER4X_NUM_PROC \
230 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) + \
231 STATIC_ARRAY_SIZE(nfs4_server4x_procedures_names))
233 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER4X_NUM_PROC)
235 static const char *nfs4_client40_procedures_names[] = {
249 "setclientid_confirm",
270 "fs_locations", /* |35| 2.6.18 */
271 "release_lockowner", /* |42| 2.6.36 */
272 "secinfo", /* |46| 2.6.39 */
273 "fsid_present" /* |54| 3.13 */
276 static const char *nfs4_client4x_procedures_names[] = {
278 "exchange_id", /* |40| 2.6.30 */
279 "create_session", /* |40| 2.6.30 */
280 "destroy_session", /* |40| 2.6.30 */
281 "sequence", /* |40| 2.6.30 */
282 "get_lease_time", /* |40| 2.6.30 */
283 "reclaim_complete", /* |41| 2.6.33 */
284 "layoutget", /* |44| 2.6.37 */
285 "getdeviceinfo", /* |44| 2.6.37 */
286 "layoutcommit", /* |46| 2.6.39 */
287 "layoutreturn", /* |47| 3.0 */
288 "secinfo_no_name", /* |51| 3.1 */
289 "test_stateid", /* |51| 3.1 */
290 "free_stateid", /* |51| 3.1 */
291 "getdevicelist", /* |51| 3.1 */
292 "bind_conn_to_session", /* |53| 3.5 */
293 "destroy_clientid", /* |53| 3.5 */
295 "seek", /* |55| 3.18 */
296 "allocate", /* |57| 3.19 */
297 "deallocate", /* |57| 3.19 */
298 "layoutstats", /* |58| 4.2 */
299 "clone", /* |59| 4.4 */
300 "copy" /* |60| 4.7 */
303 #define NFS4_CLIENT40_NUM_PROC \
304 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
306 #define NFS4_CLIENT4X_NUM_PROC \
307 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) + \
308 STATIC_ARRAY_SIZE(nfs4_client4x_procedures_names))
310 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT4X_NUM_PROC)
315 extern kstat_ctl_t *kc;
316 static kstat_t *nfs2_ksp_client;
317 static kstat_t *nfs2_ksp_server;
318 static kstat_t *nfs3_ksp_client;
319 static kstat_t *nfs3_ksp_server;
320 static kstat_t *nfs4_ksp_client;
321 static kstat_t *nfs4_ksp_server;
324 static int nfs_config(const char *key, const char *value) {
325 if (strcasecmp(key, "ReportV2") == 0)
326 report_v2 = IS_TRUE(value);
327 else if (strcasecmp(key, "ReportV3") == 0)
328 report_v3 = IS_TRUE(value);
329 else if (strcasecmp(key, "ReportV4") == 0)
330 report_v4 = IS_TRUE(value);
338 static int nfs_init(void) { return 0; }
339 /* #endif KERNEL_LINUX */
342 static int nfs_init(void) {
343 nfs2_ksp_client = NULL;
344 nfs2_ksp_server = NULL;
345 nfs3_ksp_client = NULL;
346 nfs3_ksp_server = NULL;
347 nfs4_ksp_client = NULL;
348 nfs4_ksp_server = NULL;
353 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
354 ksp_chain = ksp_chain->ks_next) {
355 if (strncmp(ksp_chain->ks_module, "nfs", 3) != 0)
357 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
358 nfs2_ksp_server = ksp_chain;
359 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
360 nfs3_ksp_server = ksp_chain;
361 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
362 nfs4_ksp_server = ksp_chain;
363 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
364 nfs2_ksp_client = ksp_chain;
365 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
366 nfs3_ksp_client = ksp_chain;
367 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
368 nfs4_ksp_client = ksp_chain;
375 static void nfs_procedures_submit(const char *plugin_instance,
376 const char **type_instances, value_t *values,
378 value_list_t vl = VALUE_LIST_INIT;
381 sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
382 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
383 sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
385 for (size_t i = 0; i < values_num; i++) {
386 vl.values = values + i;
387 sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
388 plugin_dispatch_values(&vl);
390 } /* void nfs_procedures_submit */
393 static void nfs_submit_fields(int nfs_version, const char *instance,
394 char **fields, size_t fields_num,
395 const char **proc_names) {
396 char plugin_instance[DATA_MAX_NAME_LEN];
397 value_t values[fields_num];
399 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
402 for (size_t i = 0; i < fields_num; i++)
403 (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
405 nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
408 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
409 char **fields, size_t fields_num,
410 const char **proc_names,
411 size_t proc_names_num) {
412 if (fields_num != proc_names_num) {
413 WARNING("nfs plugin: Wrong number of fields for "
414 "NFSv%i %s statistics. Expected %zu, got %zu.",
415 nfs_version, instance, proc_names_num, fields_num);
419 nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
424 static int nfs_submit_nfs4_server(const char *instance, char **fields,
426 static int suppress_warning = 0;
427 size_t proc4x_names_num;
429 switch (fields_num) {
430 case NFS4_SERVER40_NUM_PROC:
431 case NFS4_SERVER40_NUM_PROC + 19: /* NFS 4.1 */
432 case NFS4_SERVER40_NUM_PROC + 31: /* NFS 4.2 */
433 case NFS4_SERVER40_NUM_PROC + 32: /* NFS 4.2 */
436 if (!suppress_warning) {
437 WARNING("nfs plugin: Unexpected number of fields for "
438 "NFSv4 %s statistics: %zu. ",
439 instance, fields_num);
442 if (fields_num > NFS4_SERVER_MAX_PROC) {
443 fields_num = NFS4_SERVER_MAX_PROC;
444 suppress_warning = 1;
450 nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
451 nfs4_server40_procedures_names);
453 if (fields_num > nfs4_server40_procedures_names_num) {
454 proc4x_names_num = fields_num - nfs4_server40_procedures_names_num;
455 fields += nfs4_server40_procedures_names_num;
457 nfs_submit_fields(4, instance, fields, proc4x_names_num,
458 nfs4_server4x_procedures_names);
464 static int nfs_submit_nfs4_client(const char *instance, char **fields,
466 size_t proc40_names_num, proc4x_names_num;
468 static int suppress_warning = 0;
470 switch (fields_num) {
476 /* 4.0-only configuration */
477 proc40_names_num = fields_num;
481 proc40_names_num = 35;
485 proc40_names_num = 36;
491 proc40_names_num = 37;
499 proc40_names_num = 38;
502 if (!suppress_warning) {
503 WARNING("nfs plugin: Unexpected number of "
504 "fields for NFSv4 %s "
506 instance, fields_num);
509 if (fields_num > 34) {
510 /* safe fallback to basic nfs40 procedures */
512 proc40_names_num = 34;
514 suppress_warning = 1;
520 nfs_submit_fields(4, instance, fields, proc40_names_num,
521 nfs4_client40_procedures_names);
523 if (fields_num > proc40_names_num) {
524 proc4x_names_num = fields_num - proc40_names_num;
525 fields += proc40_names_num;
527 nfs_submit_fields(4, instance, fields, proc4x_names_num,
528 nfs4_client4x_procedures_names);
534 static void nfs_read_linux(FILE *fh, const char *inst) {
543 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
544 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
549 if (strcmp(fields[0], "proc2") == 0 && report_v2) {
550 nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
551 (size_t)(fields_num - 2), nfs2_procedures_names,
552 nfs2_procedures_names_num);
553 } else if (strncmp(fields[0], "proc3", 5) == 0 && report_v3) {
554 nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
555 (size_t)(fields_num - 2), nfs3_procedures_names,
556 nfs3_procedures_names_num);
557 } else if (strcmp(fields[0], "proc4ops") == 0 && report_v4) {
559 nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
560 } else if (strcmp(fields[0], "proc4") == 0 && report_v4) {
562 nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
564 } /* while (fgets) */
565 } /* void nfs_read_linux */
566 #endif /* KERNEL_LINUX */
569 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
570 char const **proc_names, size_t proc_names_num) {
571 char plugin_instance[DATA_MAX_NAME_LEN];
572 value_t values[proc_names_num];
577 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
580 kstat_read(kc, ksp, NULL);
581 for (size_t i = 0; i < proc_names_num; i++) {
582 /* The name passed to kstat_data_lookup() doesn't have the
583 * "const" modifier, so we need to copy the name here. */
585 sstrncpy(name, proc_names[i], sizeof(name));
587 values[i].counter = (derive_t)get_kstat_value(ksp, name);
590 nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
596 static int nfs_read(void) {
599 if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
600 nfs_read_linux(fh, "client");
604 if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
605 nfs_read_linux(fh, "server");
611 /* #endif KERNEL_LINUX */
614 static int nfs_read(void) {
616 nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
617 nfs2_procedures_names, nfs2_procedures_names_num);
618 nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
619 nfs2_procedures_names, nfs2_procedures_names_num);
622 nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
623 nfs3_procedures_names, nfs3_procedures_names_num);
624 nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
625 nfs3_procedures_names, nfs3_procedures_names_num);
628 nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
629 nfs4_procedures_names, nfs4_procedures_names_num);
630 nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
631 nfs4_procedures_names, nfs4_procedures_names_num);
636 #endif /* HAVE_LIBKSTAT */
638 void module_register(void) {
639 plugin_register_config("nfs", nfs_config, config_keys, config_keys_num);
640 plugin_register_init("nfs", nfs_init);
641 plugin_register_read("nfs", nfs_read);
642 } /* void module_register */