Skip to content

Commit dfe1fe7

Browse files
author
Trond Myklebust
committed
NFSv4: Fix deadlock between nfs4_evict_inode() and nfs4_opendata_get_inode()
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: zhangxiaoxu (A) <[email protected]> Fixes: 5fcdfac ("NFSv4: Return delegations synchronously in evict_inode") Signed-off-by: Trond Myklebust <[email protected]>
1 parent d1b5c23 commit dfe1fe7

File tree

2 files changed

+12
-1
lines changed

2 files changed

+12
-1
lines changed

fs/nfs/nfs4_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ struct nfs4_exception {
205205
struct inode *inode;
206206
nfs4_stateid *stateid;
207207
long timeout;
208+
unsigned char task_is_privileged : 1;
208209
unsigned char delay : 1,
209210
recovering : 1,
210211
retry : 1;

fs/nfs/nfs4proc.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,8 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
589589
goto out_retry;
590590
}
591591
if (exception->recovering) {
592+
if (exception->task_is_privileged)
593+
return -EDEADLOCK;
592594
ret = nfs4_wait_clnt_recover(clp);
593595
if (test_bit(NFS_MIG_FAILED, &server->mig_status))
594596
return -EIO;
@@ -614,6 +616,8 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
614616
goto out_retry;
615617
}
616618
if (exception->recovering) {
619+
if (exception->task_is_privileged)
620+
return -EDEADLOCK;
617621
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
618622
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
619623
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
@@ -6417,6 +6421,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
64176421
struct nfs4_exception exception = {
64186422
.inode = data->inode,
64196423
.stateid = &data->stateid,
6424+
.task_is_privileged = data->args.seq_args.sa_privileged,
64206425
};
64216426

64226427
if (!nfs4_sequence_done(task, &data->res.seq_res))
@@ -6540,7 +6545,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
65406545
data = kzalloc(sizeof(*data), GFP_NOFS);
65416546
if (data == NULL)
65426547
return -ENOMEM;
6543-
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);
65446548

65456549
nfs4_state_protect(server->nfs_client,
65466550
NFS_SP4_MACH_CRED_CLEANUP,
@@ -6571,6 +6575,12 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
65716575
}
65726576
}
65736577

6578+
if (!data->inode)
6579+
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1,
6580+
1);
6581+
else
6582+
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1,
6583+
0);
65746584
task_setup_data.callback_data = data;
65756585
msg.rpc_argp = &data->args;
65766586
msg.rpc_resp = &data->res;

0 commit comments

Comments
 (0)