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>
36 see http://www.missioncriticallinux.com/orph/NFS-Statistics
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.
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.
52 Procedure NFS Version NFS Version 3
53 Number Procedures Procedures
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);
95 static const char *nfs4_procedures_names[] = {"null",
131 "setclientid_confirm",
134 static size_t nfs4_procedures_names_num =
135 STATIC_ARRAY_SIZE(nfs4_procedures_names);
139 static const char *nfs4_server40_procedures_names[] = {"null",
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[] = {
185 "bind_conn_to_session",
190 "get_dir_delegation",
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[] = {
231 "setclientid_confirm",
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 */
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 */
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)
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;
299 static int nfs_init(void) { return 0; }
300 /* #endif KERNEL_LINUX */
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;
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)
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;
336 static void nfs_procedures_submit(const char *plugin_instance,
337 const char **type_instances, value_t *values,
339 value_list_t vl = VALUE_LIST_INIT;
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);
351 } /* void nfs_procedures_submit */
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 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
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);
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);
380 nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
385 static int nfs_submit_nfs4_server(const char *instance, char **fields,
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);
397 if (fields_num > NFS4_SERVER_MAX_PROC) {
398 fields_num = NFS4_SERVER_MAX_PROC;
399 suppress_warning = 1;
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);
418 static int nfs_submit_nfs4_client(const char *instance, char **fields,
420 size_t proc40_names_num, proc41_names_num;
422 static int suppress_warning = 0;
424 switch (fields_num) {
430 /* 4.0-only configuration */
431 proc40_names_num = fields_num;
435 proc40_names_num = 35;
439 proc40_names_num = 36;
445 proc40_names_num = 37;
448 proc40_names_num = 38;
451 if (!suppress_warning) {
452 WARNING("nfs plugin: Unexpected number of "
453 "fields for NFSv4 %s "
455 instance, fields_num);
458 if (fields_num > 34) {
459 /* safe fallback to basic nfs40 procedures */
461 proc40_names_num = 34;
463 suppress_warning = 1;
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);
483 static void nfs_read_linux(FILE *fh, const char *inst) {
492 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
493 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
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) {
508 nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
509 } else if (strcmp(fields[0], "proc4") == 0) {
511 nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
513 } /* while (fgets) */
514 } /* void nfs_read_linux */
515 #endif /* KERNEL_LINUX */
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];
526 snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
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. */
534 sstrncpy(name, proc_names[i], sizeof(name));
536 values[i].counter = (derive_t)get_kstat_value(ksp, name);
539 nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
545 static int nfs_read(void) {
548 if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
549 nfs_read_linux(fh, "client");
553 if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
554 nfs_read_linux(fh, "server");
560 /* #endif KERNEL_LINUX */
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);
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 */