diff options
| -rw-r--r-- | Documentation/filesystems/fuse.txt | 48 | ||||
| -rw-r--r-- | fs/fuse/dev.c | 162 | ||||
| -rw-r--r-- | fs/fuse/file.c | 3 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 16 | ||||
| -rw-r--r-- | fs/fuse/inode.c | 1 | ||||
| -rw-r--r-- | include/linux/fuse.h | 7 |
6 files changed, 205 insertions, 32 deletions
diff --git a/Documentation/filesystems/fuse.txt b/Documentation/filesystems/fuse.txt index 324df27704cc..a584f05403a4 100644 --- a/Documentation/filesystems/fuse.txt +++ b/Documentation/filesystems/fuse.txt | |||
| @@ -124,6 +124,46 @@ For each connection the following files exist within this directory: | |||
| 124 | 124 | ||
| 125 | Only the owner of the mount may read or write these files. | 125 | Only the owner of the mount may read or write these files. |
| 126 | 126 | ||
| 127 | Interrupting filesystem operations | ||
| 128 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 129 | |||
| 130 | If a process issuing a FUSE filesystem request is interrupted, the | ||
| 131 | following will happen: | ||
| 132 | |||
| 133 | 1) If the request is not yet sent to userspace AND the signal is | ||
| 134 | fatal (SIGKILL or unhandled fatal signal), then the request is | ||
| 135 | dequeued and returns immediately. | ||
| 136 | |||
| 137 | 2) If the request is not yet sent to userspace AND the signal is not | ||
| 138 | fatal, then an 'interrupted' flag is set for the request. When | ||
| 139 | the request has been successfully transfered to userspace and | ||
| 140 | this flag is set, an INTERRUPT request is queued. | ||
| 141 | |||
| 142 | 3) If the request is already sent to userspace, then an INTERRUPT | ||
| 143 | request is queued. | ||
| 144 | |||
| 145 | INTERRUPT requests take precedence over other requests, so the | ||
| 146 | userspace filesystem will receive queued INTERRUPTs before any others. | ||
| 147 | |||
| 148 | The userspace filesystem may ignore the INTERRUPT requests entirely, | ||
| 149 | or may honor them by sending a reply to the _original_ request, with | ||
| 150 | the error set to EINTR. | ||
| 151 | |||
| 152 | It is also possible that there's a race between processing the | ||
| 153 | original request and it's INTERRUPT request. There are two possibilities: | ||
| 154 | |||
| 155 | 1) The INTERRUPT request is processed before the original request is | ||
| 156 | processed | ||
| 157 | |||
| 158 | 2) The INTERRUPT request is processed after the original request has | ||
| 159 | been answered | ||
| 160 | |||
| 161 | If the filesystem cannot find the original request, it should wait for | ||
| 162 | some timeout and/or a number of new requests to arrive, after which it | ||
| 163 | should reply to the INTERRUPT request with an EAGAIN error. In case | ||
| 164 | 1) the INTERRUPT request will be requeued. In case 2) the INTERRUPT | ||
| 165 | reply will be ignored. | ||
| 166 | |||
| 127 | Aborting a filesystem connection | 167 | Aborting a filesystem connection |
| 128 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 168 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 129 | 169 | ||
| @@ -351,10 +391,10 @@ but is caused by a pagefault. | |||
| 351 | 391 | ||
| 352 | Solution is basically the same as above. | 392 | Solution is basically the same as above. |
| 353 | 393 | ||
| 354 | An additional problem is that while the write buffer is being | 394 | An additional problem is that while the write buffer is being copied |
| 355 | copied to the request, the request must not be interrupted. This | 395 | to the request, the request must not be interrupted/aborted. This is |
| 356 | is because the destination address of the copy may not be valid | 396 | because the destination address of the copy may not be valid after the |
| 357 | after the request is interrupted. | 397 | request has returned. |
| 358 | 398 | ||
| 359 | This is solved with doing the copy atomically, and allowing abort | 399 | This is solved with doing the copy atomically, and allowing abort |
| 360 | while the page(s) belonging to the write buffer are faulted with | 400 | while the page(s) belonging to the write buffer are faulted with |
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6b5f74cb7b54..1e2006caf158 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
| @@ -34,6 +34,7 @@ static void fuse_request_init(struct fuse_req *req) | |||
| 34 | { | 34 | { |
| 35 | memset(req, 0, sizeof(*req)); | 35 | memset(req, 0, sizeof(*req)); |
| 36 | INIT_LIST_HEAD(&req->list); | 36 | INIT_LIST_HEAD(&req->list); |
| 37 | INIT_LIST_HEAD(&req->intr_entry); | ||
| 37 | init_waitqueue_head(&req->waitq); | 38 | init_waitqueue_head(&req->waitq); |
| 38 | atomic_set(&req->count, 1); | 39 | atomic_set(&req->count, 1); |
| 39 | } | 40 | } |
| @@ -215,6 +216,7 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
| 215 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | 216 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; |
| 216 | req->end = NULL; | 217 | req->end = NULL; |
| 217 | list_del(&req->list); | 218 | list_del(&req->list); |
| 219 | list_del(&req->intr_entry); | ||
| 218 | req->state = FUSE_REQ_FINISHED; | 220 | req->state = FUSE_REQ_FINISHED; |
| 219 | if (req->background) { | 221 | if (req->background) { |
| 220 | if (fc->num_background == FUSE_MAX_BACKGROUND) { | 222 | if (fc->num_background == FUSE_MAX_BACKGROUND) { |
| @@ -235,28 +237,63 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
| 235 | fuse_put_request(fc, req); | 237 | fuse_put_request(fc, req); |
| 236 | } | 238 | } |
| 237 | 239 | ||
| 240 | static void wait_answer_interruptible(struct fuse_conn *fc, | ||
| 241 | struct fuse_req *req) | ||
| 242 | { | ||
| 243 | if (signal_pending(current)) | ||
| 244 | return; | ||
| 245 | |||
| 246 | spin_unlock(&fc->lock); | ||
| 247 | wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED); | ||
| 248 | spin_lock(&fc->lock); | ||
| 249 | } | ||
| 250 | |||
| 251 | static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req) | ||
| 252 | { | ||
| 253 | list_add_tail(&req->intr_entry, &fc->interrupts); | ||
| 254 | wake_up(&fc->waitq); | ||
| 255 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); | ||
| 256 | } | ||
| 257 | |||
| 238 | /* Called with fc->lock held. Releases, and then reacquires it. */ | 258 | /* Called with fc->lock held. Releases, and then reacquires it. */ |
| 239 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) | 259 | static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) |
| 240 | { | 260 | { |
| 241 | sigset_t oldset; | 261 | if (!fc->no_interrupt) { |
| 262 | /* Any signal may interrupt this */ | ||
| 263 | wait_answer_interruptible(fc, req); | ||
| 242 | 264 | ||
| 243 | spin_unlock(&fc->lock); | 265 | if (req->aborted) |
| 244 | if (req->force) | 266 | goto aborted; |
| 267 | if (req->state == FUSE_REQ_FINISHED) | ||
| 268 | return; | ||
| 269 | |||
| 270 | req->interrupted = 1; | ||
| 271 | if (req->state == FUSE_REQ_SENT) | ||
| 272 | queue_interrupt(fc, req); | ||
| 273 | } | ||
| 274 | |||
| 275 | if (req->force) { | ||
| 276 | spin_unlock(&fc->lock); | ||
| 245 | wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); | 277 | wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); |
| 246 | else { | 278 | spin_lock(&fc->lock); |
| 279 | } else { | ||
| 280 | sigset_t oldset; | ||
| 281 | |||
| 282 | /* Only fatal signals may interrupt this */ | ||
| 247 | block_sigs(&oldset); | 283 | block_sigs(&oldset); |
| 248 | wait_event_interruptible(req->waitq, | 284 | wait_answer_interruptible(fc, req); |
| 249 | req->state == FUSE_REQ_FINISHED); | ||
| 250 | restore_sigs(&oldset); | 285 | restore_sigs(&oldset); |
| 251 | } | 286 | } |
| 252 | spin_lock(&fc->lock); | ||
| 253 | if (req->state == FUSE_REQ_FINISHED && !req->aborted) | ||
| 254 | return; | ||
| 255 | 287 | ||
| 256 | if (!req->aborted) { | 288 | if (req->aborted) |
| 257 | req->out.h.error = -EINTR; | 289 | goto aborted; |
| 258 | req->aborted = 1; | 290 | if (req->state == FUSE_REQ_FINISHED) |
| 259 | } | 291 | return; |
| 292 | |||
| 293 | req->out.h.error = -EINTR; | ||
| 294 | req->aborted = 1; | ||
| 295 | |||
| 296 | aborted: | ||
| 260 | if (req->locked) { | 297 | if (req->locked) { |
| 261 | /* This is uninterruptible sleep, because data is | 298 | /* This is uninterruptible sleep, because data is |
| 262 | being copied to/from the buffers of req. During | 299 | being copied to/from the buffers of req. During |
| @@ -288,13 +325,19 @@ static unsigned len_args(unsigned numargs, struct fuse_arg *args) | |||
| 288 | return nbytes; | 325 | return nbytes; |
| 289 | } | 326 | } |
| 290 | 327 | ||
| 328 | static u64 fuse_get_unique(struct fuse_conn *fc) | ||
| 329 | { | ||
| 330 | fc->reqctr++; | ||
| 331 | /* zero is special */ | ||
| 332 | if (fc->reqctr == 0) | ||
| 333 | fc->reqctr = 1; | ||
| 334 | |||
| 335 | return fc->reqctr; | ||
| 336 | } | ||
| 337 | |||
| 291 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | 338 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) |
| 292 | { | 339 | { |
| 293 | fc->reqctr++; | 340 | req->in.h.unique = fuse_get_unique(fc); |
| 294 | /* zero is special */ | ||
| 295 | if (fc->reqctr == 0) | ||
| 296 | fc->reqctr = 1; | ||
| 297 | req->in.h.unique = fc->reqctr; | ||
| 298 | req->in.h.len = sizeof(struct fuse_in_header) + | 341 | req->in.h.len = sizeof(struct fuse_in_header) + |
| 299 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); | 342 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); |
| 300 | list_add_tail(&req->list, &fc->pending); | 343 | list_add_tail(&req->list, &fc->pending); |
| @@ -307,9 +350,6 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | |||
| 307 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); | 350 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); |
| 308 | } | 351 | } |
| 309 | 352 | ||
| 310 | /* | ||
| 311 | * This can only be interrupted by a SIGKILL | ||
| 312 | */ | ||
| 313 | void request_send(struct fuse_conn *fc, struct fuse_req *req) | 353 | void request_send(struct fuse_conn *fc, struct fuse_req *req) |
| 314 | { | 354 | { |
| 315 | req->isreply = 1; | 355 | req->isreply = 1; |
| @@ -566,13 +606,18 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, | |||
| 566 | return err; | 606 | return err; |
| 567 | } | 607 | } |
| 568 | 608 | ||
| 609 | static int request_pending(struct fuse_conn *fc) | ||
| 610 | { | ||
| 611 | return !list_empty(&fc->pending) || !list_empty(&fc->interrupts); | ||
| 612 | } | ||
| 613 | |||
| 569 | /* Wait until a request is available on the pending list */ | 614 | /* Wait until a request is available on the pending list */ |
| 570 | static void request_wait(struct fuse_conn *fc) | 615 | static void request_wait(struct fuse_conn *fc) |
| 571 | { | 616 | { |
| 572 | DECLARE_WAITQUEUE(wait, current); | 617 | DECLARE_WAITQUEUE(wait, current); |
| 573 | 618 | ||
| 574 | add_wait_queue_exclusive(&fc->waitq, &wait); | 619 | add_wait_queue_exclusive(&fc->waitq, &wait); |
| 575 | while (fc->connected && list_empty(&fc->pending)) { | 620 | while (fc->connected && !request_pending(fc)) { |
| 576 | set_current_state(TASK_INTERRUPTIBLE); | 621 | set_current_state(TASK_INTERRUPTIBLE); |
| 577 | if (signal_pending(current)) | 622 | if (signal_pending(current)) |
| 578 | break; | 623 | break; |
| @@ -586,6 +631,45 @@ static void request_wait(struct fuse_conn *fc) | |||
| 586 | } | 631 | } |
| 587 | 632 | ||
| 588 | /* | 633 | /* |
| 634 | * Transfer an interrupt request to userspace | ||
| 635 | * | ||
| 636 | * Unlike other requests this is assembled on demand, without a need | ||
| 637 | * to allocate a separate fuse_req structure. | ||
| 638 | * | ||
| 639 | * Called with fc->lock held, releases it | ||
| 640 | */ | ||
| 641 | static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_req *req, | ||
| 642 | const struct iovec *iov, unsigned long nr_segs) | ||
| 643 | { | ||
| 644 | struct fuse_copy_state cs; | ||
| 645 | struct fuse_in_header ih; | ||
| 646 | struct fuse_interrupt_in arg; | ||
| 647 | unsigned reqsize = sizeof(ih) + sizeof(arg); | ||
| 648 | int err; | ||
| 649 | |||
| 650 | list_del_init(&req->intr_entry); | ||
| 651 | req->intr_unique = fuse_get_unique(fc); | ||
| 652 | memset(&ih, 0, sizeof(ih)); | ||
| 653 | memset(&arg, 0, sizeof(arg)); | ||
| 654 | ih.len = reqsize; | ||
| 655 | ih.opcode = FUSE_INTERRUPT; | ||
| 656 | ih.unique = req->intr_unique; | ||
| 657 | arg.unique = req->in.h.unique; | ||
| 658 | |||
| 659 | spin_unlock(&fc->lock); | ||
| 660 | if (iov_length(iov, nr_segs) < reqsize) | ||
| 661 | return -EINVAL; | ||
| 662 | |||
| 663 | fuse_copy_init(&cs, fc, 1, NULL, iov, nr_segs); | ||
| 664 | err = fuse_copy_one(&cs, &ih, sizeof(ih)); | ||
| 665 | if (!err) | ||
| 666 | err = fuse_copy_one(&cs, &arg, sizeof(arg)); | ||
| 667 | fuse_copy_finish(&cs); | ||
| 668 | |||
| 669 | return err ? err : reqsize; | ||
| 670 | } | ||
| 671 | |||
| 672 | /* | ||
| 589 | * Read a single request into the userspace filesystem's buffer. This | 673 | * Read a single request into the userspace filesystem's buffer. This |
| 590 | * function waits until a request is available, then removes it from | 674 | * function waits until a request is available, then removes it from |
| 591 | * the pending list and copies request data to userspace buffer. If | 675 | * the pending list and copies request data to userspace buffer. If |
| @@ -610,7 +694,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
| 610 | spin_lock(&fc->lock); | 694 | spin_lock(&fc->lock); |
| 611 | err = -EAGAIN; | 695 | err = -EAGAIN; |
| 612 | if ((file->f_flags & O_NONBLOCK) && fc->connected && | 696 | if ((file->f_flags & O_NONBLOCK) && fc->connected && |
| 613 | list_empty(&fc->pending)) | 697 | !request_pending(fc)) |
| 614 | goto err_unlock; | 698 | goto err_unlock; |
| 615 | 699 | ||
| 616 | request_wait(fc); | 700 | request_wait(fc); |
| @@ -618,9 +702,15 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
| 618 | if (!fc->connected) | 702 | if (!fc->connected) |
| 619 | goto err_unlock; | 703 | goto err_unlock; |
| 620 | err = -ERESTARTSYS; | 704 | err = -ERESTARTSYS; |
| 621 | if (list_empty(&fc->pending)) | 705 | if (!request_pending(fc)) |
| 622 | goto err_unlock; | 706 | goto err_unlock; |
| 623 | 707 | ||
| 708 | if (!list_empty(&fc->interrupts)) { | ||
| 709 | req = list_entry(fc->interrupts.next, struct fuse_req, | ||
| 710 | intr_entry); | ||
| 711 | return fuse_read_interrupt(fc, req, iov, nr_segs); | ||
| 712 | } | ||
| 713 | |||
| 624 | req = list_entry(fc->pending.next, struct fuse_req, list); | 714 | req = list_entry(fc->pending.next, struct fuse_req, list); |
| 625 | req->state = FUSE_REQ_READING; | 715 | req->state = FUSE_REQ_READING; |
| 626 | list_move(&req->list, &fc->io); | 716 | list_move(&req->list, &fc->io); |
| @@ -658,6 +748,8 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, | |||
| 658 | else { | 748 | else { |
| 659 | req->state = FUSE_REQ_SENT; | 749 | req->state = FUSE_REQ_SENT; |
| 660 | list_move_tail(&req->list, &fc->processing); | 750 | list_move_tail(&req->list, &fc->processing); |
| 751 | if (req->interrupted) | ||
| 752 | queue_interrupt(fc, req); | ||
| 661 | spin_unlock(&fc->lock); | 753 | spin_unlock(&fc->lock); |
| 662 | } | 754 | } |
| 663 | return reqsize; | 755 | return reqsize; |
| @@ -684,7 +776,7 @@ static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique) | |||
| 684 | list_for_each(entry, &fc->processing) { | 776 | list_for_each(entry, &fc->processing) { |
| 685 | struct fuse_req *req; | 777 | struct fuse_req *req; |
| 686 | req = list_entry(entry, struct fuse_req, list); | 778 | req = list_entry(entry, struct fuse_req, list); |
| 687 | if (req->in.h.unique == unique) | 779 | if (req->in.h.unique == unique || req->intr_unique == unique) |
| 688 | return req; | 780 | return req; |
| 689 | } | 781 | } |
| 690 | return NULL; | 782 | return NULL; |
| @@ -750,7 +842,6 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov, | |||
| 750 | goto err_unlock; | 842 | goto err_unlock; |
| 751 | 843 | ||
| 752 | req = request_find(fc, oh.unique); | 844 | req = request_find(fc, oh.unique); |
| 753 | err = -EINVAL; | ||
| 754 | if (!req) | 845 | if (!req) |
| 755 | goto err_unlock; | 846 | goto err_unlock; |
| 756 | 847 | ||
| @@ -761,6 +852,23 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov, | |||
| 761 | request_end(fc, req); | 852 | request_end(fc, req); |
| 762 | return -ENOENT; | 853 | return -ENOENT; |
| 763 | } | 854 | } |
| 855 | /* Is it an interrupt reply? */ | ||
| 856 | if (req->intr_unique == oh.unique) { | ||
| 857 | err = -EINVAL; | ||
| 858 | if (nbytes != sizeof(struct fuse_out_header)) | ||
| 859 | goto err_unlock; | ||
| 860 | |||
| 861 | if (oh.error == -ENOSYS) | ||
| 862 | fc->no_interrupt = 1; | ||
| 863 | else if (oh.error == -EAGAIN) | ||
| 864 | queue_interrupt(fc, req); | ||
| 865 | |||
| 866 | spin_unlock(&fc->lock); | ||
| 867 | fuse_copy_finish(&cs); | ||
| 868 | return nbytes; | ||
| 869 | } | ||
| 870 | |||
| 871 | req->state = FUSE_REQ_WRITING; | ||
| 764 | list_move(&req->list, &fc->io); | 872 | list_move(&req->list, &fc->io); |
| 765 | req->out.h = oh; | 873 | req->out.h = oh; |
| 766 | req->locked = 1; | 874 | req->locked = 1; |
| @@ -809,7 +917,7 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait) | |||
| 809 | spin_lock(&fc->lock); | 917 | spin_lock(&fc->lock); |
| 810 | if (!fc->connected) | 918 | if (!fc->connected) |
| 811 | mask = POLLERR; | 919 | mask = POLLERR; |
| 812 | else if (!list_empty(&fc->pending)) | 920 | else if (request_pending(fc)) |
| 813 | mask |= POLLIN | POLLRDNORM; | 921 | mask |= POLLIN | POLLRDNORM; |
| 814 | spin_unlock(&fc->lock); | 922 | spin_unlock(&fc->lock); |
| 815 | 923 | ||
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ce759414cff9..36f92f181d2f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
| @@ -705,6 +705,9 @@ static int fuse_setlk(struct file *file, struct file_lock *fl) | |||
| 705 | fuse_lk_fill(req, file, fl, opcode, pid); | 705 | fuse_lk_fill(req, file, fl, opcode, pid); |
| 706 | request_send(fc, req); | 706 | request_send(fc, req); |
| 707 | err = req->out.h.error; | 707 | err = req->out.h.error; |
| 708 | /* locking is restartable */ | ||
| 709 | if (err == -EINTR) | ||
| 710 | err = -ERESTARTSYS; | ||
| 708 | fuse_put_request(fc, req); | 711 | fuse_put_request(fc, req); |
| 709 | return err; | 712 | return err; |
| 710 | } | 713 | } |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index fd65e75e1622..c862df58da92 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
| @@ -131,6 +131,7 @@ enum fuse_req_state { | |||
| 131 | FUSE_REQ_PENDING, | 131 | FUSE_REQ_PENDING, |
| 132 | FUSE_REQ_READING, | 132 | FUSE_REQ_READING, |
| 133 | FUSE_REQ_SENT, | 133 | FUSE_REQ_SENT, |
| 134 | FUSE_REQ_WRITING, | ||
| 134 | FUSE_REQ_FINISHED | 135 | FUSE_REQ_FINISHED |
| 135 | }; | 136 | }; |
| 136 | 137 | ||
| @@ -144,9 +145,15 @@ struct fuse_req { | |||
| 144 | fuse_conn */ | 145 | fuse_conn */ |
| 145 | struct list_head list; | 146 | struct list_head list; |
| 146 | 147 | ||
| 148 | /** Entry on the interrupts list */ | ||
| 149 | struct list_head intr_entry; | ||
| 150 | |||
| 147 | /** refcount */ | 151 | /** refcount */ |
| 148 | atomic_t count; | 152 | atomic_t count; |
| 149 | 153 | ||
| 154 | /** Unique ID for the interrupt request */ | ||
| 155 | u64 intr_unique; | ||
| 156 | |||
| 150 | /* | 157 | /* |
| 151 | * The following bitfields are either set once before the | 158 | * The following bitfields are either set once before the |
| 152 | * request is queued or setting/clearing them is protected by | 159 | * request is queued or setting/clearing them is protected by |
| @@ -165,6 +172,9 @@ struct fuse_req { | |||
| 165 | /** Request is sent in the background */ | 172 | /** Request is sent in the background */ |
| 166 | unsigned background:1; | 173 | unsigned background:1; |
| 167 | 174 | ||
| 175 | /** The request has been interrupted */ | ||
| 176 | unsigned interrupted:1; | ||
| 177 | |||
| 168 | /** Data is being copied to/from the request */ | 178 | /** Data is being copied to/from the request */ |
| 169 | unsigned locked:1; | 179 | unsigned locked:1; |
| 170 | 180 | ||
| @@ -262,6 +272,9 @@ struct fuse_conn { | |||
| 262 | /** Number of requests currently in the background */ | 272 | /** Number of requests currently in the background */ |
| 263 | unsigned num_background; | 273 | unsigned num_background; |
| 264 | 274 | ||
| 275 | /** Pending interrupts */ | ||
| 276 | struct list_head interrupts; | ||
| 277 | |||
| 265 | /** Flag indicating if connection is blocked. This will be | 278 | /** Flag indicating if connection is blocked. This will be |
| 266 | the case before the INIT reply is received, and if there | 279 | the case before the INIT reply is received, and if there |
| 267 | are too many outstading backgrounds requests */ | 280 | are too many outstading backgrounds requests */ |
| @@ -320,6 +333,9 @@ struct fuse_conn { | |||
| 320 | /** Is create not implemented by fs? */ | 333 | /** Is create not implemented by fs? */ |
| 321 | unsigned no_create : 1; | 334 | unsigned no_create : 1; |
| 322 | 335 | ||
| 336 | /** Is interrupt not implemented by fs? */ | ||
| 337 | unsigned no_interrupt : 1; | ||
| 338 | |||
| 323 | /** The number of requests waiting for completion */ | 339 | /** The number of requests waiting for completion */ |
| 324 | atomic_t num_waiting; | 340 | atomic_t num_waiting; |
| 325 | 341 | ||
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 412892905838..e21ef8a3ad30 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
| @@ -381,6 +381,7 @@ static struct fuse_conn *new_conn(void) | |||
| 381 | INIT_LIST_HEAD(&fc->pending); | 381 | INIT_LIST_HEAD(&fc->pending); |
| 382 | INIT_LIST_HEAD(&fc->processing); | 382 | INIT_LIST_HEAD(&fc->processing); |
| 383 | INIT_LIST_HEAD(&fc->io); | 383 | INIT_LIST_HEAD(&fc->io); |
| 384 | INIT_LIST_HEAD(&fc->interrupts); | ||
| 384 | atomic_set(&fc->num_waiting, 0); | 385 | atomic_set(&fc->num_waiting, 0); |
| 385 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; | 386 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; |
| 386 | fc->bdi.unplug_io_fn = default_unplug_io_fn; | 387 | fc->bdi.unplug_io_fn = default_unplug_io_fn; |
diff --git a/include/linux/fuse.h b/include/linux/fuse.h index e7a76ec0f05c..9fc48a674b82 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h | |||
| @@ -125,7 +125,8 @@ enum fuse_opcode { | |||
| 125 | FUSE_SETLK = 32, | 125 | FUSE_SETLK = 32, |
| 126 | FUSE_SETLKW = 33, | 126 | FUSE_SETLKW = 33, |
| 127 | FUSE_ACCESS = 34, | 127 | FUSE_ACCESS = 34, |
| 128 | FUSE_CREATE = 35 | 128 | FUSE_CREATE = 35, |
| 129 | FUSE_INTERRUPT = 36, | ||
| 129 | }; | 130 | }; |
| 130 | 131 | ||
| 131 | /* The read buffer is required to be at least 8k, but may be much larger */ | 132 | /* The read buffer is required to be at least 8k, but may be much larger */ |
| @@ -291,6 +292,10 @@ struct fuse_init_out { | |||
| 291 | __u32 max_write; | 292 | __u32 max_write; |
| 292 | }; | 293 | }; |
| 293 | 294 | ||
| 295 | struct fuse_interrupt_in { | ||
| 296 | __u64 unique; | ||
| 297 | }; | ||
| 298 | |||
| 294 | struct fuse_in_header { | 299 | struct fuse_in_header { |
| 295 | __u32 len; | 300 | __u32 len; |
| 296 | __u32 opcode; | 301 | __u32 opcode; |
