From: NeilBrown <neilb@cse.unsw.edu.au>

We weren't taking the reference count in the delegation recall thread until
we'd already started the new delegation thread (and possibly returned from the
break_lease callback).  That's too late.

Also document some assumptions.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/nfsd/nfs4state.c |   14 ++++++++++----
 1 files changed, 10 insertions(+), 4 deletions(-)

diff -puN fs/nfsd/nfs4state.c~nfsd4-fix-cb-race fs/nfsd/nfs4state.c
--- 25/fs/nfsd/nfs4state.c~nfsd4-fix-cb-race	2005-03-07 23:55:53.000000000 -0800
+++ 25-akpm/fs/nfsd/nfs4state.c	2005-03-07 23:55:53.000000000 -0800
@@ -1345,7 +1345,6 @@ do_recall(void *__dp)
 
 	daemonize("nfsv4-recall");
 
-	atomic_inc(&dp->dl_count);
 	nfsd4_cb_recall(dp);
 	return 0;
 }
@@ -1354,8 +1353,9 @@ do_recall(void *__dp)
  * Spawn a thread to perform a recall on the delegation represented
  * by the lease (file_lock)
  *
- * Called from break_lease() with lock_kernel() held,
- *
+ * Called from break_lease() with lock_kernel() held.
+ * Note: we assume break_lease will only call this *once* for any given
+ * lease.
  */
 static
 void nfsd_break_deleg_cb(struct file_lock *fl)
@@ -1367,7 +1367,13 @@ void nfsd_break_deleg_cb(struct file_loc
 	if (!dp)
 		return;
 
-	/* schedule delegation for recall */
+	/* We're assuming the state code never drops its reference
+	 * without first removing the lease.  Since we're in this lease
+	 * callback (and since the lease code is serialized by the kernel
+	 * lock) we know the server hasn't removed the lease yet, we know
+	 * it's safe to take a reference: */
+	atomic_inc(&dp->dl_count);
+
 	spin_lock(&recall_lock);
 	atomic_set(&dp->dl_state, NFS4_RECALL_IN_PROGRESS);
 	list_add_tail(&dp->dl_recall_lru, &del_recall_lru);
_