lockd: Fix server-side lock blocking code

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index d50946d..1d3a74d 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -193,6 +193,7 @@
 		goto failed_free;
 
 	/* Set notifier function for VFS, and init args */
+	block->b_call.a_args.lock.fl.fl_flags |= FL_SLEEP;
 	block->b_call.a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations;
 	block->b_call.a_args.cookie = *cookie;	/* see above */
 
@@ -228,19 +229,18 @@
  * can be closed hereafter.
  */
 static int
-nlmsvc_delete_block(struct nlm_block *block, int unlock)
+nlmsvc_delete_block(struct nlm_block *block)
 {
 	struct file_lock	*fl = &block->b_call.a_args.lock.fl;
 	struct nlm_file		*file = block->b_file;
 	struct nlm_block	**bp;
-	int status = 0;
+	int status;
 
 	dprintk("lockd: deleting block %p...\n", block);
 
 	/* Remove block from list */
 	nlmsvc_remove_block(block);
-	if (unlock)
-		status = posix_unblock_lock(file->f_file, fl);
+	status = posix_unblock_lock(file->f_file, fl);
 
 	/* If the block is in the middle of a GRANT callback,
 	 * don't kill it yet. */
@@ -282,7 +282,7 @@
 			block->b_host->h_inuse = 1;
 		else if (action == NLM_ACT_UNLOCK) {
 			if (host == NULL || host == block->b_host)
-				nlmsvc_delete_block(block, 1);
+				nlmsvc_delete_block(block);
 		}
 	}
 	up(&file->f_sema);
@@ -297,7 +297,7 @@
 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
 			struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
 {
-	struct nlm_block	*block;
+	struct nlm_block	*block, *newblock = NULL;
 	int			error;
 	u32			ret;
 
@@ -310,59 +310,65 @@
 				wait);
 
 
-	/* Get existing block (in case client is busy-waiting) */
-	block = nlmsvc_lookup_block(file, lock, 0);
-
+	lock->fl.fl_flags &= ~FL_SLEEP;
 again:
 	/* Lock file against concurrent access */
 	down(&file->f_sema);
+	/* Get existing block (in case client is busy-waiting) */
+	block = nlmsvc_lookup_block(file, lock, 0);
+	if (block == NULL) {
+		if (newblock != NULL)
+			lock = &newblock->b_call.a_args.lock.fl;
+	} else
+		lock = &block->b_call.a_args.lock.fl;
 
 	error = posix_lock_file(file->f_file, &lock->fl);
+	lock->fl.fl_flags &= ~FL_SLEEP;
 
 	dprintk("lockd: posix_lock_file returned %d\n", error);
 
-	if (error != -EAGAIN) {
-		if (block)
-			nlmsvc_delete_block(block, 0);
-		up(&file->f_sema);
-
-		switch(-error) {
+	switch(error) {
 		case 0:
 			ret = nlm_granted;
 			goto out;
-		case EDEADLK:
+		case -EAGAIN:
+			break;
+		case -EDEADLK:
 			ret = nlm_deadlock;
 			goto out;
 		default:			/* includes ENOLCK */
 			ret = nlm_lck_denied_nolocks;
 			goto out;
-		}
 	}
 
-	if (!wait) {
-		ret = nlm_lck_denied;
-		goto out_unlock;
-	}
+	ret = nlm_lck_denied;
+	if (!wait)
+		goto out;
+
+	ret = nlm_lck_blocked;
+	if (block != NULL)
+		goto out;
 
 	/* If we don't have a block, create and initialize it. Then
 	 * retry because we may have slept in kmalloc. */
 	/* We have to release f_sema as nlmsvc_create_block may try to
 	 * to claim it while doing host garbage collection */
-	if (block == NULL) {
+	if (newblock == NULL) {
 		up(&file->f_sema);
 		dprintk("lockd: blocking on this lock (allocating).\n");
-		if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
+		if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie)))
 			return nlm_lck_denied_nolocks;
 		goto again;
 	}
 
 	/* Append to list of blocked */
-	nlmsvc_insert_block(block, NLM_NEVER);
+	nlmsvc_insert_block(newblock, NLM_NEVER);
+	newblock = NULL;
 
-	ret = nlm_lck_blocked;
-out_unlock:
-	up(&file->f_sema);
 out:
+	up(&file->f_sema);
+	if (newblock != NULL)
+		nlmsvc_delete_block(newblock);
 	dprintk("lockd: nlmsvc_lock returned %u\n", ret);
 	return ret;
 }
@@ -445,7 +451,7 @@
 
 	down(&file->f_sema);
 	if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL)
-		status = nlmsvc_delete_block(block, 1);
+		status = nlmsvc_delete_block(block);
 	up(&file->f_sema);
 	return status ? nlm_lck_denied : nlm_granted;
 }
@@ -519,7 +525,11 @@
 	}
 
 	/* Try the lock operation again */
+	posix_unblock_lock(file->f_file, &lock->fl);
+	lock->fl.fl_flags |= FL_SLEEP;
 	error = posix_lock_file(file->f_file, &lock->fl);
+	lock->fl.fl_flags &= ~FL_SLEEP;
+
 	switch (error) {
 	case 0:
 		break;
@@ -630,11 +640,8 @@
 		} else {
 			/* Lock is now held by client, or has been rejected.
 			 * In both cases, the block should be removed. */
+			nlmsvc_delete_block(block);
 			up(&file->f_sema);
-			if (status == NLM_LCK_GRANTED)
-				nlmsvc_delete_block(block, 0);
-			else
-				nlmsvc_delete_block(block, 1);
 		}
 	}
 	nlm_release_file(file);
@@ -661,7 +668,7 @@
 		dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
 			block, block->b_when, block->b_done);
 		if (block->b_done)
-			nlmsvc_delete_block(block, 0);
+			nlmsvc_delete_block(block);
 		else
 			nlmsvc_grant_blocked(block);
 	}