aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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