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

Commit 7b1f1fd1 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4/4.1: Fix bugs in nfs4[01]_walk_client_list



It is unsafe to use list_for_each_entry_safe() here, because
when we drop the nn->nfs_client_lock, we pin the _current_ list
entry and ensure that it stays in the list, but we don't do the
same for the _next_ list entry. Use of list_for_each_entry() is
therefore the correct thing to do.

Also fix the refcounting in nfs41_walk_client_list().

Finally, ensure that the nfs_client has finished being initialised
and, in the case of NFSv4.1, that the session is set up.

Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Cc: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Cc: stable@vger.kernel.org [>= 3.7]
parent b193d59a
Loading
Loading
Loading
Loading
+28 −16
Original line number Original line Diff line number Diff line
@@ -300,7 +300,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
			   struct rpc_cred *cred)
			   struct rpc_cred *cred)
{
{
	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
	struct nfs_client *pos, *n, *prev = NULL;
	struct nfs_client *pos, *prev = NULL;
	struct nfs4_setclientid_res clid = {
	struct nfs4_setclientid_res clid = {
		.clientid	= new->cl_clientid,
		.clientid	= new->cl_clientid,
		.confirm	= new->cl_confirm,
		.confirm	= new->cl_confirm,
@@ -308,10 +308,23 @@ int nfs40_walk_client_list(struct nfs_client *new,
	int status = -NFS4ERR_STALE_CLIENTID;
	int status = -NFS4ERR_STALE_CLIENTID;


	spin_lock(&nn->nfs_client_lock);
	spin_lock(&nn->nfs_client_lock);
	list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) {
	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
		/* If "pos" isn't marked ready, we can't trust the
		/* If "pos" isn't marked ready, we can't trust the
		 * remaining fields in "pos" */
		 * remaining fields in "pos" */
		if (pos->cl_cons_state < NFS_CS_READY)
		if (pos->cl_cons_state > NFS_CS_READY) {
			atomic_inc(&pos->cl_count);
			spin_unlock(&nn->nfs_client_lock);

			if (prev)
				nfs_put_client(prev);
			prev = pos;

			status = nfs_wait_client_init_complete(pos);
			spin_lock(&nn->nfs_client_lock);
			if (status < 0)
				continue;
		}
		if (pos->cl_cons_state != NFS_CS_READY)
			continue;
			continue;


		if (pos->rpc_ops != new->rpc_ops)
		if (pos->rpc_ops != new->rpc_ops)
@@ -423,16 +436,16 @@ int nfs41_walk_client_list(struct nfs_client *new,
			   struct rpc_cred *cred)
			   struct rpc_cred *cred)
{
{
	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
	struct nfs_client *pos, *n, *prev = NULL;
	struct nfs_client *pos, *prev = NULL;
	int status = -NFS4ERR_STALE_CLIENTID;
	int status = -NFS4ERR_STALE_CLIENTID;


	spin_lock(&nn->nfs_client_lock);
	spin_lock(&nn->nfs_client_lock);
	list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) {
	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
		/* If "pos" isn't marked ready, we can't trust the
		/* If "pos" isn't marked ready, we can't trust the
		 * remaining fields in "pos", especially the client
		 * remaining fields in "pos", especially the client
		 * ID and serverowner fields.  Wait for CREATE_SESSION
		 * ID and serverowner fields.  Wait for CREATE_SESSION
		 * to finish. */
		 * to finish. */
		if (pos->cl_cons_state < NFS_CS_READY) {
		if (pos->cl_cons_state > NFS_CS_READY) {
			atomic_inc(&pos->cl_count);
			atomic_inc(&pos->cl_count);
			spin_unlock(&nn->nfs_client_lock);
			spin_unlock(&nn->nfs_client_lock);


@@ -440,18 +453,17 @@ int nfs41_walk_client_list(struct nfs_client *new,
				nfs_put_client(prev);
				nfs_put_client(prev);
			prev = pos;
			prev = pos;


			nfs4_schedule_lease_recovery(pos);
			status = nfs_wait_client_init_complete(pos);
			status = nfs_wait_client_init_complete(pos);
			if (status < 0) {
			if (status == 0) {
				nfs_put_client(pos);
				nfs4_schedule_lease_recovery(pos);
				spin_lock(&nn->nfs_client_lock);
				status = nfs4_wait_clnt_recover(pos);
				continue;
			}
			}
			status = pos->cl_cons_state;
			spin_lock(&nn->nfs_client_lock);
			spin_lock(&nn->nfs_client_lock);
			if (status < 0)
			if (status < 0)
				continue;
				continue;
		}
		}
		if (pos->cl_cons_state != NFS_CS_READY)
			continue;


		if (pos->rpc_ops != new->rpc_ops)
		if (pos->rpc_ops != new->rpc_ops)
			continue;
			continue;
@@ -469,17 +481,17 @@ int nfs41_walk_client_list(struct nfs_client *new,
			continue;
			continue;


		atomic_inc(&pos->cl_count);
		atomic_inc(&pos->cl_count);
		spin_unlock(&nn->nfs_client_lock);
		*result = pos;
		dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
		dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
			__func__, pos, atomic_read(&pos->cl_count));
			__func__, pos, atomic_read(&pos->cl_count));

		break;
		*result = pos;
		return 0;
	}
	}


	/* No matching nfs_client found. */
	/* No matching nfs_client found. */
	spin_unlock(&nn->nfs_client_lock);
	spin_unlock(&nn->nfs_client_lock);
	dprintk("NFS: <-- %s status = %d\n", __func__, status);
	dprintk("NFS: <-- %s status = %d\n", __func__, status);
	if (prev)
		nfs_put_client(prev);
	return status;
	return status;
}
}
#endif	/* CONFIG_NFS_V4_1 */
#endif	/* CONFIG_NFS_V4_1 */