SUNRPC: fix races on PipeFS MOUNT notifications
authorStanislav Kinsbursky <skinsbursky@parallels.com>
Mon, 24 Jun 2013 07:52:38 +0000 (11:52 +0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 28 Jun 2013 19:41:18 +0000 (15:41 -0400)
Below are races, when RPC client can be created without PiepFS dentries

CPU#0 CPU#1
----------------------------- -----------------------------
rpc_new_client rpc_fill_super
rpc_setup_pipedir
mutex_lock(&sn->pipefs_sb_lock)
rpc_get_sb_net == NULL
(no per-net PipeFS superblock)
sn->pipefs_sb = sb;
notifier_call_chain(MOUNT)
(client is not in the list)
rpc_register_client
(client without pipes dentries)

To fix this patch:
1) makes PipeFS mount notification call with pipefs_sb_lock being held.
2) releases pipefs_sb_lock on new SUNRPC client creation only after
registration.

Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com>
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c

index 5a750b9c36404b34a3b41bd0e2d38628f881a5f2..b827a4b9197570034971ec5df5fd98f2606f43e0 100644 (file)
@@ -157,20 +157,15 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
 }
 
 static int
-rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name)
+rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name,
+                 struct super_block *pipefs_sb)
 {
-       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(net);
-       if (!pipefs_sb)
-               return 0;
        dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
-       rpc_put_sb_net(net);
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
        clnt->cl_dentry = dentry;
@@ -296,6 +291,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        struct rpc_clnt         *clnt = NULL;
        struct rpc_auth         *auth;
        int err;
+       struct super_block *pipefs_sb;
 
        /* sanity check the name before trying to print it */
        dprintk("RPC:       creating %s client for %s (xprt %p)\n",
@@ -354,9 +350,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 
        atomic_set(&clnt->cl_count, 1);
 
-       err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
-       if (err < 0)
-               goto out_no_path;
+       pipefs_sb = rpc_get_sb_net(rpc_net_ns(clnt));
+       if (pipefs_sb) {
+               err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb);
+               if (err)
+                       goto out_no_path;
+       }
 
        auth = rpcauth_create(args->authflavor, clnt);
        if (IS_ERR(auth)) {
@@ -369,11 +368,16 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        /* save the nodename */
        rpc_clnt_set_nodename(clnt, utsname()->nodename);
        rpc_register_client(clnt);
+       if (pipefs_sb)
+               rpc_put_sb_net(rpc_net_ns(clnt));
        return clnt;
 
 out_no_auth:
-       rpc_clnt_remove_pipedir(clnt);
+       if (pipefs_sb)
+               __rpc_clnt_remove_pipedir(clnt);
 out_no_path:
+       if (pipefs_sb)
+               rpc_put_sb_net(rpc_net_ns(clnt));
        kfree(clnt->cl_principal);
 out_no_principal:
        rpc_free_iostats(clnt->cl_metrics);
index a816b3a690593fbf76068e884a3a0b012258c594..e02823bdfe98bfc75b631ff936ab670f104953bc 100644 (file)
@@ -1127,6 +1127,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
                return -ENOMEM;
        dprintk("RPC:       sending pipefs MOUNT notification for net %p%s\n",
                net, NET_NAME(net));
+       mutex_lock(&sn->pipefs_sb_lock);
        sn->pipefs_sb = sb;
        err = blocking_notifier_call_chain(&rpc_pipefs_notifier_list,
                                           RPC_PIPEFS_MOUNT,
@@ -1134,6 +1135,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
        if (err)
                goto err_depopulate;
        sb->s_fs_info = get_net(net);
+       mutex_unlock(&sn->pipefs_sb_lock);
        return 0;
 
 err_depopulate:
@@ -1142,6 +1144,7 @@ err_depopulate:
                                           sb);
        sn->pipefs_sb = NULL;
        __rpc_depopulate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF);
+       mutex_unlock(&sn->pipefs_sb_lock);
        return err;
 }