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

Commit abcdff09 authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

nfsd4: fix destroy_session race



destroy_session uses the session and client without continuously holding
any reference or locks.

Put the whole thing under the state lock for now.

Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent bfa85e83
Loading
Loading
Loading
Loading
+10 −16
Original line number Diff line number Diff line
@@ -1926,41 +1926,35 @@ nfsd4_destroy_session(struct svc_rqst *r,
		      struct nfsd4_destroy_session *sessionid)
{
	struct nfsd4_session *ses;
	__be32 status = nfserr_badsession;
	__be32 status;
	struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);

	/* Notes:
	 * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
	 * - Should we return nfserr_back_chan_busy if waiting for
	 *   callbacks on to-be-destroyed session?
	 * - Do we need to clear any callback info from previous session?
	 */

	nfs4_lock_state();
	status = nfserr_not_only_op;
	if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) {
		if (!nfsd4_last_compound_op(r))
			return nfserr_not_only_op;
			goto out;
	}
	dump_sessionid(__func__, &sessionid->sessionid);
	spin_lock(&nn->client_lock);
	ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r));
	if (!ses) {
		spin_unlock(&nn->client_lock);
		goto out;
	}
	status = nfserr_badsession;
	if (!ses)
		goto out_client_lock;

	unhash_session(ses);
	spin_unlock(&nn->client_lock);

	nfs4_lock_state();
	nfsd4_probe_callback_sync(ses->se_client);
	nfs4_unlock_state();

	spin_lock(&nn->client_lock);
	nfsd4_del_conns(ses);
	nfsd4_put_session_locked(ses);
	spin_unlock(&nn->client_lock);
	status = nfs_ok;
out_client_lock:
	spin_unlock(&nn->client_lock);
out:
	nfs4_unlock_state();
	return status;
}