diff options
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 78 |
1 files changed, 59 insertions, 19 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index e8f3170946f1..ca6fc0e96d7c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -26,7 +26,7 @@ static inline struct fuse_conn *fuse_get_conn(struct file *file) | |||
26 | struct fuse_conn *fc; | 26 | struct fuse_conn *fc; |
27 | spin_lock(&fuse_lock); | 27 | spin_lock(&fuse_lock); |
28 | fc = file->private_data; | 28 | fc = file->private_data; |
29 | if (fc && !fc->sb) | 29 | if (fc && !fc->mounted) |
30 | fc = NULL; | 30 | fc = NULL; |
31 | spin_unlock(&fuse_lock); | 31 | spin_unlock(&fuse_lock); |
32 | return fc; | 32 | return fc; |
@@ -148,6 +148,17 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | |||
148 | fuse_putback_request(fc, req); | 148 | fuse_putback_request(fc, req); |
149 | } | 149 | } |
150 | 150 | ||
151 | void fuse_release_background(struct fuse_req *req) | ||
152 | { | ||
153 | iput(req->inode); | ||
154 | iput(req->inode2); | ||
155 | if (req->file) | ||
156 | fput(req->file); | ||
157 | spin_lock(&fuse_lock); | ||
158 | list_del(&req->bg_entry); | ||
159 | spin_unlock(&fuse_lock); | ||
160 | } | ||
161 | |||
151 | /* | 162 | /* |
152 | * This function is called when a request is finished. Either a reply | 163 | * This function is called when a request is finished. Either a reply |
153 | * has arrived or it was interrupted (and not yet sent) or some error | 164 | * has arrived or it was interrupted (and not yet sent) or some error |
@@ -166,12 +177,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
166 | putback = atomic_dec_and_test(&req->count); | 177 | putback = atomic_dec_and_test(&req->count); |
167 | spin_unlock(&fuse_lock); | 178 | spin_unlock(&fuse_lock); |
168 | if (req->background) { | 179 | if (req->background) { |
169 | if (req->inode) | 180 | down_read(&fc->sbput_sem); |
170 | iput(req->inode); | 181 | if (fc->mounted) |
171 | if (req->inode2) | 182 | fuse_release_background(req); |
172 | iput(req->inode2); | 183 | up_read(&fc->sbput_sem); |
173 | if (req->file) | ||
174 | fput(req->file); | ||
175 | } | 184 | } |
176 | wake_up(&req->waitq); | 185 | wake_up(&req->waitq); |
177 | if (req->in.h.opcode == FUSE_INIT) { | 186 | if (req->in.h.opcode == FUSE_INIT) { |
@@ -191,11 +200,39 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
191 | fuse_putback_request(fc, req); | 200 | fuse_putback_request(fc, req); |
192 | } | 201 | } |
193 | 202 | ||
194 | static void background_request(struct fuse_req *req) | 203 | /* |
204 | * Unfortunately request interruption not just solves the deadlock | ||
205 | * problem, it causes problems too. These stem from the fact, that an | ||
206 | * interrupted request is continued to be processed in userspace, | ||
207 | * while all the locks and object references (inode and file) held | ||
208 | * during the operation are released. | ||
209 | * | ||
210 | * To release the locks is exactly why there's a need to interrupt the | ||
211 | * request, so there's not a lot that can be done about this, except | ||
212 | * introduce additional locking in userspace. | ||
213 | * | ||
214 | * More important is to keep inode and file references until userspace | ||
215 | * has replied, otherwise FORGET and RELEASE could be sent while the | ||
216 | * inode/file is still used by the filesystem. | ||
217 | * | ||
218 | * For this reason the concept of "background" request is introduced. | ||
219 | * An interrupted request is backgrounded if it has been already sent | ||
220 | * to userspace. Backgrounding involves getting an extra reference to | ||
221 | * inode(s) or file used in the request, and adding the request to | ||
222 | * fc->background list. When a reply is received for a background | ||
223 | * request, the object references are released, and the request is | ||
224 | * removed from the list. If the filesystem is unmounted while there | ||
225 | * are still background requests, the list is walked and references | ||
226 | * are released as if a reply was received. | ||
227 | * | ||
228 | * There's one more use for a background request. The RELEASE message is | ||
229 | * always sent as background, since it doesn't return an error or | ||
230 | * data. | ||
231 | */ | ||
232 | static void background_request(struct fuse_conn *fc, struct fuse_req *req) | ||
195 | { | 233 | { |
196 | /* Need to get hold of the inode(s) and/or file used in the | ||
197 | request, so FORGET and RELEASE are not sent too early */ | ||
198 | req->background = 1; | 234 | req->background = 1; |
235 | list_add(&req->bg_entry, &fc->background); | ||
199 | if (req->inode) | 236 | if (req->inode) |
200 | req->inode = igrab(req->inode); | 237 | req->inode = igrab(req->inode); |
201 | if (req->inode2) | 238 | if (req->inode2) |
@@ -215,7 +252,8 @@ static int request_wait_answer_nonint(struct fuse_req *req) | |||
215 | } | 252 | } |
216 | 253 | ||
217 | /* Called with fuse_lock held. Releases, and then reacquires it. */ | 254 | /* Called with fuse_lock held. Releases, and then reacquires it. */ |
218 | static void request_wait_answer(struct fuse_req *req, int interruptible) | 255 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req, |
256 | int interruptible) | ||
219 | { | 257 | { |
220 | int intr; | 258 | int intr; |
221 | 259 | ||
@@ -255,7 +293,7 @@ static void request_wait_answer(struct fuse_req *req, int interruptible) | |||
255 | list_del(&req->list); | 293 | list_del(&req->list); |
256 | __fuse_put_request(req); | 294 | __fuse_put_request(req); |
257 | } else if (!req->finished && req->sent) | 295 | } else if (!req->finished && req->sent) |
258 | background_request(req); | 296 | background_request(fc, req); |
259 | } | 297 | } |
260 | 298 | ||
261 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) | 299 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) |
@@ -297,7 +335,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, | |||
297 | { | 335 | { |
298 | req->isreply = 1; | 336 | req->isreply = 1; |
299 | spin_lock(&fuse_lock); | 337 | spin_lock(&fuse_lock); |
300 | if (!fc->file) | 338 | if (!fc->connected) |
301 | req->out.h.error = -ENOTCONN; | 339 | req->out.h.error = -ENOTCONN; |
302 | else if (fc->conn_error) | 340 | else if (fc->conn_error) |
303 | req->out.h.error = -ECONNREFUSED; | 341 | req->out.h.error = -ECONNREFUSED; |
@@ -307,7 +345,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, | |||
307 | after request_end() */ | 345 | after request_end() */ |
308 | __fuse_get_request(req); | 346 | __fuse_get_request(req); |
309 | 347 | ||
310 | request_wait_answer(req, interruptible); | 348 | request_wait_answer(fc, req, interruptible); |
311 | } | 349 | } |
312 | spin_unlock(&fuse_lock); | 350 | spin_unlock(&fuse_lock); |
313 | } | 351 | } |
@@ -330,7 +368,7 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req) | |||
330 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) | 368 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) |
331 | { | 369 | { |
332 | spin_lock(&fuse_lock); | 370 | spin_lock(&fuse_lock); |
333 | if (fc->file) { | 371 | if (fc->connected) { |
334 | queue_request(fc, req); | 372 | queue_request(fc, req); |
335 | spin_unlock(&fuse_lock); | 373 | spin_unlock(&fuse_lock); |
336 | } else { | 374 | } else { |
@@ -348,7 +386,9 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) | |||
348 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req) | 386 | void request_send_background(struct fuse_conn *fc, struct fuse_req *req) |
349 | { | 387 | { |
350 | req->isreply = 1; | 388 | req->isreply = 1; |
351 | background_request(req); | 389 | spin_lock(&fuse_lock); |
390 | background_request(fc, req); | ||
391 | spin_unlock(&fuse_lock); | ||
352 | request_send_nowait(fc, req); | 392 | request_send_nowait(fc, req); |
353 | } | 393 | } |
354 | 394 | ||
@@ -583,7 +623,7 @@ static void request_wait(struct fuse_conn *fc) | |||
583 | DECLARE_WAITQUEUE(wait, current); | 623 | DECLARE_WAITQUEUE(wait, current); |
584 | 624 | ||
585 | add_wait_queue_exclusive(&fc->waitq, &wait); | 625 | add_wait_queue_exclusive(&fc->waitq, &wait); |
586 | while (fc->sb && list_empty(&fc->pending)) { | 626 | while (fc->mounted && list_empty(&fc->pending)) { |
587 | set_current_state(TASK_INTERRUPTIBLE); | 627 | set_current_state(TASK_INTERRUPTIBLE); |
588 | if (signal_pending(current)) | 628 | if (signal_pending(current)) |
589 | break; | 629 | break; |
@@ -622,7 +662,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
622 | goto err_unlock; | 662 | goto err_unlock; |
623 | request_wait(fc); | 663 | request_wait(fc); |
624 | err = -ENODEV; | 664 | err = -ENODEV; |
625 | if (!fc->sb) | 665 | if (!fc->mounted) |
626 | goto err_unlock; | 666 | goto err_unlock; |
627 | err = -ERESTARTSYS; | 667 | err = -ERESTARTSYS; |
628 | if (list_empty(&fc->pending)) | 668 | if (list_empty(&fc->pending)) |
@@ -839,7 +879,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file) | |||
839 | spin_lock(&fuse_lock); | 879 | spin_lock(&fuse_lock); |
840 | fc = file->private_data; | 880 | fc = file->private_data; |
841 | if (fc) { | 881 | if (fc) { |
842 | fc->file = NULL; | 882 | fc->connected = 0; |
843 | end_requests(fc, &fc->pending); | 883 | end_requests(fc, &fc->pending); |
844 | end_requests(fc, &fc->processing); | 884 | end_requests(fc, &fc->processing); |
845 | fuse_release_conn(fc); | 885 | fuse_release_conn(fc); |