From: Trond Myklebust <trond.myklebust@fys.uio.no>

Fix a bug in the NFS write code whereby writepage() may end up deadlocking
on clear_inode().



---

 fs/nfs/write.c |   16 +++++++++++++++-
 1 files changed, 15 insertions(+), 1 deletion(-)

diff -puN fs/nfs/write.c~nfs-client-deadlock-fix fs/nfs/write.c
--- 25/fs/nfs/write.c~nfs-client-deadlock-fix	2004-01-08 18:38:45.000000000 -0800
+++ 25-akpm/fs/nfs/write.c	2004-01-08 18:38:45.000000000 -0800
@@ -228,8 +228,19 @@ nfs_writepage(struct page *page, struct 
 	unsigned long end_index;
 	unsigned offset = PAGE_CACHE_SIZE;
 	loff_t i_size = i_size_read(inode);
+	int inode_referenced = 0;
 	int err;
 
+	/*
+	 * Note: We need to ensure that we have a reference to the inode
+	 *       if we are to do asynchronous writes. If not, waiting
+	 *       in nfs_wait_on_request() may deadlock with clear_inode().
+	 *
+	 *       If igrab() fails here, then it is in any case safe to
+	 *       call nfs_wb_page(), since there will be no pending writes.
+	 */
+	if (igrab(inode) != 0)
+		inode_referenced = 1;
 	end_index = i_size >> PAGE_CACHE_SHIFT;
 
 	/* Ensure we've flushed out any previous writes */
@@ -247,7 +258,8 @@ nfs_writepage(struct page *page, struct 
 		goto out;
 do_it:
 	lock_kernel();
-	if (NFS_SERVER(inode)->wsize >= PAGE_CACHE_SIZE && !IS_SYNC(inode)) {
+	if (NFS_SERVER(inode)->wsize >= PAGE_CACHE_SIZE && !IS_SYNC(inode) &&
+			inode_referenced) {
 		err = nfs_writepage_async(NULL, inode, page, 0, offset);
 		if (err >= 0)
 			err = 0;
@@ -259,6 +271,8 @@ do_it:
 	unlock_kernel();
 out:
 	unlock_page(page);
+	if (inode_referenced)
+		iput(inode);
 	return err; 
 }
 

_