aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trondmy@gmail.com>2018-09-05 14:07:14 -0400
committerAnna Schumaker <Anna.Schumaker@Netapp.com>2018-09-14 16:24:11 -0400
commit994b15b983a72e1148a173b61e5b279219bb45ae (patch)
tree31d957402cc7175002c8e8dcf507d768fa0d536d
parent2edaead69e7573f35e8d5dc20938e41eacc21b35 (diff)
NFSv4.1 fix infinite loop on I/O.
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>
-rw-r--r--fs/nfs/nfs4proc.c10
-rw-r--r--fs/nfs/nfs4state.c2
2 files changed, 9 insertions, 3 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index df60dce935f3..094c3c09ff00 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2676,14 +2676,18 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state)
2676 } 2676 }
2677 2677
2678 nfs4_stateid_copy(&stateid, &delegation->stateid); 2678 nfs4_stateid_copy(&stateid, &delegation->stateid);
2679 if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) || 2679 if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
2680 !test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED,
2681 &delegation->flags)) {
2682 rcu_read_unlock(); 2680 rcu_read_unlock();
2683 nfs_finish_clear_delegation_stateid(state, &stateid); 2681 nfs_finish_clear_delegation_stateid(state, &stateid);
2684 return; 2682 return;
2685 } 2683 }
2686 2684
2685 if (!test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED,
2686 &delegation->flags)) {
2687 rcu_read_unlock();
2688 return;
2689 }
2690
2687 cred = get_rpccred(delegation->cred); 2691 cred = get_rpccred(delegation->cred);
2688 rcu_read_unlock(); 2692 rcu_read_unlock();
2689 status = nfs41_test_and_free_expired_stateid(server, &stateid, cred); 2693 status = nfs41_test_and_free_expired_stateid(server, &stateid, cred);
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 3df0eb52da1c..40a08cd483f0 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1390,6 +1390,8 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
1390 1390
1391 if (!nfs4_state_mark_reclaim_nograce(clp, state)) 1391 if (!nfs4_state_mark_reclaim_nograce(clp, state))
1392 return -EBADF; 1392 return -EBADF;
1393 nfs_inode_find_delegation_state_and_recover(state->inode,
1394 &state->stateid);
1393 dprintk("%s: scheduling stateid recovery for server %s\n", __func__, 1395 dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
1394 clp->cl_hostname); 1396 clp->cl_hostname);
1395 nfs4_schedule_state_manager(clp); 1397 nfs4_schedule_state_manager(clp);