summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jeff.layton@primarydata.com>2014-07-22 13:52:06 -0400
committerJ. Bruce Fields <bfields@redhat.com>2014-07-22 15:34:47 -0400
commitd55a166c961714e18907f4723252f72097cd2d23 (patch)
treec8cd6088f54b2e85615107e3bfb59d25b8e6c3bf
parent72c0b0fb9f8a24612b6c33c8adf9e9406818981b (diff)
downloadlinux-stable-d55a166c961714e18907f4723252f72097cd2d23.tar.gz
linux-stable-d55a166c961714e18907f4723252f72097cd2d23.tar.bz2
linux-stable-d55a166c961714e18907f4723252f72097cd2d23.zip
nfsd: bump dl_time when unhashing delegation
There's a potential race between a lease break and DELEGRETURN call. Suppose a lease break comes in and queues the workqueue job for a delegation, but it doesn't run just yet. Then, a DELEGRETURN comes in finds the delegation and calls destroy_delegation on it to unhash it and put its primary reference. Next, the workqueue job runs and queues the delegation back onto the del_recall_lru list, issues the CB_RECALL and puts the final reference. With that, the final reference to the delegation is put, but it's still on the LRU list. When we go to unhash a delegation, it's because we intend to get rid of it soon afterward, so we don't want lease breaks to mess with it once that occurs. Fix this by bumping the dl_time whenever we unhash a delegation, to ensure that lease breaks don't monkey with it. I believe this is a regression due to commit 02e1215f9f7 (nfsd: Avoid taking state_lock while holding inode lock in nfsd_break_one_deleg). Prior to that, the state_lock was held in the lm_break callback itself, and that would have prevented this race. Cc: Trond Myklebust <trond.myklebust@primarydata.com> Signed-off-by: Jeff Layton <jlayton@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c2
1 files changed, 2 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 72da0d44e66b..a3a828d17563 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -660,6 +660,8 @@ unhash_delegation(struct nfs4_delegation *dp)
spin_lock(&state_lock);
dp->dl_stid.sc_type = NFS4_CLOSED_DELEG_STID;
+ /* Ensure that deleg break won't try to requeue it */
+ ++dp->dl_time;
spin_lock(&fp->fi_lock);
list_del_init(&dp->dl_perclnt);
list_del_init(&dp->dl_recall_lru);