SUNRPC: Use RCU to dereference the rpc_clnt.cl_xprt field
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 1 Mar 2012 22:00:56 +0000 (17:00 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 2 Mar 2012 20:36:38 +0000 (15:36 -0500)
A migration event will replace the rpc_xprt used by an rpc_clnt.  To
ensure this can be done safely, all references to cl_xprt must now use
a form of rcu_dereference().

Special care is taken with rpc_peeraddr2str(), which returns a pointer
to memory whose lifetime is the same as the rpc_xprt.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
[ cel: fix lockdep splats and layering violations ]
[ cel: forward ported to 3.4 ]
[ cel: remove rpc_max_reqs(), add rpc_net_ns() ]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
13 files changed:
fs/nfs/callback_proc.c
fs/nfs/client.c
fs/nfs/nfs4namespace.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/super.c
include/linux/sunrpc/clnt.h
include/linux/sunrpc/debug.h
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/stats.c

index 0e0865e380653f1c41ac76c750dfa05bec2a2691..1bb297243624313bf94e61cb8ac530b4a02068ba 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/slab.h>
+#include <linux/rcupdate.h>
 #include "nfs4_fs.h"
 #include "callback.h"
 #include "delegation.h"
@@ -33,7 +34,7 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
        res->bitmap[0] = res->bitmap[1] = 0;
        res->status = htonl(NFS4ERR_BADHANDLE);
 
-       dprintk("NFS: GETATTR callback request from %s\n",
+       dprintk_rcu("NFS: GETATTR callback request from %s\n",
                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
 
        inode = nfs_delegation_find_inode(cps->clp, &args->fh);
@@ -73,7 +74,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
        if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
                goto out;
 
-       dprintk("NFS: RECALL callback request from %s\n",
+       dprintk_rcu("NFS: RECALL callback request from %s\n",
                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
 
        res = htonl(NFS4ERR_BADHANDLE);
@@ -533,7 +534,7 @@ __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
        if (!cps->clp) /* set in cb_sequence */
                goto out;
 
-       dprintk("NFS: RECALL_ANY callback request from %s\n",
+       dprintk_rcu("NFS: RECALL_ANY callback request from %s\n",
                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
 
        status = cpu_to_be32(NFS4ERR_INVAL);
@@ -568,7 +569,7 @@ __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
        if (!cps->clp) /* set in cb_sequence */
                goto out;
 
-       dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
+       dprintk_rcu("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
                rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
                args->crsa_target_max_slots);
 
index 1506adf4d4edd3d76f86d503481c380f5813e598..d038dc5916e552881f7983010d8341585c93d314 100644 (file)
@@ -1284,16 +1284,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);
@@ -1678,7 +1680,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);
@@ -1905,12 +1907,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;
 }
@@ -1993,6 +1997,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),
@@ -2000,6 +2005,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;
 }
index 667ea7406fd35b154fde443b57b7ac143dff5561..9c8eca315f431199aa481c0eabc6ae3e044f8267 100644 (file)
@@ -96,8 +96,8 @@ static int nfs4_validate_fspath(struct dentry *dentry,
 static size_t nfs_parse_server_name(char *string, size_t len,
                struct sockaddr *sa, size_t salen, struct nfs_server *server)
 {
+       struct net *net = rpc_net_ns(server->client);
        ssize_t ret;
-       struct net *net = server->client->cl_xprt->xprt_net;
 
        ret = rpc_pton(net, string, len, sa, salen);
        if (ret == 0) {
index 6c8e170e2e6bf941962255e6db0566c31bfece57..671510cc14c0cd0641ac472c7c07a396bf512428 100644 (file)
@@ -3833,6 +3833,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
        *p = htonl((u32)clp->cl_boot_time.tv_nsec);
 
        for(;;) {
+               rcu_read_lock();
                setclientid.sc_name_len = scnprintf(setclientid.sc_name,
                                sizeof(setclientid.sc_name), "%s/%s %s %s %u",
                                clp->cl_ipaddr,
@@ -3849,6 +3850,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
                setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
                                sizeof(setclientid.sc_uaddr), "%s.%u.%u",
                                clp->cl_ipaddr, port >> 8, port & 255);
+               rcu_read_unlock();
 
                status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
                if (status != -NFS4ERR_CLID_INUSE)
@@ -5244,11 +5246,16 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
 
 void nfs4_destroy_session(struct nfs4_session *session)
 {
+       struct rpc_xprt *xprt;
+
        nfs4_proc_destroy_session(session);
+
+       rcu_read_lock();
+       xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt);
+       rcu_read_unlock();
        dprintk("%s Destroy backchannel for xprt %p\n",
-               __func__, session->clp->cl_rpcclient->cl_xprt);
-       xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt,
-                               NFS41_BC_MIN_CALLBACKS);
+               __func__, xprt);
+       xprt_destroy_backchannel(xprt, NFS41_BC_MIN_CALLBACKS);
        nfs4_destroy_slot_tables(session);
        kfree(session);
 }
index c1111a37dc1447646aba98ef352aa77c8af33a29..bae959e294cdee745a4940308d63771b02e4ede2 100644 (file)
@@ -1037,19 +1037,28 @@ static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
 void nfs4_schedule_state_manager(struct nfs_client *clp)
 {
        struct task_struct *task;
+       char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
 
        if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
                return;
        __module_get(THIS_MODULE);
        atomic_inc(&clp->cl_count);
-       task = kthread_run(nfs4_run_state_manager, clp, "%s-manager",
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_ADDR));
-       if (!IS_ERR(task))
-               return;
-       nfs4_clear_state_manager_bit(clp);
-       nfs_put_client(clp);
-       module_put(THIS_MODULE);
+
+       /* The rcu_read_lock() is not strictly necessary, as the state
+        * manager is the only thread that ever changes the rpc_xprt
+        * after it's initialized.  At this point, we're single threaded. */
+       rcu_read_lock();
+       snprintf(buf, sizeof(buf), "%s-manager",
+                       rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+       rcu_read_unlock();
+       task = kthread_run(nfs4_run_state_manager, clp, buf);
+       if (IS_ERR(task)) {
+               printk(KERN_ERR "%s: kthread_run: %ld\n",
+                       __func__, PTR_ERR(task));
+               nfs4_clear_state_manager_bit(clp);
+               nfs_put_client(clp);
+               module_put(THIS_MODULE);
+       }
 }
 
 /*
index f4ccdae6a0cfef40bc5608671acba4e3178e265b..7002be11d99f99c49d64279571d8db155eba8139 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/magic.h>
 #include <linux/parser.h>
 #include <linux/nsproxy.h>
+#include <linux/rcupdate.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -701,8 +702,10 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
                else
                        seq_puts(m, nfs_infop->nostr);
        }
+       rcu_read_lock();
        seq_printf(m, ",proto=%s",
                   rpc_peeraddr2str(nfss->client, RPC_DISPLAY_NETID));
+       rcu_read_unlock();
        if (version == 4) {
                if (nfss->port != NFS_PORT)
                        seq_printf(m, ",port=%u", nfss->port);
@@ -751,9 +754,11 @@ static int nfs_show_options(struct seq_file *m, struct dentry *root)
 
        nfs_show_mount_options(m, nfss, 0);
 
+       rcu_read_lock();
        seq_printf(m, ",addr=%s",
                        rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient,
                                                        RPC_DISPLAY_ADDR));
+       rcu_read_unlock();
 
        return 0;
 }
index a4c62e95c720c3afdeed2788c7d1ae333d7b1d81..e3d12b4a0314a3b79d1a79a96d482bfec8fa3c39 100644 (file)
@@ -35,7 +35,7 @@ struct rpc_clnt {
        struct list_head        cl_clients;     /* Global list of clients */
        struct list_head        cl_tasks;       /* List of tasks */
        spinlock_t              cl_lock;        /* spinlock */
-       struct rpc_xprt *       cl_xprt;        /* transport */
+       struct rpc_xprt __rcu * cl_xprt;        /* transport */
        struct rpc_procinfo *   cl_procinfo;    /* procedure info */
        u32                     cl_prog,        /* RPC program number */
                                cl_vers,        /* RPC version number */
@@ -156,6 +156,8 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
 int            rpc_restart_call_prepare(struct rpc_task *);
 int            rpc_restart_call(struct rpc_task *);
 void           rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
+int            rpc_protocol(struct rpc_clnt *);
+struct net *   rpc_net_ns(struct rpc_clnt *);
 size_t         rpc_max_payload(struct rpc_clnt *);
 void           rpc_force_rebind(struct rpc_clnt *);
 size_t         rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
index b506936f4ce68e1d29e4cab487152d751b06a65b..6cb2517bcf759fe130a765e14ff63bab156f6a5e 100644 (file)
@@ -50,19 +50,32 @@ extern unsigned int         nlm_debug;
 #endif
 
 #define dprintk(args...)       dfprintk(FACILITY, ## args)
+#define dprintk_rcu(args...)   dfprintk_rcu(FACILITY, ## args)
 
 #undef ifdebug
 #ifdef RPC_DEBUG                       
 # define ifdebug(fac)          if (unlikely(rpc_debug & RPCDBG_##fac))
+
 # define dfprintk(fac, args...)        \
        do { \
                ifdebug(fac) \
                        printk(KERN_DEFAULT args); \
        } while (0)
+
+# define dfprintk_rcu(fac, args...)    \
+       do { \
+               ifdebug(fac) { \
+                       rcu_read_lock(); \
+                       printk(KERN_DEFAULT args); \
+                       rcu_read_unlock(); \
+               } \
+       } while (0)
+
 # define RPC_IFDEBUG(x)                x
 #else
 # define ifdebug(fac)          if (0)
 # define dfprintk(fac, args...)        do ; while (0)
+# define dfprintk_rcu(fac, args...)    do ; while (0)
 # define RPC_IFDEBUG(x)
 #endif
 
index cb2e564527482c9d931aa92a7ee81724675abe0c..d3ad81f8da5b79551c36b17a7d53007406946699 100644 (file)
@@ -799,7 +799,7 @@ err_unlink_pipe_1:
 static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt,
                                           struct rpc_auth *auth)
 {
-       struct net *net = clnt->cl_xprt->xprt_net;
+       struct net *net = rpc_net_ns(clnt);
        struct super_block *sb;
 
        sb = rpc_get_sb_net(net);
@@ -813,7 +813,7 @@ static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt,
 static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt,
                                         struct rpc_auth *auth)
 {
-       struct net *net = clnt->cl_xprt->xprt_net;
+       struct net *net = rpc_net_ns(clnt);
        struct super_block *sb;
        int err = 0;
 
index 25c3da53fb690742591f50f580ecf37bc061f5b0..7783fc0e726361cd711135cb80e778decda44505 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <linux/un.h>
+#include <linux/rcupdate.h>
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
@@ -81,7 +82,8 @@ static int    rpc_ping(struct rpc_clnt *clnt);
 
 static void rpc_register_client(struct rpc_clnt *clnt)
 {
-       struct sunrpc_net *sn = net_generic(clnt->cl_xprt->xprt_net, sunrpc_net_id);
+       struct net *net = rpc_net_ns(clnt);
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
 
        spin_lock(&sn->rpc_client_lock);
        list_add(&clnt->cl_clients, &sn->all_clients);
@@ -90,7 +92,8 @@ static void rpc_register_client(struct rpc_clnt *clnt)
 
 static void rpc_unregister_client(struct rpc_clnt *clnt)
 {
-       struct sunrpc_net *sn = net_generic(clnt->cl_xprt->xprt_net, sunrpc_net_id);
+       struct net *net = rpc_net_ns(clnt);
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
 
        spin_lock(&sn->rpc_client_lock);
        list_del(&clnt->cl_clients);
@@ -109,12 +112,13 @@ static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
 
 static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
 {
+       struct net *net = rpc_net_ns(clnt);
        struct super_block *pipefs_sb;
 
-       pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net);
+       pipefs_sb = rpc_get_sb_net(net);
        if (pipefs_sb) {
                __rpc_clnt_remove_pipedir(clnt);
-               rpc_put_sb_net(clnt->cl_xprt->xprt_net);
+               rpc_put_sb_net(net);
        }
 }
 
@@ -155,17 +159,18 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
 static int
 rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name)
 {
+       struct net *net = rpc_net_ns(clnt);
        struct super_block *pipefs_sb;
        struct dentry *dentry;
 
        clnt->cl_dentry = NULL;
        if (dir_name == NULL)
                return 0;
-       pipefs_sb = rpc_get_sb_net(clnt->cl_xprt->xprt_net);
+       pipefs_sb = rpc_get_sb_net(net);
        if (!pipefs_sb)
                return 0;
        dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
-       rpc_put_sb_net(clnt->cl_xprt->xprt_net);
+       rpc_put_sb_net(net);
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
        clnt->cl_dentry = dentry;
@@ -295,7 +300,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        if (clnt->cl_server == NULL)
                goto out_no_server;
 
-       clnt->cl_xprt     = xprt;
+       rcu_assign_pointer(clnt->cl_xprt, xprt);
        clnt->cl_procinfo = version->procs;
        clnt->cl_maxproc  = version->nrprocs;
        clnt->cl_protname = program->name;
@@ -310,7 +315,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        INIT_LIST_HEAD(&clnt->cl_tasks);
        spin_lock_init(&clnt->cl_lock);
 
-       if (!xprt_bound(clnt->cl_xprt))
+       if (!xprt_bound(xprt))
                clnt->cl_autobind = 1;
 
        clnt->cl_timeout = xprt->timeout;
@@ -477,6 +482,7 @@ struct rpc_clnt *
 rpc_clone_client(struct rpc_clnt *clnt)
 {
        struct rpc_clnt *new;
+       struct rpc_xprt *xprt;
        int err = -ENOMEM;
 
        new = kmemdup(clnt, sizeof(*new), GFP_KERNEL);
@@ -499,18 +505,25 @@ rpc_clone_client(struct rpc_clnt *clnt)
                if (new->cl_principal == NULL)
                        goto out_no_principal;
        }
+       rcu_read_lock();
+       xprt = xprt_get(rcu_dereference(clnt->cl_xprt));
+       rcu_read_unlock();
+       if (xprt == NULL)
+               goto out_no_transport;
+       rcu_assign_pointer(new->cl_xprt, xprt);
        atomic_set(&new->cl_count, 1);
        err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name);
        if (err != 0)
                goto out_no_path;
        if (new->cl_auth)
                atomic_inc(&new->cl_auth->au_count);
-       xprt_get(clnt->cl_xprt);
        atomic_inc(&clnt->cl_count);
        rpc_register_client(new);
        rpciod_up();
        return new;
 out_no_path:
+       xprt_put(xprt);
+out_no_transport:
        kfree(new->cl_principal);
 out_no_principal:
        rpc_free_iostats(new->cl_metrics);
@@ -590,7 +603,7 @@ rpc_free_client(struct rpc_clnt *clnt)
        rpc_free_iostats(clnt->cl_metrics);
        kfree(clnt->cl_principal);
        clnt->cl_metrics = NULL;
-       xprt_put(clnt->cl_xprt);
+       xprt_put(rcu_dereference_raw(clnt->cl_xprt));
        rpciod_down();
        kfree(clnt);
 }
@@ -879,13 +892,18 @@ EXPORT_SYMBOL_GPL(rpc_call_start);
 size_t rpc_peeraddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t bufsize)
 {
        size_t bytes;
-       struct rpc_xprt *xprt = clnt->cl_xprt;
+       struct rpc_xprt *xprt;
+
+       rcu_read_lock();
+       xprt = rcu_dereference(clnt->cl_xprt);
 
-       bytes = sizeof(xprt->addr);
+       bytes = xprt->addrlen;
        if (bytes > bufsize)
                bytes = bufsize;
-       memcpy(buf, &clnt->cl_xprt->addr, bytes);
-       return xprt->addrlen;
+       memcpy(buf, &xprt->addr, bytes);
+       rcu_read_unlock();
+
+       return bytes;
 }
 EXPORT_SYMBOL_GPL(rpc_peeraddr);
 
@@ -894,11 +912,16 @@ EXPORT_SYMBOL_GPL(rpc_peeraddr);
  * @clnt: RPC client structure
  * @format: address format
  *
+ * NB: the lifetime of the memory referenced by the returned pointer is
+ * the same as the rpc_xprt itself.  As long as the caller uses this
+ * pointer, it must hold the RCU read lock.
  */
 const char *rpc_peeraddr2str(struct rpc_clnt *clnt,
                             enum rpc_display_format_t format)
 {
-       struct rpc_xprt *xprt = clnt->cl_xprt;
+       struct rpc_xprt *xprt;
+
+       xprt = rcu_dereference(clnt->cl_xprt);
 
        if (xprt->address_strings[format] != NULL)
                return xprt->address_strings[format];
@@ -910,14 +933,51 @@ EXPORT_SYMBOL_GPL(rpc_peeraddr2str);
 void
 rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize)
 {
-       struct rpc_xprt *xprt = clnt->cl_xprt;
+       struct rpc_xprt *xprt;
+
+       rcu_read_lock();
+       xprt = rcu_dereference(clnt->cl_xprt);
        if (xprt->ops->set_buffer_size)
                xprt->ops->set_buffer_size(xprt, sndsize, rcvsize);
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(rpc_setbufsize);
 
-/*
- * Return size of largest payload RPC client can support, in bytes
+/**
+ * rpc_protocol - Get transport protocol number for an RPC client
+ * @clnt: RPC client to query
+ *
+ */
+int rpc_protocol(struct rpc_clnt *clnt)
+{
+       int protocol;
+
+       rcu_read_lock();
+       protocol = rcu_dereference(clnt->cl_xprt)->prot;
+       rcu_read_unlock();
+       return protocol;
+}
+EXPORT_SYMBOL_GPL(rpc_protocol);
+
+/**
+ * rpc_net_ns - Get the network namespace for this RPC client
+ * @clnt: RPC client to query
+ *
+ */
+struct net *rpc_net_ns(struct rpc_clnt *clnt)
+{
+       struct net *ret;
+
+       rcu_read_lock();
+       ret = rcu_dereference(clnt->cl_xprt)->xprt_net;
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rpc_net_ns);
+
+/**
+ * rpc_max_payload - Get maximum payload size for a transport, in bytes
+ * @clnt: RPC client to query
  *
  * For stream transports, this is one RPC record fragment (see RFC
  * 1831), as we don't support multi-record requests yet.  For datagram
@@ -926,7 +986,12 @@ EXPORT_SYMBOL_GPL(rpc_setbufsize);
  */
 size_t rpc_max_payload(struct rpc_clnt *clnt)
 {
-       return clnt->cl_xprt->max_payload;
+       size_t ret;
+
+       rcu_read_lock();
+       ret = rcu_dereference(clnt->cl_xprt)->max_payload;
+       rcu_read_unlock();
+       return ret;
 }
 EXPORT_SYMBOL_GPL(rpc_max_payload);
 
@@ -937,8 +1002,11 @@ EXPORT_SYMBOL_GPL(rpc_max_payload);
  */
 void rpc_force_rebind(struct rpc_clnt *clnt)
 {
-       if (clnt->cl_autobind)
-               xprt_clear_bound(clnt->cl_xprt);
+       if (clnt->cl_autobind) {
+               rcu_read_lock();
+               xprt_clear_bound(rcu_dereference(clnt->cl_xprt));
+               rcu_read_unlock();
+       }
 }
 EXPORT_SYMBOL_GPL(rpc_force_rebind);
 
index ac9ee1590739a9f17c38fdb2f4412c029a39a07a..3d30943ed6db4412c0f2f70aad29a548f0e787a8 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/namei.h>
 #include <linux/fsnotify.h>
 #include <linux/kernel.h>
+#include <linux/rcupdate.h>
 
 #include <asm/ioctls.h>
 #include <linux/poll.h>
@@ -402,12 +403,14 @@ rpc_show_info(struct seq_file *m, void *v)
 {
        struct rpc_clnt *clnt = m->private;
 
+       rcu_read_lock();
        seq_printf(m, "RPC server: %s\n", clnt->cl_server);
        seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname,
                        clnt->cl_prog, clnt->cl_vers);
        seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));
        seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO));
        seq_printf(m, "port: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PORT));
+       rcu_read_unlock();
        return 0;
 }
 
