diff options
author | Stanislav Kinsbursky <skinsbursky@parallels.com> | 2013-06-26 02:15:14 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-06-28 15:42:02 -0400 |
commit | adb6fa7ffe9031857ec14b8aab75c9ab65556cbc (patch) | |
tree | 4d13bd7bf95d0c24478e95f429d6b4e6bbf5a6a5 /net/sunrpc/clnt.c | |
parent | 384816051ca9125cd54750e59c780c2a2655fa4f (diff) |
SUNRPC: fix races on PipeFS UMOUNT notifications
CPU#0 CPU#1
----------------------------- -----------------------------
rpc_kill_sb
sn->pipefs_sb = NULL rpc_release_client
(UMOUNT_EVENT) rpc_free_auth
rpc_pipefs_event
rpc_get_client_for_event
!atomic_inc_not_zero(cl_count)
<skip the client>
atomic_inc(cl_count)
rpc_free_client
rpc_clnt_remove_pipedir
<skip client dir removing>
To fix this, this patch does the following:
1) Calls RPC_PIPEFS_UMOUNT notification with sn->pipefs_sb_lock being held.
2) Removes SUNRPC client from the list AFTER pipes destroying.
3) Doesn't hold RPC client on notification: if client in the list, then it
can't be destroyed while sn->pipefs_sb_lock in hold by notification caller.
Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com>
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc/clnt.c')
-rw-r--r-- | net/sunrpc/clnt.c | 5 |
1 files changed, 1 insertions, 4 deletions
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b827a4b91975..41f180c5a498 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -236,8 +236,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event) | |||
236 | continue; | 236 | continue; |
237 | if (rpc_clnt_skip_event(clnt, event)) | 237 | if (rpc_clnt_skip_event(clnt, event)) |
238 | continue; | 238 | continue; |
239 | if (atomic_inc_not_zero(&clnt->cl_count) == 0) | ||
240 | continue; | ||
241 | spin_unlock(&sn->rpc_client_lock); | 239 | spin_unlock(&sn->rpc_client_lock); |
242 | return clnt; | 240 | return clnt; |
243 | } | 241 | } |
@@ -254,7 +252,6 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, | |||
254 | 252 | ||
255 | while ((clnt = rpc_get_client_for_event(sb->s_fs_info, event))) { | 253 | while ((clnt = rpc_get_client_for_event(sb->s_fs_info, event))) { |
256 | error = __rpc_pipefs_event(clnt, event, sb); | 254 | error = __rpc_pipefs_event(clnt, event, sb); |
257 | rpc_release_client(clnt); | ||
258 | if (error) | 255 | if (error) |
259 | break; | 256 | break; |
260 | } | 257 | } |
@@ -641,8 +638,8 @@ rpc_free_client(struct rpc_clnt *clnt) | |||
641 | rcu_dereference(clnt->cl_xprt)->servername); | 638 | rcu_dereference(clnt->cl_xprt)->servername); |
642 | if (clnt->cl_parent != clnt) | 639 | if (clnt->cl_parent != clnt) |
643 | rpc_release_client(clnt->cl_parent); | 640 | rpc_release_client(clnt->cl_parent); |
644 | rpc_unregister_client(clnt); | ||
645 | rpc_clnt_remove_pipedir(clnt); | 641 | rpc_clnt_remove_pipedir(clnt); |
642 | rpc_unregister_client(clnt); | ||
646 | rpc_free_iostats(clnt->cl_metrics); | 643 | rpc_free_iostats(clnt->cl_metrics); |
647 | kfree(clnt->cl_principal); | 644 | kfree(clnt->cl_principal); |
648 | clnt->cl_metrics = NULL; | 645 | clnt->cl_metrics = NULL; |