diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2006-04-26 04:49:06 -0400 |
---|---|---|
committer | Miklos Szeredi <miklos@szeredi.hu> | 2006-04-26 04:49:06 -0400 |
commit | 6dbbcb120570d747b00783820ee02d1e1bcf63de (patch) | |
tree | 9be2938cea30b1f2ce93ed59b8806fdfac62fd1e /fs/fuse | |
parent | 5a5fb1ea74d8b82ca1461b885a1334fb21e037be (diff) |
[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')
-rw-r--r-- | fs/fuse/dev.c | 13 |
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) | |||
128 | } | 128 | } |
129 | } | 129 | } |
130 | 130 | ||
131 | /* | ||
132 | * Called with sbput_sem held for read (request_end) or write | ||
133 | * (fuse_put_super). By the time fuse_put_super() is finished, all | ||
134 | * inodes belonging to background requests must be released, so the | ||
135 | * iputs have to be done within the locked region. | ||
136 | */ | ||
131 | void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req) | 137 | void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req) |
132 | { | 138 | { |
133 | iput(req->inode); | 139 | iput(req->inode); |
134 | iput(req->inode2); | 140 | iput(req->inode2); |
135 | if (req->file) | ||
136 | fput(req->file); | ||
137 | spin_lock(&fc->lock); | 141 | spin_lock(&fc->lock); |
138 | list_del(&req->bg_entry); | 142 | list_del(&req->bg_entry); |
139 | if (fc->num_background == FUSE_MAX_BACKGROUND) { | 143 | if (fc->num_background == FUSE_MAX_BACKGROUND) { |
@@ -178,6 +182,11 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
178 | if (fc->mounted) | 182 | if (fc->mounted) |
179 | fuse_release_background(fc, req); | 183 | fuse_release_background(fc, req); |
180 | up_read(&fc->sbput_sem); | 184 | up_read(&fc->sbput_sem); |
185 | |||
186 | /* fput must go outside sbput_sem, otherwise it can deadlock */ | ||
187 | if (req->file) | ||
188 | fput(req->file); | ||
189 | |||
181 | if (end) | 190 | if (end) |
182 | end(fc, req); | 191 | end(fc, req); |
183 | else | 192 | else |