NFSv4.1 fix infinite loop on I/O.
authorTrond Myklebust <trondmy@gmail.com>
Wed, 5 Sep 2018 18:07:14 +0000 (14:07 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Sep 2018 06:38:09 +0000 (08:38 +0200)
commit 994b15b983a72e1148a173b61e5b279219bb45ae upstream.

The previous fix broke recovery of delegated stateids because it assumes
that if we did not mark the delegation as suspect, then the delegation has
effectively been revoked, and so it removes that delegation irrespectively
of whether or not it is valid and still in use. While this is "mostly
harmless" for ordinary I/O, we've seen pNFS fail with LAYOUTGET spinning
in an infinite loop while complaining that we're using an invalid stateid
(in this case the all-zero stateid).

What we rather want to do here is ensure that the delegation is always
correctly marked as needing testing when that is the case. So we want
to close the loophole offered by nfs4_schedule_stateid_recovery(),
which marks the state as needing to be reclaimed, but not the
delegation that may be backing it.

Fixes: 0e3d3e5df07dc ("NFSv4.1 fix infinite loop on IO BAD_STATEID error")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Cc: stable@vger.kernel.org # v4.11+
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c

index 77c7d29fcd3b8d26c2eddf6c4ef521c80b0da5be..a3b67d3b1dfb70371bd908293216dfc1b1c60b71 100644 (file)
@@ -2533,14 +2533,18 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state)
        }
 
        nfs4_stateid_copy(&stateid, &delegation->stateid);
-       if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) ||
-               !test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED,
-                       &delegation->flags)) {
+       if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
                rcu_read_unlock();
                nfs_finish_clear_delegation_stateid(state, &stateid);
                return;
        }
 
+       if (!test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED,
+                               &delegation->flags)) {
+               rcu_read_unlock();
+               return;
+       }
+
        cred = get_rpccred(delegation->cred);
        rcu_read_unlock();
        status = nfs41_test_and_free_expired_stateid(server, &stateid, cred);
index 45873ed92057b329fe0b05450d97a3626fde49ff..e1d88bca815e14d09eef609880978ccad56b0c7c 100644 (file)
@@ -1354,6 +1354,8 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
 
        if (!nfs4_state_mark_reclaim_nograce(clp, state))
                return -EBADF;
+       nfs_inode_find_delegation_state_and_recover(state->inode,
+                       &state->stateid);
        dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
                        clp->cl_hostname);
        nfs4_schedule_state_manager(clp);