Skip to content

Commit

Permalink
SUNRPC: Use RCU to dereference the rpc_clnt.cl_xprt field
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Mar 2, 2012
1 parent a3ca565 commit 2446ab6
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 50 deletions.
9 changes: 5 additions & 4 deletions fs/nfs/callback_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
16 changes: 11 additions & 5 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -1993,13 +1997,15 @@ 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),
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
dev,
fsid,
nfs_server_fscache_state(server));
rcu_read_unlock();

return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs4namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
13 changes: 10 additions & 3 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down
25 changes: 17 additions & 8 deletions fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

/*
Expand Down
5 changes: 5 additions & 0 deletions fs/nfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion include/linux/sunrpc/clnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions include/linux/sunrpc/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions net/sunrpc/auth_gss/auth_gss.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ static int gss_pipes_dentries_create(struct rpc_auth *auth)
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);
Expand All @@ -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;

Expand Down
Loading

0 comments on commit 2446ab6

Please sign in to comment.