index b1f08bd67883a648ec736720019732a73a41d966..4f8af63798a2c87e0dc61abba4e1fdb47053cf45 100644 (file)
@@ -620,9 +620,10 @@ static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbi
 static struct rpc_clnt *rpcb_find_transport_owner(struct rpc_clnt *clnt)
 {
        struct rpc_clnt *parent = clnt->cl_parent;
+       struct rpc_xprt *xprt = rcu_dereference(clnt->cl_xprt);
 
        while (parent != clnt) {
-               if (parent->cl_xprt != clnt->cl_xprt)
+               if (rcu_dereference(parent->cl_xprt) != xprt)
                        break;
                if (clnt->cl_autobind)
                        break;
@@ -653,8 +654,12 @@ void rpcb_getport_async(struct rpc_task *task)
        size_t salen;
        int status;
 
-       clnt = rpcb_find_transport_owner(task->tk_client);
-       xprt = clnt->cl_xprt;
+       rcu_read_lock();
+       do {
+               clnt = rpcb_find_transport_owner(task->tk_client);
+               xprt = xprt_get(rcu_dereference(clnt->cl_xprt));
+       } while (xprt == NULL);
+       rcu_read_unlock();
 
        dprintk("RPC: %5u %s(%s, %u, %u, %d)\n",
                task->tk_pid, __func__,
@@ -667,6 +672,7 @@ void rpcb_getport_async(struct rpc_task *task)
        if (xprt_test_and_set_binding(xprt)) {
                dprintk("RPC: %5u %s: waiting for another binder\n",
                        task->tk_pid, __func__);
+               xprt_put(xprt);
                return;
        }
 
@@ -734,7 +740,7 @@ void rpcb_getport_async(struct rpc_task *task)
        switch (bind_version) {
        case RPCBVERS_4:
        case RPCBVERS_3:
-               map->r_netid = rpc_peeraddr2str(clnt, RPC_DISPLAY_NETID);
+               map->r_netid = xprt->address_strings[RPC_DISPLAY_NETID];
                map->r_addr = rpc_sockaddr2uaddr(sap, GFP_ATOMIC);
                map->r_owner = "";
                break;
@@ -763,6 +769,7 @@ bailout_release_client:
 bailout_nofree:
        rpcb_wake_rpcbind_waiters(xprt, status);
        task->tk_status = status;
+       xprt_put(xprt);
 }
 EXPORT_SYMBOL_GPL(rpcb_getport_async);
 
index 1eb3304bc1055549513f3d1d842f634c8f9cd464..bc2068ee795b95d7fdec5d57503ab3919ff3e97d 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/metrics.h>
+#include <linux/rcupdate.h>
 
 #include "netns.h"
 
@@ -179,7 +180,7 @@ static void _print_name(struct seq_file *seq, unsigned int op,
 void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt)
 {
        struct rpc_iostats *stats = clnt->cl_metrics;
-       struct rpc_xprt *xprt = clnt->cl_xprt;
+       struct rpc_xprt *xprt;
        unsigned int op, maxproc = clnt->cl_maxproc;
 
        if (!stats)
@@ -189,8 +190,11 @@ void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt)
        seq_printf(seq, "p/v: %u/%u (%s)\n",
                        clnt->cl_prog, clnt->cl_vers, clnt->cl_protname);
 
+       rcu_read_lock();
+       xprt = rcu_dereference(clnt->cl_xprt);
        if (xprt)
                xprt->ops->print_stats(xprt, seq);
+       rcu_read_unlock();
 
        seq_printf(seq, "\tper-op statistics\n");
        for (op = 0; op < maxproc; op++) {