Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4697bd5e authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4: Fix a race in the net namespace mount notification



Since the struct nfs_client gets added to the global nfs_client_list
before it is initialised, it is possible that rpc_pipefs_event can
end up trying to create idmapper entries on such a thing.

The solution is to have the mount notification wait for the
initialisation of each nfs_client to complete, and then to
skip any entries for which the it failed.

Reported-by: default avatarStanislav Kinsbursky <skinsbursky@parallels.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Acked-by: default avatarStanislav Kinsbursky <skinsbursky@parallels.com>
parent 7b38c368
Loading
Loading
Loading
Loading
+13 −3
Original line number Original line Diff line number Diff line
@@ -507,6 +507,17 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
	return NULL;
	return NULL;
}
}


static bool nfs_client_init_is_complete(const struct nfs_client *clp)
{
	return clp->cl_cons_state != NFS_CS_INITING;
}

int nfs_wait_client_init_complete(const struct nfs_client *clp)
{
	return wait_event_killable(nfs_client_active_wq,
			nfs_client_init_is_complete(clp));
}

/*
/*
 * Found an existing client.  Make sure it's ready before returning.
 * Found an existing client.  Make sure it's ready before returning.
 */
 */
@@ -516,8 +527,7 @@ nfs_found_client(const struct nfs_client_initdata *cl_init,
{
{
	int error;
	int error;


	error = wait_event_killable(nfs_client_active_wq,
	error = nfs_wait_client_init_complete(clp);
				clp->cl_cons_state < NFS_CS_INITING);
	if (error < 0) {
	if (error < 0) {
		nfs_put_client(clp);
		nfs_put_client(clp);
		return ERR_PTR(-ERESTARTSYS);
		return ERR_PTR(-ERESTARTSYS);
@@ -1333,7 +1343,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp)
		 * so that the client back channel can find the
		 * so that the client back channel can find the
		 * nfs_client struct
		 * nfs_client struct
		 */
		 */
		clp->cl_cons_state = NFS_CS_SESSION_INITING;
		nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
	}
	}
#endif /* CONFIG_NFS_V4_1 */
#endif /* CONFIG_NFS_V4_1 */


+15 −0
Original line number Original line Diff line number Diff line
@@ -530,9 +530,24 @@ static struct nfs_client *nfs_get_client_for_event(struct net *net, int event)
	struct nfs_net *nn = net_generic(net, nfs_net_id);
	struct nfs_net *nn = net_generic(net, nfs_net_id);
	struct dentry *cl_dentry;
	struct dentry *cl_dentry;
	struct nfs_client *clp;
	struct nfs_client *clp;
	int err;


restart:
	spin_lock(&nn->nfs_client_lock);
	spin_lock(&nn->nfs_client_lock);
	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
		/* Wait for initialisation to finish */
		if (clp->cl_cons_state == NFS_CS_INITING) {
			atomic_inc(&clp->cl_count);
			spin_unlock(&nn->nfs_client_lock);
			err = nfs_wait_client_init_complete(clp);
			nfs_put_client(clp);
			if (err)
				return NULL;
			goto restart;
		}
		/* Skip nfs_clients that failed to initialise */
		if (clp->cl_cons_state < 0)
			continue;
		if (clp->rpc_ops != &nfs_v4_clientops)
		if (clp->rpc_ops != &nfs_v4_clientops)
			continue;
			continue;
		cl_dentry = clp->cl_idmap->idmap_pipe->dentry;
		cl_dentry = clp->cl_idmap->idmap_pipe->dentry;
+1 −0
Original line number Original line Diff line number Diff line
@@ -168,6 +168,7 @@ extern struct nfs_server *nfs_clone_server(struct nfs_server *,
					   struct nfs_fh *,
					   struct nfs_fh *,
					   struct nfs_fattr *,
					   struct nfs_fattr *,
					   rpc_authflavor_t);
					   rpc_authflavor_t);
extern int nfs_wait_client_init_complete(const struct nfs_client *clp);
extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
extern struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
					     const struct sockaddr *ds_addr,
					     const struct sockaddr *ds_addr,