NFS: Add version registering framework
[deliverable/linux.git] / fs / nfs / client.c
index 65afa382c5e3583c78a24e3deb907dd8bdfc276a..462de24482b47066ab5e364d4e1227a22bbe407c 100644 (file)
 #include "internal.h"
 #include "fscache.h"
 #include "pnfs.h"
+#include "nfs.h"
 #include "netns.h"
 
 #define NFSDBG_FACILITY                NFSDBG_CLIENT
 
 static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
+static DEFINE_SPINLOCK(nfs_version_lock);
+static DEFINE_MUTEX(nfs_version_mutex);
+static LIST_HEAD(nfs_versions);
 
 /*
  * RPC cruft for NFS
  */
 static const struct rpc_version *nfs_version[5] = {
-#ifdef CONFIG_NFS_V2
-       [2]                     = &nfs_version2,
-#endif
-#ifdef CONFIG_NFS_V3
-       [3]                     = &nfs_version3,
-#endif
-#ifdef CONFIG_NFS_V4
-       [4]                     = &nfs_version4,
-#endif
+       [2] = NULL,
+       [3] = NULL,
+       [4] = NULL,
 };
 
 const struct rpc_program nfs_program = {
@@ -101,6 +99,93 @@ const struct rpc_program nfsacl_program = {
 };
 #endif  /* CONFIG_NFS_V3_ACL */
 
+static struct nfs_subversion *find_nfs_version(unsigned int version)
+{
+       struct nfs_subversion *nfs;
+       spin_lock(&nfs_version_lock);
+
+       list_for_each_entry(nfs, &nfs_versions, list) {
+               if (nfs->rpc_ops->version == version) {
+                       spin_unlock(&nfs_version_lock);
+                       return nfs;
+               }
+       };
+
+       spin_unlock(&nfs_version_lock);
+       return ERR_PTR(-EPROTONOSUPPORT);;
+}
+
+struct nfs_subversion *get_nfs_version(unsigned int version)
+{
+       struct nfs_subversion *nfs = find_nfs_version(version);
+
+       if (IS_ERR(nfs)) {
+               mutex_lock(&nfs_version_mutex);
+               request_module("nfs%d", version);
+               nfs = find_nfs_version(version);
+               mutex_unlock(&nfs_version_mutex);
+       }
+
+       if (!IS_ERR(nfs))
+               try_module_get(nfs->owner);
+       return nfs;
+}
+
+void put_nfs_version(struct nfs_subversion *nfs)
+{
+       module_put(nfs->owner);
+}
+
+void register_nfs_version(struct nfs_subversion *nfs)
+{
+       spin_lock(&nfs_version_lock);
+
+       list_add(&nfs->list, &nfs_versions);
+       nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers;
+
+       spin_unlock(&nfs_version_lock);
+}
+EXPORT_SYMBOL_GPL(register_nfs_version);
+
+void unregister_nfs_version(struct nfs_subversion *nfs)
+{
+       spin_lock(&nfs_version_lock);
+
+       nfs_version[nfs->rpc_ops->version] = NULL;
+       list_del(&nfs->list);
+
+       spin_unlock(&nfs_version_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_nfs_version);
+
+/*
+ * Preload all configured NFS versions during module init.
+ * This function should be edited after each protocol is converted,
+ * and eventually removed.
+ */
+int __init nfs_register_versions(void)
+{
+       int err = init_nfs_v2();
+       if (err)
+               return err;
+
+       err = init_nfs_v3();
+       if (err)
+               return err;
+
+       return init_nfs_v4();
+}
+
+/*
+ * Remove each pre-loaded NFS version
+ */
+void nfs_unregister_versions(void)
+{
+       exit_nfs_v2();
+       exit_nfs_v3();
+       exit_nfs_v4();
+}
+
 /*
  * Allocate a shared client record
  *
@@ -116,7 +201,10 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
        if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
                goto error_0;
 
-       clp->rpc_ops = cl_init->rpc_ops;
+       clp->cl_nfs_mod = cl_init->nfs_mod;
+       try_module_get(clp->cl_nfs_mod->owner);
+
+       clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;
 
        atomic_set(&clp->cl_count, 1);
        clp->cl_cons_state = NFS_CS_INITING;
@@ -145,6 +233,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
        return clp;
 
 error_cleanup:
+       put_nfs_version(clp->cl_nfs_mod);
        kfree(clp);
 error_0:
        return ERR_PTR(err);
@@ -205,6 +294,7 @@ void nfs_free_client(struct nfs_client *clp)
                put_rpccred(clp->cl_machine_cred);
 
        put_net(clp->cl_net);
+       put_nfs_version(clp->cl_nfs_mod);
        kfree(clp->cl_hostname);
        kfree(clp);
 
@@ -362,7 +452,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
                        continue;
 
                /* Different NFS versions cannot share the same nfs_client */
-               if (clp->rpc_ops != data->rpc_ops)
+               if (clp->rpc_ops != data->nfs_mod->rpc_ops)
                        continue;
 
                if (clp->cl_proto != data->proto)
@@ -431,9 +521,10 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
 {
        struct nfs_client *clp, *new = NULL;
        struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
+       const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;
 
        dprintk("--> nfs_get_client(%s,v%u)\n",
-               cl_init->hostname ?: "", cl_init->rpc_ops->version);
+               cl_init->hostname ?: "", rpc_ops->version);
 
        /* see if the client already exists */
        do {
@@ -450,14 +541,13 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
                        list_add(&new->cl_share_link, &nn->nfs_client_list);
                        spin_unlock(&nn->nfs_client_lock);
                        new->cl_flags = cl_init->init_flags;
-                       return cl_init->rpc_ops->init_client(new,
-                                               timeparms, ip_addr,
-                                               authflavour);
+                       return rpc_ops->init_client(new, timeparms, ip_addr,
+                                                   authflavour);
                }
 
                spin_unlock(&nn->nfs_client_lock);
 
-               new = cl_init->rpc_ops->alloc_client(cl_init);
+               new = rpc_ops->alloc_client(cl_init);
        } while (!IS_ERR(new));
 
        dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
@@ -714,13 +804,14 @@ error:
  * Create a version 2 or 3 client
  */
 static int nfs_init_server(struct nfs_server *server,
-                          const struct nfs_parsed_mount_data *data)
+                          const struct nfs_parsed_mount_data *data,
+                          struct nfs_subversion *nfs_mod)
 {
        struct nfs_client_initdata cl_init = {
                .hostname = data->nfs_server.hostname,
                .addr = (const struct sockaddr *)&data->nfs_server.address,
                .addrlen = data->nfs_server.addrlen,
-               .rpc_ops = NULL,
+               .nfs_mod = nfs_mod,
                .proto = data->nfs_server.protocol,
                .net = data->net,
        };
@@ -730,21 +821,6 @@ static int nfs_init_server(struct nfs_server *server,
 
        dprintk("--> nfs_init_server()\n");
 
-       switch (data->version) {
-#ifdef CONFIG_NFS_V2
-       case 2:
-               cl_init.rpc_ops = &nfs_v2_clientops;
-               break;
-#endif
-#ifdef CONFIG_NFS_V3
-       case 3:
-               cl_init.rpc_ops = &nfs_v3_clientops;
-               break;
-#endif
-       default:
-               return -EPROTONOSUPPORT;
-       }
-
        nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
                        data->timeo, data->retrans);
        if (data->flags & NFS_MOUNT_NORESVPORT)
@@ -1033,7 +1109,8 @@ void nfs_free_server(struct nfs_server *server)
  * - keyed on server and FSID
  */
 struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
-                                    struct nfs_fh *mntfh)
+                                    struct nfs_fh *mntfh,
+                                    struct nfs_subversion *nfs_mod)
 {
        struct nfs_server *server;
        struct nfs_fattr *fattr;
@@ -1049,7 +1126,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
                goto error;
 
        /* Get a client representation */
-       error = nfs_init_server(server, data);
+       error = nfs_init_server(server, data, nfs_mod);
        if (error < 0)
                goto error;
 
This page took 0.030617 seconds and 5 git commands to generate.