aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-01-17 01:14:41 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-17 02:15:30 -0500
commit69a53bf267fa58b89aa659d121dfe38436562a30 (patch)
tree36276fdbf7bfdc787708e8d5b2d4b79a0b5a4c56
parent0cd5b88553acf0611474dbaf8e43770eed268060 (diff)
[PATCH] fuse: add connection aborting
Add ability to abort a filesystem connection. With the introduction of asynchronous reads, the ability to interrupt any request is not enough to dissolve deadlocks, since now waiting for the request completion (page unlocked) is independent of the actual request, so in a deadlock all threads will be uninterruptible. The solution is to make it possible to abort all requests, even those currently undergoing I/O to/from userspace. The natural interface for this is 'mount -f mountpoint', but that only works as long as the filesystem is attached. So also add an 'abort' attribute to the sysfs view of the connection. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/fuse/dev.c71
-rw-r--r--fs/fuse/fuse_i.h7
-rw-r--r--fs/fuse/inode.c16
3 files changed, 88 insertions, 6 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index c72e44b58d09..60c222517ccd 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -260,11 +260,13 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
260 wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED); 260 wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED);
261 restore_sigs(&oldset); 261 restore_sigs(&oldset);
262 spin_lock(&fuse_lock); 262 spin_lock(&fuse_lock);
263 if (req->state == FUSE_REQ_FINISHED) 263 if (req->state == FUSE_REQ_FINISHED && !req->interrupted)
264 return; 264 return;
265 265
266 req->out.h.error = -EINTR; 266 if (!req->interrupted) {
267 req->interrupted = 1; 267 req->out.h.error = -EINTR;
268 req->interrupted = 1;
269 }
268 if (req->locked) { 270 if (req->locked) {
269 /* This is uninterruptible sleep, because data is 271 /* This is uninterruptible sleep, because data is
270 being copied to/from the buffers of req. During 272 being copied to/from the buffers of req. During
@@ -770,6 +772,10 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
770 goto err_finish; 772 goto err_finish;
771 773
772 spin_lock(&fuse_lock); 774 spin_lock(&fuse_lock);
775 err = -ENOENT;
776 if (!fc->connected)
777 goto err_unlock;
778
773 req = request_find(fc, oh.unique); 779 req = request_find(fc, oh.unique);
774 err = -EINVAL; 780 err = -EINVAL;
775 if (!req) 781 if (!req)
@@ -836,7 +842,11 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait)
836 return mask; 842 return mask;
837} 843}
838 844
839/* Abort all requests on the given list (pending or processing) */ 845/*
846 * Abort all requests on the given list (pending or processing)
847 *
848 * This function releases and reacquires fuse_lock
849 */
840static void end_requests(struct fuse_conn *fc, struct list_head *head) 850static void end_requests(struct fuse_conn *fc, struct list_head *head)
841{ 851{
842 while (!list_empty(head)) { 852 while (!list_empty(head)) {
@@ -848,6 +858,59 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
848 } 858 }
849} 859}
850 860
861/*
862 * Abort requests under I/O
863 *
864 * The requests are set to interrupted and finished, and the request
865 * waiter is woken up. This will make request_wait_answer() wait
866 * until the request is unlocked and then return.
867 */
868static void end_io_requests(struct fuse_conn *fc)
869{
870 while (!list_empty(&fc->io)) {
871 struct fuse_req *req;
872 req = list_entry(fc->io.next, struct fuse_req, list);
873 req->interrupted = 1;
874 req->out.h.error = -ECONNABORTED;
875 req->state = FUSE_REQ_FINISHED;
876 list_del_init(&req->list);
877 wake_up(&req->waitq);
878 }
879}
880
881/*
882 * Abort all requests.
883 *
884 * Emergency exit in case of a malicious or accidental deadlock, or
885 * just a hung filesystem.
886 *
887 * The same effect is usually achievable through killing the
888 * filesystem daemon and all users of the filesystem. The exception
889 * is the combination of an asynchronous request and the tricky
890 * deadlock (see Documentation/filesystems/fuse.txt).
891 *
892 * During the aborting, progression of requests from the pending and
893 * processing lists onto the io list, and progression of new requests
894 * onto the pending list is prevented by req->connected being false.
895 *
896 * Progression of requests under I/O to the processing list is
897 * prevented by the req->interrupted flag being true for these
898 * requests. For this reason requests on the io list must be aborted
899 * first.
900 */
901void fuse_abort_conn(struct fuse_conn *fc)
902{
903 spin_lock(&fuse_lock);
904 if (fc->connected) {
905 fc->connected = 0;
906 end_io_requests(fc);
907 end_requests(fc, &fc->pending);
908 end_requests(fc, &fc->processing);
909 wake_up_all(&fc->waitq);
910 }
911 spin_unlock(&fuse_lock);
912}
913
851static int fuse_dev_release(struct inode *inode, struct file *file) 914static int fuse_dev_release(struct inode *inode, struct file *file)
852{ 915{
853 struct fuse_conn *fc; 916 struct fuse_conn *fc;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index bcb453f68111..e6381db41df9 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -246,8 +246,8 @@ struct fuse_conn {
246 /** Mount is active */ 246 /** Mount is active */
247 unsigned mounted : 1; 247 unsigned mounted : 1;
248 248
249 /** Connection established, cleared on umount and device 249 /** Connection established, cleared on umount, connection
250 release */ 250 abort and device release */
251 unsigned connected : 1; 251 unsigned connected : 1;
252 252
253 /** Connection failed (version mismatch) */ 253 /** Connection failed (version mismatch) */
@@ -463,6 +463,9 @@ void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
463 */ 463 */
464void fuse_release_background(struct fuse_req *req); 464void fuse_release_background(struct fuse_req *req);
465 465
466/* Abort all requests */
467void fuse_abort_conn(struct fuse_conn *fc);
468
466/** 469/**
467 * Get the attributes of a file 470 * Get the attributes of a file
468 */ 471 */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 182235923cdd..d359d8de22a4 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -196,6 +196,11 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
196 return inode; 196 return inode;
197} 197}
198 198
199static void fuse_umount_begin(struct super_block *sb)
200{
201 fuse_abort_conn(get_fuse_conn_super(sb));
202}
203
199static void fuse_put_super(struct super_block *sb) 204static void fuse_put_super(struct super_block *sb)
200{ 205{
201 struct fuse_conn *fc = get_fuse_conn_super(sb); 206 struct fuse_conn *fc = get_fuse_conn_super(sb);
@@ -454,6 +459,7 @@ static struct super_operations fuse_super_operations = {
454 .read_inode = fuse_read_inode, 459 .read_inode = fuse_read_inode,
455 .clear_inode = fuse_clear_inode, 460 .clear_inode = fuse_clear_inode,
456 .put_super = fuse_put_super, 461 .put_super = fuse_put_super,
462 .umount_begin = fuse_umount_begin,
457 .statfs = fuse_statfs, 463 .statfs = fuse_statfs,
458 .show_options = fuse_show_options, 464 .show_options = fuse_show_options,
459}; 465};
@@ -560,11 +566,21 @@ static ssize_t fuse_conn_waiting_show(struct fuse_conn *fc, char *page)
560 return sprintf(page, "%i\n", atomic_read(&fc->num_waiting)); 566 return sprintf(page, "%i\n", atomic_read(&fc->num_waiting));
561} 567}
562 568
569static ssize_t fuse_conn_abort_store(struct fuse_conn *fc, const char *page,
570 size_t count)
571{
572 fuse_abort_conn(fc);
573 return count;
574}
575
563static struct fuse_conn_attr fuse_conn_waiting = 576static struct fuse_conn_attr fuse_conn_waiting =
564 __ATTR(waiting, 0400, fuse_conn_waiting_show, NULL); 577 __ATTR(waiting, 0400, fuse_conn_waiting_show, NULL);
578static struct fuse_conn_attr fuse_conn_abort =
579 __ATTR(abort, 0600, NULL, fuse_conn_abort_store);
565 580
566static struct attribute *fuse_conn_attrs[] = { 581static struct attribute *fuse_conn_attrs[] = {
567 &fuse_conn_waiting.attr, 582 &fuse_conn_waiting.attr,
583 &fuse_conn_abort.attr,
568 NULL, 584 NULL,
569}; 585};
570 586