NFS: Fix a soft lockup in the delegation recovery code

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: 45870d6909 ("NFSv4.1: Test delegation stateids when server...")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
Trond Myklebust 2019-02-21 14:51:25 -05:00
parent 3453d5708b
commit 6f9449be53
2 changed files with 13 additions and 8 deletions

View File

@ -229,6 +229,8 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
spin_lock(&delegation->lock); spin_lock(&delegation->lock);
if (delegation->inode != NULL) if (delegation->inode != NULL)
inode = igrab(delegation->inode); inode = igrab(delegation->inode);
if (!inode)
set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
return inode; return inode;
} }
@ -944,10 +946,11 @@ restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations, list_for_each_entry_rcu(delegation, &server->delegations,
super_list) { super_list) {
if (test_bit(NFS_DELEGATION_RETURNING, if (test_bit(NFS_DELEGATION_INODE_FREEING,
&delegation->flags)) &delegation->flags) ||
continue; test_bit(NFS_DELEGATION_RETURNING,
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) ||
test_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags) == 0) &delegation->flags) == 0)
continue; continue;
if (!nfs_sb_active(server->super)) if (!nfs_sb_active(server->super))
@ -1053,10 +1056,11 @@ restart:
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations, list_for_each_entry_rcu(delegation, &server->delegations,
super_list) { super_list) {
if (test_bit(NFS_DELEGATION_RETURNING, if (test_bit(NFS_DELEGATION_INODE_FREEING,
&delegation->flags)) &delegation->flags) ||
continue; test_bit(NFS_DELEGATION_RETURNING,
if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags) ||
test_bit(NFS_DELEGATION_TEST_EXPIRED,
&delegation->flags) == 0) &delegation->flags) == 0)
continue; continue;
if (!nfs_sb_active(server->super)) if (!nfs_sb_active(server->super))

View File

@ -34,6 +34,7 @@ enum {
NFS_DELEGATION_RETURNING, NFS_DELEGATION_RETURNING,
NFS_DELEGATION_REVOKED, NFS_DELEGATION_REVOKED,
NFS_DELEGATION_TEST_EXPIRED, NFS_DELEGATION_TEST_EXPIRED,
NFS_DELEGATION_INODE_FREEING,
}; };
int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,