diff options
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 71 |
1 files changed, 67 insertions, 4 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 | */ | ||
840 | static void end_requests(struct fuse_conn *fc, struct list_head *head) | 850 | static 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 | */ | ||
868 | static 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 | */ | ||
901 | void 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 | |||
851 | static int fuse_dev_release(struct inode *inode, struct file *file) | 914 | static int fuse_dev_release(struct inode *inode, struct file *file) |
852 | { | 915 | { |
853 | struct fuse_conn *fc; | 916 | struct fuse_conn *fc; |