summary refs log tree commit diff
path: root/fs/fuse/dev.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-04-26 10:49:06 +0200
committerMiklos Szeredi <miklos@szeredi.hu>2006-04-26 10:49:06 +0200
commit6dbbcb120570d747b00783820ee02d1e1bcf63de (patch)
tree9be2938cea30b1f2ce93ed59b8806fdfac62fd1e /fs/fuse/dev.c
parent5a5fb1ea74d8b82ca1461b885a1334fb21e037be (diff)
downloadlinux-6dbbcb120570d747b00783820ee02d1e1bcf63de.tar.gz
[fuse] fix deadlock between fuse_put_super() and request_end(), try #2
A deadlock was possible, when the last reference to the superblock was
held due to a background request containing a file reference.

Releasing the file would release the vfsmount which in turn would
release the superblock.  Since sbput_sem is held during the fput() and
fuse_put_super() tries to acquire this same semaphore, a deadlock
results.

The solution is to move the fput() outside the region protected by
sbput_sem.

Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r--fs/fuse/dev.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 4967bd40b953..104a62dadb94 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -128,12 +128,16 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 	}
 }
 
+/*
+ * Called with sbput_sem held for read (request_end) or write
+ * (fuse_put_super).  By the time fuse_put_super() is finished, all
+ * inodes belonging to background requests must be released, so the
+ * iputs have to be done within the locked region.
+ */
 void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
 {
 	iput(req->inode);
 	iput(req->inode2);
-	if (req->file)
-		fput(req->file);
 	spin_lock(&fc->lock);
 	list_del(&req->bg_entry);
 	if (fc->num_background == FUSE_MAX_BACKGROUND) {
@@ -178,6 +182,11 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
 		if (fc->mounted)
 			fuse_release_background(fc, req);
 		up_read(&fc->sbput_sem);
+
+		/* fput must go outside sbput_sem, otherwise it can deadlock */
+		if (req->file)
+			fput(req->file);
+
 		if (end)
 			end(fc, req);
 		else