| From 7378564e1aa0073a8e4f0f3a87d741206b82c69b Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 21 Feb 2019 14:51:25 -0500 |
| Subject: NFS: Fix a soft lockup in the delegation recovery code |
| |
| From: Trond Myklebust <trond.myklebust@hammerspace.com> |
| |
| [ Upstream commit 6f9449be53f3ce383caed797708b332ede8d952c ] |
| |
| Fix a soft lockup when NFS client delegation recovery is attempted |
| but the inode is in the process of being freed. When the |
| igrab(inode) call fails, and we have to restart the recovery process, |
| we need to ensure that we won't attempt to recover the same delegation |
| again. |
| |
| Fixes: 45870d6909d5a ("NFSv4.1: Test delegation stateids when server...") |
| Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/nfs/delegation.c | 20 ++++++++++++-------- |
| fs/nfs/delegation.h | 1 + |
| 2 files changed, 13 insertions(+), 8 deletions(-) |
| |
| diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c |
| index 9a8830a0f31f9..014039618cff0 100644 |
| --- a/fs/nfs/delegation.c |
| +++ b/fs/nfs/delegation.c |
| @@ -234,6 +234,8 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation |
| spin_lock(&delegation->lock); |
| if (delegation->inode != NULL) |
| inode = igrab(delegation->inode); |
| + if (!inode) |
| + set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags); |
| spin_unlock(&delegation->lock); |
| return inode; |
| } |
| @@ -867,10 +869,11 @@ restart: |
| list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { |
| list_for_each_entry_rcu(delegation, &server->delegations, |
| super_list) { |
| - if (test_bit(NFS_DELEGATION_RETURNING, |
| - &delegation->flags)) |
| - continue; |
| - if (test_bit(NFS_DELEGATION_NEED_RECLAIM, |
| + if (test_bit(NFS_DELEGATION_INODE_FREEING, |
| + &delegation->flags) || |
| + test_bit(NFS_DELEGATION_RETURNING, |
| + &delegation->flags) || |
| + test_bit(NFS_DELEGATION_NEED_RECLAIM, |
| &delegation->flags) == 0) |
| continue; |
| if (!nfs_sb_active(server->super)) |
| @@ -975,10 +978,11 @@ restart: |
| list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { |
| list_for_each_entry_rcu(delegation, &server->delegations, |
| super_list) { |
| - if (test_bit(NFS_DELEGATION_RETURNING, |
| - &delegation->flags)) |
| - continue; |
| - if (test_bit(NFS_DELEGATION_TEST_EXPIRED, |
| + if (test_bit(NFS_DELEGATION_INODE_FREEING, |
| + &delegation->flags) || |
| + test_bit(NFS_DELEGATION_RETURNING, |
| + &delegation->flags) || |
| + test_bit(NFS_DELEGATION_TEST_EXPIRED, |
| &delegation->flags) == 0) |
| continue; |
| if (!nfs_sb_active(server->super)) |
| diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h |
| index 2c6cb7fb7d5ee..f72095bf9e107 100644 |
| --- a/fs/nfs/delegation.h |
| +++ b/fs/nfs/delegation.h |
| @@ -33,6 +33,7 @@ enum { |
| NFS_DELEGATION_RETURNING, |
| NFS_DELEGATION_REVOKED, |
| NFS_DELEGATION_TEST_EXPIRED, |
| + NFS_DELEGATION_INODE_FREEING, |
| }; |
| |
| int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); |
| -- |
| 2.20.1 |
| |