Added nfs4 support for linux
authorMarek Becka <marek.becka@superhosting.cz>
Sun, 9 Feb 2014 21:14:44 +0000 (22:14 +0100)
committerMarek Becka <marek.becka@superhosting.cz>
Sun, 9 Feb 2014 21:14:44 +0000 (22:14 +0100)
src/nfs.c

index f5b579e..ac21165 100644 (file)
--- a/src/nfs.c
+++ b/src/nfs.c
@@ -171,6 +171,160 @@ static const char *nfs4_procedures_names[] =
 static size_t nfs4_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_procedures_names);
 #endif
 
+#if KERNEL_LINUX
+static const char *nfs4_server40_procedures_names[] =
+{
+       "null",
+       "compound",
+       "reserved",
+       "access",
+       "close",
+       "commit",
+       "create",
+       "delegpurge",
+       "delegreturn",
+       "getattr",
+       "getfh",
+       "link",
+       "lock",
+       "lockt",
+       "locku",
+       "lookup",
+       "lookupp",
+       "nverify",
+       "open",
+       "openattr",
+       "open_confirm",
+       "open_downgrade",
+       "putfh",
+       "putpubfh",
+       "putrootfh",
+       "read",
+       "readdir",
+       "readlink",
+       "remove",
+       "rename",
+       "renew",
+       "restorefh",
+       "savefh",
+       "secinfo",
+       "setattr",
+       "setclientid",
+       "setcltid_confirm",
+       "verify",
+       "write",
+       "release_lockowner"
+};
+
+static size_t nfs4_server40_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server40_procedures_names);
+
+static const char *nfs4_server41_procedures_names[] =
+{
+       "backchannel_ctl",
+       "bind_conn_to_session", 
+       "exchange_id",
+       "create_session",
+       "destroy_session",
+       "free_stateid",
+       "get_dir_delegation",
+       "getdeviceinfo",
+       "getdevicelist",
+       "layoutcommit",
+       "layoutget",
+       "layoutreturn",
+       "secinfo_no_name",
+       "sequence",
+       "set_ssv",
+       "test_stateid",
+       "want_delegation",
+       "destroy_clientid",
+       "reclaim_complete",
+};
+
+static size_t nfs4_server41_procedures_names_num = STATIC_ARRAY_SIZE (nfs4_server41_procedures_names);
+
+#define NFS4_SERVER40_NUM_PROC ( \
+       STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) )
+
+#define NFS4_SERVER41_NUM_PROC ( \
+       STATIC_ARRAY_SIZE (nfs4_server40_procedures_names) + \
+       STATIC_ARRAY_SIZE (nfs4_server41_procedures_names) )
+
+#define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
+
+static const char *nfs4_client40_procedures_names[] =
+{
+       "null",
+       "read",
+       "write",
+       "commit",
+       "open",
+       "open_confirm",
+       "open_noattr",
+       "open_downgrade",
+       "close",
+       "setattr",
+       "fsinfo",
+       "renew",
+       "setclientid",
+       "setclientid_confirm",
+       "lock",
+       "lockt",
+       "locku",
+       "access",
+       "getattr",
+       "lookup",
+       "lookupp",
+       "remove",
+       "rename",
+       "link",
+       "symlink",
+       "create",
+       "pathconf",
+       "statfs",
+       "readlink",
+       "readdir",
+       "server_caps",
+       "delegreturn",
+       "getacl",
+       "setacl",
+       "fs_locations",         /* |35| 2.6.18 */
+       "release_lockowner",    /* |42| 2.6.36 */
+       "secinfo",              /* |46| 2.6.39 */
+       "fsid_present"          /* |54| 3.13 */
+};
+
+static const char *nfs4_client41_procedures_names[] =
+{
+       "exchange_id",          /* |40| 3.6.30 */
+       "create_session",       /* |40| 3.6.30 */
+       "destroy_session",      /* |40| 3.6.30 */
+       "sequence",             /* |40| 3.6.30 */
+       "get_lease_time",       /* |40| 3.6.30 */
+       "reclaim_complete",     /* |41| 2.6.33 */
+       "layoutget",            /* |44| 2.6.37 */
+       "getdeviceinfo",        /* |44| 2.6.37 */
+       "layoutcommit",         /* |46| 2.6.39 */
+       "layoutreturn",         /* |47| 3.0 */
+       "secinfo_no_name",      /* |51| 3.1 */
+       "test_stateid",         /* |51| 3.1 */
+       "free_stateid",         /* |51| 3.1 */
+       "getdevicelist",        /* |51| 3.1 */
+       "bind_conn_to_session", /* |53| 3.5 */
+       "destroy_clientid"      /* |53| 3.5 */
+};
+
+#define NFS4_CLIENT40_NUM_PROC ( \
+       STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) )
+
+#define NFS4_CLIENT41_NUM_PROC ( \
+       STATIC_ARRAY_SIZE (nfs4_client40_procedures_names) + \
+       STATIC_ARRAY_SIZE (nfs4_client41_procedures_names) )
+
+#define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
+
+#endif
+
 #if HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 static kstat_t *nfs2_ksp_client;
