mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 15:26:21 +09:00
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: zhangxiaoxu (A) <zhangxiaoxu5@huawei.com>
Fixes: 5fcdfacc01
("NFSv4: Return delegations synchronously in evict_inode")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
0057ecef9f
commit
34769f17e4
|
@ -203,6 +203,7 @@ struct nfs4_exception {
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
nfs4_stateid *stateid;
|
nfs4_stateid *stateid;
|
||||||
long timeout;
|
long timeout;
|
||||||
|
unsigned char task_is_privileged : 1;
|
||||||
unsigned char delay : 1,
|
unsigned char delay : 1,
|
||||||
recovering : 1,
|
recovering : 1,
|
||||||
retry : 1;
|
retry : 1;
|
||||||
|
|
|
@ -581,6 +581,8 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
|
||||||
goto out_retry;
|
goto out_retry;
|
||||||
}
|
}
|
||||||
if (exception->recovering) {
|
if (exception->recovering) {
|
||||||
|
if (exception->task_is_privileged)
|
||||||
|
return -EDEADLOCK;
|
||||||
ret = nfs4_wait_clnt_recover(clp);
|
ret = nfs4_wait_clnt_recover(clp);
|
||||||
if (test_bit(NFS_MIG_FAILED, &server->mig_status))
|
if (test_bit(NFS_MIG_FAILED, &server->mig_status))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -606,6 +608,8 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
|
||||||
goto out_retry;
|
goto out_retry;
|
||||||
}
|
}
|
||||||
if (exception->recovering) {
|
if (exception->recovering) {
|
||||||
|
if (exception->task_is_privileged)
|
||||||
|
return -EDEADLOCK;
|
||||||
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
|
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
|
||||||
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
|
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
|
||||||
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
|
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
|
||||||
|
@ -6231,6 +6235,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
|
||||||
struct nfs4_exception exception = {
|
struct nfs4_exception exception = {
|
||||||
.inode = data->inode,
|
.inode = data->inode,
|
||||||
.stateid = &data->stateid,
|
.stateid = &data->stateid,
|
||||||
|
.task_is_privileged = data->args.seq_args.sa_privileged,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
||||||
|
@ -6349,7 +6354,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
|
||||||
data = kzalloc(sizeof(*data), GFP_NOFS);
|
data = kzalloc(sizeof(*data), GFP_NOFS);
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);
|
|
||||||
|
|
||||||
nfs4_state_protect(server->nfs_client,
|
nfs4_state_protect(server->nfs_client,
|
||||||
NFS_SP4_MACH_CRED_CLEANUP,
|
NFS_SP4_MACH_CRED_CLEANUP,
|
||||||
|
@ -6377,6 +6381,12 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
task_setup_data.callback_data = data;
|
||||||
msg.rpc_argp = &data->args;
|
msg.rpc_argp = &data->args;
|
||||||
msg.rpc_resp = &data->res;
|
msg.rpc_resp = &data->res;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user