]> git.baikalelectronics.ru Git - kernel.git/commitdiff
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)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 21 Feb 2019 19:51:25 +0000 (14:51 -0500)
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: 21f14ac32bfb6 ("NFSv4.1: Test delegation stateids when server...")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/delegation.c
fs/nfs/delegation.h

index 70e5931f3c604e92b1d82a0e4ec08229d8af31c3..2f6b447cdd8256c393fcf1cc4580a7174e8a3055 100644 (file)
@@ -229,6 +229,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;
 }
@@ -944,10 +946,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))
@@ -1053,10 +1056,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 dcbf3394ba0e0431fdbbf17486cf3e6d3bda1290..35b4b02c1ae01d04478b4f4de56c5f59b3ebc911 100644 (file)
@@ -34,6 +34,7 @@ enum {
        NFS_DELEGATION_RETURNING,
        NFS_DELEGATION_REVOKED,
        NFS_DELEGATION_TEST_EXPIRED,
+       NFS_DELEGATION_INODE_FREEING,
 };
 
 int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,