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

Commit f7d2172d authored by Trond Myklebust's avatar Trond Myklebust Committed by Greg Kroah-Hartman
Browse files

NFSv4: Fix deadlock between nfs4_evict_inode() and nfs4_opendata_get_inode()



[ Upstream commit dfe1fe75e00e4c724ede7b9e593f6f680e446c5f ]

If the inode is being evicted, but has to return a delegation first,
then it can cause a deadlock in the corner case where the server reboots
before the delegreturn completes, but while the call to iget5_locked() in
nfs4_opendata_get_inode() is waiting for the inode free to complete.
Since the open call still holds a session slot, the reboot recovery
cannot proceed.

In order to break the logjam, we can turn the delegation return into a
privileged operation for the case where we're evicting the inode. We
know that in that case, there can be no other state recovery operation
that conflicts.

Reported-by: default avatarzhangxiaoxu (A) <zhangxiaoxu5@huawei.com>
Fixes: 5fcdfacc ("NFSv4: Return delegations synchronously in evict_inode")
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 4b380a7d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -201,6 +201,7 @@ struct nfs4_exception {
	struct inode *inode;
	nfs4_stateid *stateid;
	long timeout;
	unsigned char task_is_privileged : 1;
	unsigned char delay : 1,
		      recovering : 1,
		      retry : 1;
+11 −1
Original line number Diff line number Diff line
@@ -550,6 +550,8 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
		goto out_retry;
	}
	if (exception->recovering) {
		if (exception->task_is_privileged)
			return -EDEADLOCK;
		ret = nfs4_wait_clnt_recover(clp);
		if (test_bit(NFS_MIG_FAILED, &server->mig_status))
			return -EIO;
@@ -575,6 +577,8 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
		goto out_retry;
	}
	if (exception->recovering) {
		if (exception->task_is_privileged)
			return -EDEADLOCK;
		rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
		if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
			rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
@@ -6017,6 +6021,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
	struct nfs4_exception exception = {
		.inode = data->inode,
		.stateid = &data->stateid,
		.task_is_privileged = data->args.seq_args.sa_privileged,
	};

	if (!nfs4_sequence_done(task, &data->res.seq_res))
@@ -6160,7 +6165,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
	data = kzalloc(sizeof(*data), GFP_NOFS);
	if (data == NULL)
		return -ENOMEM;
	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);

	nfs4_state_protect(server->nfs_client,
			NFS_SP4_MACH_CRED_CLEANUP,
@@ -6190,6 +6194,12 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
		data->lr.roc = false;
	}

	if (!data->inode)
		nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1,
				   1);
	else
		nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1,
				   0);
	task_setup_data.callback_data = data;
	msg.rpc_argp = &data->args;
	msg.rpc_resp = &data->res;