提交 d1c9802f 编写于 作者: T Trond Myklebust 提交者: Yang Yingliang

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

hulk inclusion
category: bugfix
bugzilla: 51898
CVE: NA

---------------------------

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: Nzhangxiaoxu (A) <zhangxiaoxu5@huawei.com>
Fixes: 5fcdfacc ("NFSv4: Return delegations synchronously in evict_inode")
Signed-off-by: NTrond Myklebust <trond.myklebust@hammerspace.com>

Link: https://patchwork.kernel.org/project/linux-nfs/list/?series=491989
Conflict:
	fs/nfs/nfs4proc.c
Signed-off-by: NZhang Xiaoxu <zhangxiaoxu5@huawei.com>
Reviewed-by: NZhang Yi <yi.zhang@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 d71caefa
...@@ -201,6 +201,7 @@ struct nfs4_exception { ...@@ -201,6 +201,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;
......
...@@ -564,6 +564,8 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_ ...@@ -564,6 +564,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;
...@@ -589,6 +591,8 @@ nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server, ...@@ -589,6 +591,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);
...@@ -6102,6 +6106,7 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) ...@@ -6102,6 +6106,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))
...@@ -6246,7 +6251,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co ...@@ -6246,7 +6251,6 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
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,
...@@ -6276,6 +6280,13 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co ...@@ -6276,6 +6280,13 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
data->lr.roc = false; 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; 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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册