NFS: Fix a soft lockup in the delegation recovery code
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 21 Feb 2019 19:51:25 +0000 (14:51 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jan 2020 09:24:17 +0000 (10:24 +0100)
[ 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
fs/nfs/delegation.h

index 9a8830a0f31f953dcd50e2f6b54de32fed7da760..014039618cff052c0a74b9cb1b2dfd58deb64e00 100644 (file)
@@ -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))
index 2c6cb7fb7d5ee1c4c288d1486e782b028b15184b..f72095bf9e1074ac505ca81136c6055a8950187e 100644 (file)
@@ -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);