@@ -181,8 +335,6 @@ static kstat_t *nfs4_ksp_client;
 static kstat_t *nfs4_ksp_server;
 #endif
 
-/* Possibly TODO: NFSv4 statistics */
-
 #if KERNEL_LINUX
 static int nfs_init (void)
 {
@@ -252,14 +404,27 @@ static void nfs_procedures_submit (const char *plugin_instance,
 } /* void nfs_procedures_submit */
 
 #if KERNEL_LINUX
-static int nfs_submit_fields (int nfs_version, const char *instance,
-               char **fields, size_t fields_num,
-               const char **proc_names, size_t proc_names_num)
+static void nfs_submit_fields (int nfs_version, const char *instance, 
+               char **fields, size_t fields_num, const char **proc_names)
 {
        char plugin_instance[DATA_MAX_NAME_LEN];
        value_t values[fields_num];
        size_t i;
 
+       ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
+                       nfs_version, instance);
+
+       for (i = 0; i < fields_num; i++)
+               (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
+
+       nfs_procedures_submit (plugin_instance, proc_names, values,
+                       fields_num);
+}
+
+static int nfs_submit_fields_safe (int nfs_version, const char *instance,
+               char **fields, size_t fields_num,
+               const char **proc_names, size_t proc_names_num)
+{
        if (fields_num != proc_names_num)
        {
                WARNING ("nfs plugin: Wrong number of fields for "
@@ -269,14 +434,130 @@ static int nfs_submit_fields (int nfs_version, const char *instance,
                return (EINVAL);
        }
 
-       ssnprintf (plugin_instance, sizeof (plugin_instance), "v%i%s",
-                       nfs_version, instance);
+       nfs_submit_fields (nfs_version, instance, fields, fields_num, 
+                       proc_names);
 
-       for (i = 0; i < proc_names_num; i++)
-               (void) parse_value (fields[i], &values[i], DS_TYPE_DERIVE);
+       return (0);
+}
 
-       nfs_procedures_submit (plugin_instance, proc_names, values,
-                       proc_names_num);
+static int nfs_submit_nfs4_server (const char *instance, char **fields, 
+               size_t fields_num)
+{
+       static int suppress_warning = 0;
+
+       if (fields_num != NFS4_SERVER40_NUM_PROC &&
+               fields_num != NFS4_SERVER41_NUM_PROC) 
+       {
+               if (!suppress_warning)
+               {
+                       WARNING ("nfs plugin: Unexpected number of fields for "
+                                       "NFSv4 %s statistics: %zu. ",
+                                       instance, fields_num);
+               }
+
+               if (fields_num > NFS4_SERVER_MAX_PROC)
+               {
+                       fields_num = NFS4_SERVER_MAX_PROC;
+                       suppress_warning = 1;
+               }
+               else
+               {
+                       return (EINVAL);
+               }
+       }
+
+        nfs_submit_fields (4, instance, fields, 
+                       nfs4_server40_procedures_names_num,
+                       nfs4_server40_procedures_names);
+
+       if (fields_num >= NFS4_SERVER41_NUM_PROC)
+       {
+               fields += nfs4_server40_procedures_names_num;
+
+               nfs_submit_fields (4, instance, fields, 
+                               nfs4_server41_procedures_names_num, 
+                               nfs4_server41_procedures_names);
+       }
+
+       return (0);
+}
+
+static int nfs_submit_nfs4_client (const char *instance, char **fields, 
+               size_t fields_num)
+{
+       size_t proc40_names_num, proc41_names_num;
+
+       static int suppress_warning = 0;
+
+       switch (fields_num)
+       {
+               case 34:
+                       proc40_names_num = 34;
+                       break;
+               case 35:
+                       proc40_names_num = 35;
+                       break;
+               case 40:
+                       proc40_names_num = 35;
+                       break;
+               case 41:
+                       proc40_names_num = 35;
+                       break;
+               case 42:
+                       proc40_names_num = 36;
+                       break;
+               case 44:
+                       proc40_names_num = 36;
+                       break;
+               case 46:
+                       proc40_names_num = 36;
+                       break;
+               case 47:
+                       proc40_names_num = 36;
+                       break;
+               case 51:
+                       proc40_names_num = 36;
+                       break;
+               case 53:
+                       proc40_names_num = 36;
+                       break;
+               case 54:
+                       proc40_names_num = 37;
+                       break;
+               default:
+                       if (!suppress_warning)
+                       {
+                               WARNING ("nfs plugin: Unexpected number of "
+                                               "fields for NFSv4 %s "
+                                               "statistics: %zu. ",
+                                               instance, fields_num);
+                       }
+
+                       if (fields_num > 34)
+                       {
+                               /* safe fallback to basic nfs40 procedures */
+                               fields_num = 34;
+                               proc40_names_num = 34;
+
+                               suppress_warning = 1;
+                       }
+                       else
+                       {
+                               return (EINVAL);
+                       }
+       }
+
+       nfs_submit_fields (4, instance, fields, proc40_names_num,
+                       nfs4_client40_procedures_names);
+
+       if (fields_num > proc40_names_num)
+       {
+               proc41_names_num = fields_num - proc40_names_num;
+               fields += proc40_names_num;
+
+               nfs_submit_fields (4, instance, fields,proc41_names_num,
+                                nfs4_client41_procedures_names);
+       }
 
        return (0);
 }
@@ -285,7 +566,7 @@ static void nfs_read_linux (FILE *fh, char *inst)
 {
        char buffer[1024];
 
-       char *fields[48];
+       char *fields[64];
        int fields_num = 0;
 
        if (fh == NULL)
@@ -301,18 +582,30 @@ static void nfs_read_linux (FILE *fh, char *inst)
 
                if (strcmp (fields[0], "proc2") == 0)
                {
-                       nfs_submit_fields (/* version = */ 2, inst,
+                       nfs_submit_fields_safe (/* version = */ 2, inst,
                                        fields + 2, (size_t) (fields_num - 2),
                                        nfs2_procedures_names,
                                        nfs2_procedures_names_num);
                }
                else if (strncmp (fields[0], "proc3", 5) == 0)
                {
-                       nfs_submit_fields (/* version = */ 3, inst,
+                       nfs_submit_fields_safe (/* version = */ 3, inst,
                                        fields + 2, (size_t) (fields_num - 2),
                                        nfs3_procedures_names,
                                        nfs3_procedures_names_num);
                }
+               else if (strcmp (fields[0], "proc4ops") == 0)
+               {
+                       if (inst[0] == 's')
+                               nfs_submit_nfs4_server (inst, fields + 2, 
+                                               (size_t) (fields_num - 2));
+               }
+               else if (strcmp (fields[0], "proc4") == 0)
+               {
+                       if (inst[0] == 'c')
+                               nfs_submit_nfs4_client (inst, fields + 2,
+                                               (size_t) (fields_num - 2));                     
+               }
        } /* while (fgets) */
 } /* void nfs_read_linux */
 #endif /* KERNEL_LINUX */