Merge tag 'nfs-for-3.4-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
[cascardo/linux.git] / fs / nfs / client.c
index 1a5cd49..4a108a0 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/inet.h>
 #include <linux/in6.h>
 #include <linux/slab.h>
+#include <linux/idr.h>
 #include <net/ipv6.h>
 #include <linux/nfs_xdr.h>
 #include <linux/sunrpc/bc_xprt.h>
@@ -172,7 +173,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        clp->cl_rpcclient = ERR_PTR(-EINVAL);
 
        clp->cl_proto = cl_init->proto;
-       clp->net = cl_init->net;
+       clp->net = get_net(cl_init->net);
 
 #ifdef CONFIG_NFS_V4
        err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
@@ -204,8 +205,11 @@ error_0:
 #ifdef CONFIG_NFS_V4_1
 static void nfs4_shutdown_session(struct nfs_client *clp)
 {
-       if (nfs4_has_session(clp))
+       if (nfs4_has_session(clp)) {
+               nfs4_deviceid_purge_client(clp);
                nfs4_destroy_session(clp->cl_session);
+       }
+
 }
 #else /* CONFIG_NFS_V4_1 */
 static void nfs4_shutdown_session(struct nfs_client *clp)
@@ -298,10 +302,10 @@ static void nfs_free_client(struct nfs_client *clp)
        if (clp->cl_machine_cred != NULL)
                put_rpccred(clp->cl_machine_cred);
 
-       nfs4_deviceid_purge_client(clp);
-
+       put_net(clp->net);
        kfree(clp->cl_hostname);
        kfree(clp->server_scope);
+       kfree(clp->impl_id);
        kfree(clp);
 
        dprintk("<-- nfs_free_client()\n");
@@ -401,6 +405,7 @@ static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1,
                (sin1->sin_port == sin2->sin_port);
 }
 
+#if defined(CONFIG_NFS_V4_1)
 /*
  * Test if two socket addresses represent the same actual socket,
  * by comparing (only) relevant fields, excluding the port number.
@@ -419,6 +424,7 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
        }
        return 0;
 }
+#endif /* CONFIG_NFS_V4_1 */
 
 /*
  * Test if two socket addresses represent the same actual socket,
@@ -439,10 +445,10 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
        return 0;
 }
 
+#if defined(CONFIG_NFS_V4_1)
 /* Common match routine for v4.0 and v4.1 callback services */
-bool
-nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
-                    u32 minorversion)
+static bool nfs4_cb_match_client(const struct sockaddr *addr,
+               struct nfs_client *clp, u32 minorversion)
 {
        struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
 
@@ -462,6 +468,7 @@ nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
 
        return true;
 }
+#endif /* CONFIG_NFS_V4_1 */
 
 /*
  * Find an nfs_client on the list that matches the initialisation data
@@ -1055,11 +1062,14 @@ static void nfs_server_insert_lists(struct nfs_server *server)
 static void nfs_server_remove_lists(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
-       struct nfs_net *nn = net_generic(clp->net, nfs_net_id);
+       struct nfs_net *nn;
 
+       if (clp == NULL)
+               return;
+       nn = net_generic(clp->net, nfs_net_id);
        spin_lock(&nn->nfs_client_lock);
        list_del_rcu(&server->client_link);
-       if (clp && list_empty(&clp->cl_superblocks))
+       if (list_empty(&clp->cl_superblocks))
                set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state);
        list_del(&server->master_link);
        spin_unlock(&nn->nfs_client_lock);
@@ -1263,7 +1273,7 @@ nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
 #else /* CONFIG_NFS_V4_1 */
 
 struct nfs_client *
-nfs4_find_client_sessionid(const struct sockaddr *addr,
+nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
                           struct nfs4_sessionid *sid)
 {
        return NULL;
@@ -1278,16 +1288,18 @@ static int nfs4_init_callback(struct nfs_client *clp)
        int error;
 
        if (clp->rpc_ops->version == 4) {
+               struct rpc_xprt *xprt;
+
+               xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
+
                if (nfs4_has_session(clp)) {
-                       error = xprt_setup_backchannel(
-                                               clp->cl_rpcclient->cl_xprt,
+                       error = xprt_setup_backchannel(xprt,
                                                NFS41_BC_MIN_CALLBACKS);
                        if (error < 0)
                                return error;
                }
 
-               error = nfs_callback_up(clp->cl_mvops->minor_version,
-                                       clp->cl_rpcclient->cl_xprt);
+               error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
                if (error < 0) {
                        dprintk("%s: failed to start callback. Error = %d\n",
                                __func__, error);
@@ -1338,6 +1350,7 @@ int nfs4_init_client(struct nfs_client *clp,
                     rpc_authflavor_t authflavour,
                     int noresvport)
 {
+       char buf[INET6_ADDRSTRLEN + 1];
        int error;
 
        if (clp->cl_cons_state == NFS_CS_READY) {
@@ -1353,6 +1366,20 @@ int nfs4_init_client(struct nfs_client *clp,
                                      1, noresvport);
        if (error < 0)
                goto error;
+
+       /* If no clientaddr= option was specified, find a usable cb address */
+       if (ip_addr == NULL) {
+               struct sockaddr_storage cb_addr;
+               struct sockaddr *sap = (struct sockaddr *)&cb_addr;
+
+               error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
+               if (error < 0)
+                       goto error;
+               error = rpc_ntop(sap, buf, sizeof(buf));
+               if (error < 0)
+                       goto error;
+               ip_addr = (const char *)buf;
+       }
        strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
 
        error = nfs_idmap_new(clp);
@@ -1672,7 +1699,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
                                data->addrlen,
                                parent_client->cl_ipaddr,
                                data->authflavor,
-                               parent_server->client->cl_xprt->prot,
+                               rpc_protocol(parent_server->client),
                                parent_server->client->cl_timeout,
                                parent_client->cl_mvops->minor_version,
                                parent_client->net);
@@ -1777,6 +1804,7 @@ void nfs_clients_init(struct net *net)
 #ifdef CONFIG_NFS_V4
        idr_init(&nn->cb_ident_idr);
 #endif
+       spin_lock_init(&nn->nfs_client_lock);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -1898,12 +1926,14 @@ static int nfs_server_list_show(struct seq_file *m, void *v)
        if (clp->cl_cons_state != NFS_CS_READY)
                return 0;
 
+       rcu_read_lock();
        seq_printf(m, "v%u %s %s %3d %s\n",
                   clp->rpc_ops->version,
                   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
                   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
                   atomic_read(&clp->cl_count),
                   clp->cl_hostname);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -1986,6 +2016,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
                 (unsigned long long) server->fsid.major,
                 (unsigned long long) server->fsid.minor);
 
+       rcu_read_lock();
        seq_printf(m, "v%u %s %s %-7s %-17s %s\n",
                   clp->rpc_ops->version,
                   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
@@ -1993,6 +2024,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)
                   dev,
                   fsid,
                   nfs_server_fscache_state(server));
+       rcu_read_unlock();
 
        return 0;
 }