2013-06-10 14:40:15

by Stanislav Kinsbursky

[permalink] [raw]
Subject: [PATCH 0/3] SUNRPC: PipeFS races fixes

This series fixed races between PipeFS mount/umount notification calls and
SUNRPC clients creation and destruction.

https://bugzilla.redhat.com/show_bug.cgi?id=924649

The following series implements...

---

Stanislav Kinsbursky (3):
SUNRPC: fix races on PipeFS UMOUNT notifications
SUNRPC: fix races on PipeFS UMOUNT notifications
SUNRPC: PipeFS MOUNT notification optimization for dying clients


net/sunrpc/clnt.c | 14 +++++++++-----
net/sunrpc/rpc_pipe.c | 5 ++++-
2 files changed, 13 insertions(+), 6 deletions(-)



2013-06-10 14:40:31

by Stanislav Kinsbursky

[permalink] [raw]
Subject: [PATCH 3/3] SUNRPC: PipeFS MOUNT notification optimization for dying clients

Not need to create pipes for dying client. So just skip them.

Note: we can safely dereference the client structure, because notification
caller is holding sn->pipefs_sb_lock.

Signed-off-by: Stanislav Kinsbursky <[email protected]>
Cc: [email protected]
---
net/sunrpc/clnt.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 3c133f2..fc4f4d1 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -185,6 +185,8 @@ static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event
if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) ||
((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry))
return 1;
+ if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0)
+ return 1;
return 0;
}



2013-06-10 14:41:51

by Stanislav Kinsbursky

[permalink] [raw]
Subject: Re: [PATCH 0/3] SUNRPC: PipeFS races fixes

This series has flaws. Will fix and resend.
Sorry for noise.

10.06.2013 18:39, Stanislav Kinsbursky пишет:
> This series fixed races between PipeFS mount/umount notification calls and
> SUNRPC clients creation and destruction.
>
> https://bugzilla.redhat.com/show_bug.cgi?id=924649
>
> The following series implements...
>
> ---
>
> Stanislav Kinsbursky (3):
> SUNRPC: fix races on PipeFS UMOUNT notifications
> SUNRPC: fix races on PipeFS UMOUNT notifications
> SUNRPC: PipeFS MOUNT notification optimization for dying clients
>
>
> net/sunrpc/clnt.c | 14 +++++++++-----
> net/sunrpc/rpc_pipe.c | 5 ++++-
> 2 files changed, 13 insertions(+), 6 deletions(-)
>


--
Best regards,
Stanislav Kinsbursky

2013-06-10 14:40:21

by Stanislav Kinsbursky

[permalink] [raw]
Subject: [PATCH 2/3] 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 <[email protected]>
Cc: [email protected]
---
net/sunrpc/clnt.c | 5 +----
net/sunrpc/rpc_pipe.c | 2 +-
2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index cf5b226..3c133f2 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -244,8 +244,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event)
continue;
if (rpc_clnt_skip_event(clnt, event))
continue;
- if (atomic_inc_not_zero(&clnt->cl_count) == 0)
- continue;
spin_unlock(&sn->rpc_client_lock);
return clnt;
}
@@ -262,7 +260,6 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,

while ((clnt = rpc_get_client_for_event(sb->s_fs_info, event))) {
error = __rpc_pipefs_event(clnt, event, sb);
- rpc_release_client(clnt);
if (error)
break;
}
@@ -642,8 +639,8 @@ rpc_free_client(struct rpc_clnt *clnt)
rcu_dereference(clnt->cl_xprt)->servername);
if (clnt->cl_parent != clnt)
rpc_release_client(clnt->cl_parent);
- rpc_unregister_client(clnt);
rpc_clnt_remove_pipedir(clnt);
+ rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics);
kfree(clnt->cl_principal);
clnt->cl_metrics = NULL;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index c512448..efca2f7 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -1165,7 +1165,6 @@ static void rpc_kill_sb(struct super_block *sb)
goto out;
}
sn->pipefs_sb = NULL;
- mutex_unlock(&sn->pipefs_sb_lock);
dprintk("RPC: sending pipefs UMOUNT notification for net %p%s\n",
net, NET_NAME(net));
blocking_notifier_call_chain(&rpc_pipefs_notifier_list,
@@ -1173,6 +1172,7 @@ static void rpc_kill_sb(struct super_block *sb)
sb);
put_net(net);
out:
+ mutex_unlock(&sn->pipefs_sb_lock);
kill_litter_super(sb);
}



2013-06-10 14:40:21

by Stanislav Kinsbursky

[permalink] [raw]
Subject: [PATCH 1/3] SUNRPC: fix races on PipeFS UMOUNT notifications

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 <[email protected]>
Cc: [email protected]
---
net/sunrpc/clnt.c | 7 ++++++-
net/sunrpc/rpc_pipe.c | 3 +++
2 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 5a750b9..cf5b226 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -170,7 +170,10 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name)
if (!pipefs_sb)
return 0;
dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name);
- rpc_put_sb_net(net);
+ /*
+ * PipeFS superblock will be put after client registration to prevent
+ * races with PipeFS mount call.
+ */
if (IS_ERR(dentry))
return PTR_ERR(dentry);
clnt->cl_dentry = dentry;
@@ -369,11 +372,13 @@ 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);
+ rpc_put_sb_net(xprt->xprt_net);
return clnt;

out_no_auth:
rpc_clnt_remove_pipedir(clnt);
out_no_path:
+ rpc_put_sb_net(xprt->xprt_net);
kfree(clnt->cl_principal);
out_no_principal:
rpc_free_iostats(clnt->cl_metrics);
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index e7ce4b3..c512448 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -1126,6 +1126,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,
@@ -1133,6 +1134,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:
@@ -1141,6 +1143,7 @@ err_depopulate:
sb);
sn->pipefs_sb = NULL;
__rpc_depopulate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF);
+ mutex_unlock(&sn->pipefs_sb_lock);
return err;
}