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_server41_procedures_names[] = {
191 "bind_conn_to_session",
196 "get_dir_delegation",
211 static size_t nfs4_server41_procedures_names_num =
212 STATIC_ARRAY_SIZE(nfs4_server41_procedures_names);
214 #define NFS4_SERVER40_NUM_PROC \
215 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
217 #define NFS4_SERVER41_NUM_PROC \
218 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) + \
219 STATIC_ARRAY_SIZE(nfs4_server41_procedures_names))
221 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
223 static const char *nfs4_client40_procedures_names[] = {
237 "setclientid_confirm",
258 "fs_locations", /* |35| 2.6.18 */
259 "release_lockowner", /* |42| 2.6.36 */
260 "secinfo", /* |46| 2.6.39 */
261 "fsid_present" /* |54| 3.13 */
264 static const char *nfs4_client41_procedures_names[] = {
265 "exchange_id", /* |40| 2.6.30 */
266 "create_session", /* |40| 2.6.30 */
267 "destroy_session", /* |40| 2.6.30 */
268 "sequence", /* |40| 2.6.30 */
269 "get_lease_time", /* |40| 2.6.30 */
270 "reclaim_complete", /* |41| 2.6.33 */
271 "layoutget", /* |44| 2.6.37 */
272 "getdeviceinfo", /* |44| 2.6.37 */
273 "layoutcommit", /* |46| 2.6.39 */
274 "layoutreturn", /* |47| 3.0 */
275 "secinfo_no_name", /* |51| 3.1 */
276 "test_stateid", /* |51| 3.1 */
277 "free_stateid", /* |51| 3.1 */
278 "getdevicelist", /* |51| 3.1 */
279 "bind_conn_to_session", /* |53| 3.5 */
280 "destroy_clientid" /* |53| 3.5 */
283 #define NFS4_CLIENT40_NUM_PROC \
284 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
286 #define NFS4_CLIENT41_NUM_PROC \
287 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) + \
288 STATIC_ARRAY_SIZE(nfs4_client41_procedures_names))
290 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
295 extern kstat_ctl_t *kc;
296 static kstat_t *nfs2_ksp_client;
297 static kstat_t *nfs2_ksp_server;
298 static kstat_t *nfs3_ksp_client;
299 static kstat_t *nfs3_ksp_server;
300 static kstat_t *nfs4_ksp_client;
301 static kstat_t *nfs4_ksp_server;
304 static int nfs_config(const char *key, const char *value) {
305 if (strcasecmp(key, "ReportV2") == 0)
306 report_v2 = IS_TRUE(value);
307 else if (strcasecmp(key, "ReportV3") == 0)
308 report_v3 = IS_TRUE(value);
309 else if (strcasecmp(key, "ReportV4") == 0)
310 report_v4 = IS_TRUE(value);
318 static int nfs_init(void) { return 0; }
319 /* #endif KERNEL_LINUX */
322 static int nfs_init(void) {
323 nfs2_ksp_client = NULL;
324 nfs2_ksp_server = NULL;
325 nfs3_ksp_client = NULL;
326 nfs3_ksp_server = NULL;
327 nfs4_ksp_client = NULL;
328 nfs4_ksp_server = NULL;
333 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
334 ksp_chain = ksp_chain->ks_next) {
335 if (strncmp(ksp_chain->ks_module, "nfs", 3) != 0)
337 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
338 nfs2_ksp_server = ksp_chain;
339 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
340 nfs3_ksp_server = ksp_chain;
341 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
342 nfs4_ksp_server = ksp_chain;
343 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
344 nfs2_ksp_client = ksp_chain;
345 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
346 nfs3_ksp_client = ksp_chain;
347 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
348 nfs4_ksp_client = ksp_chain;
355 static void nfs_procedures_submit(const char *plugin_instance,
356 const char **type_instances, value_t *values,
358 value_list_t vl = VALUE_LIST_INIT;
361 sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
362 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
363 sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
365 for (size_t i = 0; i < values_num; i++) {
366 vl.values = values + i;
367 sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
368 plugin_dispatch_values(&vl);
370 } /* void nfs_procedures_submit */
373 static void nfs_submit_fields(int nfs_version, const char *instance,
374 char **fields, size_t fields_num,
375 const char **proc_names) {
376 char plugin_instance[DATA_MAX_NAME_LEN];
377 value_t values[fields_num];
379 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
382 for (size_t i = 0; i < fields_num; i++)
383 (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
385 nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
388 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
389 char **fields, size_t fields_num,
390 const char **proc_names,
391 size_t proc_names_num) {
392 if (fields_num != proc_names_num) {
393 WARNING("nfs plugin: Wrong number of fields for "
394 "NFSv%i %s statistics. Expected %zu, got %zu.",
395 nfs_version, instance, proc_names_num, fields_num);
399 nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
404 static int nfs_submit_nfs4_server(const char *instance, char **fields,
406 static int suppress_warning = 0;
408 if (fields_num != NFS4_SERVER40_NUM_PROC &&
409 fields_num != NFS4_SERVER41_NUM_PROC) {
410 if (!suppress_warning) {
411 WARNING("nfs plugin: Unexpected number of fields for "
412 "NFSv4 %s statistics: %zu. ",
413 instance, fields_num);
416 if (fields_num > NFS4_SERVER_MAX_PROC) {
417 fields_num = NFS4_SERVER_MAX_PROC;
418 suppress_warning = 1;
424 nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
425 nfs4_server40_procedures_names);
427 if (fields_num >= NFS4_SERVER41_NUM_PROC) {
428 fields += nfs4_server40_procedures_names_num;
430 nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
431 nfs4_server41_procedures_names);
437 static int nfs_submit_nfs4_client(const char *instance, char **fields,
439 size_t proc40_names_num, proc41_names_num;
441 static int suppress_warning = 0;
443 switch (fields_num) {
449 /* 4.0-only configuration */
450 proc40_names_num = fields_num;
454 proc40_names_num = 35;
458 proc40_names_num = 36;
464 proc40_names_num = 37;
467 proc40_names_num = 38;
470 if (!suppress_warning) {
471 WARNING("nfs plugin: Unexpected number of "
472 "fields for NFSv4 %s "
474 instance, fields_num);
477 if (fields_num > 34) {
478 /* safe fallback to basic nfs40 procedures */
480 proc40_names_num = 34;
482 suppress_warning = 1;
488 nfs_submit_fields(4, instance, fields, proc40_names_num,
489 nfs4_client40_procedures_names);
491 if (fields_num > proc40_names_num) {
492 proc41_names_num = fields_num - proc40_names_num;
493 fields += proc40_names_num;
495 nfs_submit_fields(4, instance, fields, proc41_names_num,
496 nfs4_client41_procedures_names);
502 static void nfs_read_linux(FILE *fh, const char *inst) {
511 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
512 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
517 if (strcmp(fields[0], "proc2") == 0 && report_v2) {
518 nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
519 (size_t)(fields_num - 2), nfs2_procedures_names,
520 nfs2_procedures_names_num);
521 } else if (strncmp(fields[0], "proc3", 5) == 0 && report_v3) {
522 nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
523 (size_t)(fields_num - 2), nfs3_procedures_names,
524 nfs3_procedures_names_num);
525 } else if (strcmp(fields[0], "proc4ops") == 0 && report_v4) {
527 nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
528 } else if (strcmp(fields[0], "proc4") == 0 && report_v4) {
530 nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
532 } /* while (fgets) */
533 } /* void nfs_read_linux */
534 #endif /* KERNEL_LINUX */
537 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
538 char const **proc_names, size_t proc_names_num) {
539 char plugin_instance[DATA_MAX_NAME_LEN];
540 value_t values[proc_names_num];
545 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
548 kstat_read(kc, ksp, NULL);
549 for (size_t i = 0; i < proc_names_num; i++) {
550 /* The name passed to kstat_data_lookup() doesn't have the
551 * "const" modifier, so we need to copy the name here. */
553 sstrncpy(name, proc_names[i], sizeof(name));
555 values[i].counter = (derive_t)get_kstat_value(ksp, name);
558 nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
564 static int nfs_read(void) {
567 if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
568 nfs_read_linux(fh, "client");
572 if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
573 nfs_read_linux(fh, "server");
579 /* #endif KERNEL_LINUX */
582 static int nfs_read(void) {
584 nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
585 nfs2_procedures_names, nfs2_procedures_names_num);
586 nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
587 nfs2_procedures_names, nfs2_procedures_names_num);
590 nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
591 nfs3_procedures_names, nfs3_procedures_names_num);
592 nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
593 nfs3_procedures_names, nfs3_procedures_names_num);
596 nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
597 nfs4_procedures_names, nfs4_procedures_names_num);
598 nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
599 nfs4_procedures_names, nfs4_procedures_names_num);
604 #endif /* HAVE_LIBKSTAT */
606 void module_register(void) {
607 plugin_register_config("nfs", nfs_config, config_keys, config_keys_num);
608 plugin_register_init("nfs", nfs_init);
609 plugin_register_read("nfs", nfs_read);
610 } /* void module_register */