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[] = {
191 "backchannel_ctl", "bind_conn_to_session", "exchange_id", "create_session",
192 "destroy_session", "free_stateid", "get_dir_delegation", "getdeviceinfo",
193 "getdevicelist", "layoutcommit", "layoutget", "layoutreturn",
194 "secinfo_no_name", "sequence", "set_ssv", "test_stateid", "want_delegation",
195 "destroy_clientid", "reclaim_complete",
197 "allocate", /* 3.18 */
199 "copy_notify", /* 3.18 */
200 "deallocate", /* 3.18 */
201 "ioadvise", /* 3.18 */
202 "layouterror", /* 3.18 */
203 "layoutstats", /* 3.18 */
204 "offloadcancel", /* 3.18 */
205 "offloadstatus", /* 3.18 */
206 "readplus", /* 3.18 */
208 "write_same", /* 3.18 */
212 #define NFS4_SERVER40_NUM_PROC \
213 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
215 #define NFS4_SERVER4X_NUM_PROC \
216 (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) + \
217 STATIC_ARRAY_SIZE(nfs4_server4x_procedures_names))
219 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER4X_NUM_PROC)
221 static const char *nfs4_client40_procedures_names[] = {
235 "setclientid_confirm",
256 "fs_locations", /* |35| 2.6.18 */
257 "release_lockowner", /* |42| 2.6.36 */
258 "secinfo", /* |46| 2.6.39 */
259 "fsid_present" /* |54| 3.13 */
262 static const char *nfs4_client4x_procedures_names[] = {
264 "exchange_id", /* |40| 2.6.30 */
265 "create_session", /* |40| 2.6.30 */
266 "destroy_session", /* |40| 2.6.30 */
267 "sequence", /* |40| 2.6.30 */
268 "get_lease_time", /* |40| 2.6.30 */
269 "reclaim_complete", /* |41| 2.6.33 */
270 "layoutget", /* |44| 2.6.37 */
271 "getdeviceinfo", /* |44| 2.6.37 */
272 "layoutcommit", /* |46| 2.6.39 */
273 "layoutreturn", /* |47| 3.0 */
274 "secinfo_no_name", /* |51| 3.1 */
275 "test_stateid", /* |51| 3.1 */
276 "free_stateid", /* |51| 3.1 */
277 "getdevicelist", /* |51| 3.1 */
278 "bind_conn_to_session", /* |53| 3.5 */
279 "destroy_clientid", /* |53| 3.5 */
281 "seek", /* |55| 3.18 */
282 "allocate", /* |57| 3.19 */
283 "deallocate", /* |57| 3.19 */
284 "layoutstats", /* |58| 4.2 */
285 "clone", /* |59| 4.4 */
286 "copy" /* |60| 4.7 */
289 #define NFS4_CLIENT40_NUM_PROC \
290 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
292 #define NFS4_CLIENT4X_NUM_PROC \
293 (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) + \
294 STATIC_ARRAY_SIZE(nfs4_client4x_procedures_names))
296 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT4X_NUM_PROC)
301 extern kstat_ctl_t *kc;
302 static kstat_t *nfs2_ksp_client;
303 static kstat_t *nfs2_ksp_server;
304 static kstat_t *nfs3_ksp_client;
305 static kstat_t *nfs3_ksp_server;
306 static kstat_t *nfs4_ksp_client;
307 static kstat_t *nfs4_ksp_server;
310 static int nfs_config(const char *key, const char *value) {
311 if (strcasecmp(key, "ReportV2") == 0)
312 report_v2 = IS_TRUE(value);
313 else if (strcasecmp(key, "ReportV3") == 0)
314 report_v3 = IS_TRUE(value);
315 else if (strcasecmp(key, "ReportV4") == 0)
316 report_v4 = IS_TRUE(value);
324 static int nfs_init(void) { return 0; }
325 /* #endif KERNEL_LINUX */
328 static int nfs_init(void) {
329 nfs2_ksp_client = NULL;
330 nfs2_ksp_server = NULL;
331 nfs3_ksp_client = NULL;
332 nfs3_ksp_server = NULL;
333 nfs4_ksp_client = NULL;
334 nfs4_ksp_server = NULL;
339 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
340 ksp_chain = ksp_chain->ks_next) {
341 if (strncmp(ksp_chain->ks_module, "nfs", 3) != 0)
343 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
344 nfs2_ksp_server = ksp_chain;
345 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
346 nfs3_ksp_server = ksp_chain;
347 else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
348 nfs4_ksp_server = ksp_chain;
349 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
350 nfs2_ksp_client = ksp_chain;
351 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
352 nfs3_ksp_client = ksp_chain;
353 else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
354 nfs4_ksp_client = ksp_chain;
361 static void nfs_procedures_submit(const char *plugin_instance,
362 const char **type_instances, value_t *values,
364 value_list_t vl = VALUE_LIST_INIT;
367 sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
368 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
369 sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
371 for (size_t i = 0; i < values_num; i++) {
372 vl.values = values + i;
373 sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
374 plugin_dispatch_values(&vl);
376 } /* void nfs_procedures_submit */
379 static void nfs_submit_fields(int nfs_version, const char *instance,
380 char **fields, size_t fields_num,
381 const char **proc_names) {
382 char plugin_instance[DATA_MAX_NAME_LEN];
383 value_t values[fields_num];
385 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
388 for (size_t i = 0; i < fields_num; i++)
389 (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
391 nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
394 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
395 char **fields, size_t fields_num,
396 const char **proc_names,
397 size_t proc_names_num) {
398 if (fields_num != proc_names_num) {
399 WARNING("nfs plugin: Wrong number of fields for "
400 "NFSv%i %s statistics. Expected %" PRIsz ", got %" PRIsz ".",
401 nfs_version, instance, proc_names_num, fields_num);
405 nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
410 static int nfs_submit_nfs4_server(const char *instance, char **fields,
412 static int suppress_warning = 0;
413 size_t proc4x_names_num;
415 switch (fields_num) {
416 case NFS4_SERVER40_NUM_PROC:
417 case NFS4_SERVER40_NUM_PROC + 19: /* NFS 4.1 */
418 case NFS4_SERVER40_NUM_PROC + 31: /* NFS 4.2 */
419 case NFS4_SERVER40_NUM_PROC + 32: /* NFS 4.2 */
422 if (!suppress_warning) {
423 WARNING("nfs plugin: Unexpected number of fields for "
424 "NFSv4 %s statistics: %" PRIsz ". ",
425 instance, fields_num);
428 if (fields_num > NFS4_SERVER_MAX_PROC) {
429 fields_num = NFS4_SERVER_MAX_PROC;
430 suppress_warning = 1;
436 nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
437 nfs4_server40_procedures_names);
439 if (fields_num > nfs4_server40_procedures_names_num) {
440 proc4x_names_num = fields_num - nfs4_server40_procedures_names_num;
441 fields += nfs4_server40_procedures_names_num;
443 nfs_submit_fields(4, instance, fields, proc4x_names_num,
444 nfs4_server4x_procedures_names);
450 static int nfs_submit_nfs4_client(const char *instance, char **fields,
452 size_t proc40_names_num, proc4x_names_num;
454 static int suppress_warning = 0;
456 switch (fields_num) {
462 /* 4.0-only configuration */
463 proc40_names_num = fields_num;
467 proc40_names_num = 35;
471 proc40_names_num = 36;
477 proc40_names_num = 37;
485 proc40_names_num = 38;
488 if (!suppress_warning) {
489 WARNING("nfs plugin: Unexpected number of fields for NFSv4 %s "
490 "statistics: %" PRIsz ". ",
491 instance, fields_num);
494 if (fields_num > 34) {
495 /* safe fallback to basic nfs40 procedures */
497 proc40_names_num = 34;
499 suppress_warning = 1;
505 nfs_submit_fields(4, instance, fields, proc40_names_num,
506 nfs4_client40_procedures_names);
508 if (fields_num > proc40_names_num) {
509 proc4x_names_num = fields_num - proc40_names_num;
510 fields += proc40_names_num;
512 nfs_submit_fields(4, instance, fields, proc4x_names_num,
513 nfs4_client4x_procedures_names);
519 static void nfs_read_linux(FILE *fh, const char *inst) {
528 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
529 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
534 if (strcmp(fields[0], "proc2") == 0 && report_v2) {
535 nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
536 (size_t)(fields_num - 2), nfs2_procedures_names,
537 nfs2_procedures_names_num);
538 } else if (strncmp(fields[0], "proc3", 5) == 0 && report_v3) {
539 nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
540 (size_t)(fields_num - 2), nfs3_procedures_names,
541 nfs3_procedures_names_num);
542 } else if (strcmp(fields[0], "proc4ops") == 0 && report_v4) {
544 nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
545 } else if (strcmp(fields[0], "proc4") == 0 && report_v4) {
547 nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
549 } /* while (fgets) */
550 } /* void nfs_read_linux */
551 #endif /* KERNEL_LINUX */
554 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
555 char const **proc_names, size_t proc_names_num) {
556 char plugin_instance[DATA_MAX_NAME_LEN];
557 value_t values[proc_names_num];
562 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
565 kstat_read(kc, ksp, NULL);
566 for (size_t i = 0; i < proc_names_num; i++) {
567 /* The name passed to kstat_data_lookup() doesn't have the
568 * "const" modifier, so we need to copy the name here. */
570 sstrncpy(name, proc_names[i], sizeof(name));
572 values[i].counter = (derive_t)get_kstat_value(ksp, name);
575 nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
581 static int nfs_read(void) {
584 if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
585 nfs_read_linux(fh, "client");
589 if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
590 nfs_read_linux(fh, "server");
596 /* #endif KERNEL_LINUX */
599 static int nfs_read(void) {
601 nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
602 nfs2_procedures_names, nfs2_procedures_names_num);
603 nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
604 nfs2_procedures_names, nfs2_procedures_names_num);
607 nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
608 nfs3_procedures_names, nfs3_procedures_names_num);
609 nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
610 nfs3_procedures_names, nfs3_procedures_names_num);
613 nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
614 nfs4_procedures_names, nfs4_procedures_names_num);
615 nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
616 nfs4_procedures_names, nfs4_procedures_names_num);
621 #endif /* HAVE_LIBKSTAT */
623 void module_register(void) {
624 plugin_register_config("nfs", nfs_config, config_keys, config_keys_num);
625 plugin_register_init("nfs", nfs_init);
626 plugin_register_read("nfs", nfs_read);
627 } /* void module_register */