diff options
-rw-r--r-- | fs/fuse/dev.c | 151 | ||||
-rw-r--r-- | fs/fuse/file.c | 28 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 3 |
3 files changed, 167 insertions, 15 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 2795045484ee..b070d3adf9b0 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/file.h> | 17 | #include <linux/file.h> |
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/pipe_fs_i.h> | 19 | #include <linux/pipe_fs_i.h> |
20 | #include <linux/swap.h> | ||
21 | #include <linux/splice.h> | ||
20 | 22 | ||
21 | MODULE_ALIAS_MISCDEV(FUSE_MINOR); | 23 | MODULE_ALIAS_MISCDEV(FUSE_MINOR); |
22 | 24 | ||
@@ -509,6 +511,7 @@ struct fuse_copy_state { | |||
509 | void *mapaddr; | 511 | void *mapaddr; |
510 | void *buf; | 512 | void *buf; |
511 | unsigned len; | 513 | unsigned len; |
514 | unsigned move_pages:1; | ||
512 | }; | 515 | }; |
513 | 516 | ||
514 | static void fuse_copy_init(struct fuse_copy_state *cs, struct fuse_conn *fc, | 517 | static void fuse_copy_init(struct fuse_copy_state *cs, struct fuse_conn *fc, |
@@ -609,13 +612,135 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) | |||
609 | return ncpy; | 612 | return ncpy; |
610 | } | 613 | } |
611 | 614 | ||
615 | static int fuse_check_page(struct page *page) | ||
616 | { | ||
617 | if (page_mapcount(page) || | ||
618 | page->mapping != NULL || | ||
619 | page_count(page) != 1 || | ||
620 | (page->flags & PAGE_FLAGS_CHECK_AT_PREP & | ||
621 | ~(1 << PG_locked | | ||
622 | 1 << PG_referenced | | ||
623 | 1 << PG_uptodate | | ||
624 | 1 << PG_lru | | ||
625 | 1 << PG_active | | ||
626 | 1 << PG_reclaim))) { | ||
627 | printk(KERN_WARNING "fuse: trying to steal weird page\n"); | ||
628 | printk(KERN_WARNING " page=%p index=%li flags=%08lx, count=%i, mapcount=%i, mapping=%p\n", page, page->index, page->flags, page_count(page), page_mapcount(page), page->mapping); | ||
629 | return 1; | ||
630 | } | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) | ||
635 | { | ||
636 | int err; | ||
637 | struct page *oldpage = *pagep; | ||
638 | struct page *newpage; | ||
639 | struct pipe_buffer *buf = cs->pipebufs; | ||
640 | struct address_space *mapping; | ||
641 | pgoff_t index; | ||
642 | |||
643 | unlock_request(cs->fc, cs->req); | ||
644 | fuse_copy_finish(cs); | ||
645 | |||
646 | err = buf->ops->confirm(cs->pipe, buf); | ||
647 | if (err) | ||
648 | return err; | ||
649 | |||
650 | BUG_ON(!cs->nr_segs); | ||
651 | cs->currbuf = buf; | ||
652 | cs->len = buf->len; | ||
653 | cs->pipebufs++; | ||
654 | cs->nr_segs--; | ||
655 | |||
656 | if (cs->len != PAGE_SIZE) | ||
657 | goto out_fallback; | ||
658 | |||
659 | if (buf->ops->steal(cs->pipe, buf) != 0) | ||
660 | goto out_fallback; | ||
661 | |||
662 | newpage = buf->page; | ||
663 | |||
664 | if (WARN_ON(!PageUptodate(newpage))) | ||
665 | return -EIO; | ||
666 | |||
667 | ClearPageMappedToDisk(newpage); | ||
668 | |||
669 | if (fuse_check_page(newpage) != 0) | ||
670 | goto out_fallback_unlock; | ||
671 | |||
672 | mapping = oldpage->mapping; | ||
673 | index = oldpage->index; | ||
674 | |||
675 | /* | ||
676 | * This is a new and locked page, it shouldn't be mapped or | ||
677 | * have any special flags on it | ||
678 | */ | ||
679 | if (WARN_ON(page_mapped(oldpage))) | ||
680 | goto out_fallback_unlock; | ||
681 | if (WARN_ON(page_has_private(oldpage))) | ||
682 | goto out_fallback_unlock; | ||
683 | if (WARN_ON(PageDirty(oldpage) || PageWriteback(oldpage))) | ||
684 | goto out_fallback_unlock; | ||
685 | if (WARN_ON(PageMlocked(oldpage))) | ||
686 | goto out_fallback_unlock; | ||
687 | |||
688 | remove_from_page_cache(oldpage); | ||
689 | page_cache_release(oldpage); | ||
690 | |||
691 | err = add_to_page_cache_locked(newpage, mapping, index, GFP_KERNEL); | ||
692 | if (err) { | ||
693 | printk(KERN_WARNING "fuse_try_move_page: failed to add page"); | ||
694 | goto out_fallback_unlock; | ||
695 | } | ||
696 | page_cache_get(newpage); | ||
697 | |||
698 | if (!(buf->flags & PIPE_BUF_FLAG_LRU)) | ||
699 | lru_cache_add_file(newpage); | ||
700 | |||
701 | err = 0; | ||
702 | spin_lock(&cs->fc->lock); | ||
703 | if (cs->req->aborted) | ||
704 | err = -ENOENT; | ||
705 | else | ||
706 | *pagep = newpage; | ||
707 | spin_unlock(&cs->fc->lock); | ||
708 | |||
709 | if (err) { | ||
710 | unlock_page(newpage); | ||
711 | page_cache_release(newpage); | ||
712 | return err; | ||
713 | } | ||
714 | |||
715 | unlock_page(oldpage); | ||
716 | page_cache_release(oldpage); | ||
717 | cs->len = 0; | ||
718 | |||
719 | return 0; | ||
720 | |||
721 | out_fallback_unlock: | ||
722 | unlock_page(newpage); | ||
723 | out_fallback: | ||
724 | cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); | ||
725 | cs->buf = cs->mapaddr + buf->offset; | ||
726 | |||
727 | err = lock_request(cs->fc, cs->req); | ||
728 | if (err) | ||
729 | return err; | ||
730 | |||
731 | return 1; | ||
732 | } | ||
733 | |||
612 | /* | 734 | /* |
613 | * Copy a page in the request to/from the userspace buffer. Must be | 735 | * Copy a page in the request to/from the userspace buffer. Must be |
614 | * done atomically | 736 | * done atomically |
615 | */ | 737 | */ |
616 | static int fuse_copy_page(struct fuse_copy_state *cs, struct page *page, | 738 | static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep, |
617 | unsigned offset, unsigned count, int zeroing) | 739 | unsigned offset, unsigned count, int zeroing) |
618 | { | 740 | { |
741 | int err; | ||
742 | struct page *page = *pagep; | ||
743 | |||
619 | if (page && zeroing && count < PAGE_SIZE) { | 744 | if (page && zeroing && count < PAGE_SIZE) { |
620 | void *mapaddr = kmap_atomic(page, KM_USER1); | 745 | void *mapaddr = kmap_atomic(page, KM_USER1); |
621 | memset(mapaddr, 0, PAGE_SIZE); | 746 | memset(mapaddr, 0, PAGE_SIZE); |
@@ -623,9 +748,16 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page *page, | |||
623 | } | 748 | } |
624 | while (count) { | 749 | while (count) { |
625 | if (!cs->len) { | 750 | if (!cs->len) { |
626 | int err = fuse_copy_fill(cs); | 751 | if (cs->move_pages && page && |
627 | if (err) | 752 | offset == 0 && count == PAGE_SIZE) { |
628 | return err; | 753 | err = fuse_try_move_page(cs, pagep); |
754 | if (err <= 0) | ||
755 | return err; | ||
756 | } else { | ||
757 | err = fuse_copy_fill(cs); | ||
758 | if (err) | ||
759 | return err; | ||
760 | } | ||
629 | } | 761 | } |
630 | if (page) { | 762 | if (page) { |
631 | void *mapaddr = kmap_atomic(page, KM_USER1); | 763 | void *mapaddr = kmap_atomic(page, KM_USER1); |
@@ -650,8 +782,10 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, | |||
650 | unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); | 782 | unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); |
651 | 783 | ||
652 | for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) { | 784 | for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) { |
653 | struct page *page = req->pages[i]; | 785 | int err; |
654 | int err = fuse_copy_page(cs, page, offset, count, zeroing); | 786 | |
787 | err = fuse_copy_page(cs, &req->pages[i], offset, count, | ||
788 | zeroing); | ||
655 | if (err) | 789 | if (err) |
656 | return err; | 790 | return err; |
657 | 791 | ||
@@ -1079,6 +1213,8 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, | |||
1079 | req->out.h = oh; | 1213 | req->out.h = oh; |
1080 | req->locked = 1; | 1214 | req->locked = 1; |
1081 | cs->req = req; | 1215 | cs->req = req; |
1216 | if (!req->out.page_replace) | ||
1217 | cs->move_pages = 0; | ||
1082 | spin_unlock(&fc->lock); | 1218 | spin_unlock(&fc->lock); |
1083 | 1219 | ||
1084 | err = copy_out_args(cs, &req->out, nbytes); | 1220 | err = copy_out_args(cs, &req->out, nbytes); |
@@ -1182,6 +1318,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, | |||
1182 | cs.nr_segs = nbuf; | 1318 | cs.nr_segs = nbuf; |
1183 | cs.pipe = pipe; | 1319 | cs.pipe = pipe; |
1184 | 1320 | ||
1321 | if (flags & SPLICE_F_MOVE) | ||
1322 | cs.move_pages = 1; | ||
1323 | |||
1185 | ret = fuse_dev_do_write(fc, &cs, len); | 1324 | ret = fuse_dev_do_write(fc, &cs, len); |
1186 | 1325 | ||
1187 | for (idx = 0; idx < nbuf; idx++) { | 1326 | for (idx = 0; idx < nbuf; idx++) { |
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 9ca68edcbdbe..06e3775b2282 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -517,17 +517,26 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) | |||
517 | int i; | 517 | int i; |
518 | size_t count = req->misc.read.in.size; | 518 | size_t count = req->misc.read.in.size; |
519 | size_t num_read = req->out.args[0].size; | 519 | size_t num_read = req->out.args[0].size; |
520 | struct inode *inode = req->pages[0]->mapping->host; | 520 | struct address_space *mapping = NULL; |
521 | 521 | ||
522 | /* | 522 | for (i = 0; mapping == NULL && i < req->num_pages; i++) |
523 | * Short read means EOF. If file size is larger, truncate it | 523 | mapping = req->pages[i]->mapping; |
524 | */ | ||
525 | if (!req->out.h.error && num_read < count) { | ||
526 | loff_t pos = page_offset(req->pages[0]) + num_read; | ||
527 | fuse_read_update_size(inode, pos, req->misc.read.attr_ver); | ||
528 | } | ||
529 | 524 | ||
530 | fuse_invalidate_attr(inode); /* atime changed */ | 525 | if (mapping) { |
526 | struct inode *inode = mapping->host; | ||
527 | |||
528 | /* | ||
529 | * Short read means EOF. If file size is larger, truncate it | ||
530 | */ | ||
531 | if (!req->out.h.error && num_read < count) { | ||
532 | loff_t pos; | ||
533 | |||
534 | pos = page_offset(req->pages[0]) + num_read; | ||
535 | fuse_read_update_size(inode, pos, | ||
536 | req->misc.read.attr_ver); | ||
537 | } | ||
538 | fuse_invalidate_attr(inode); /* atime changed */ | ||
539 | } | ||
531 | 540 | ||
532 | for (i = 0; i < req->num_pages; i++) { | 541 | for (i = 0; i < req->num_pages; i++) { |
533 | struct page *page = req->pages[i]; | 542 | struct page *page = req->pages[i]; |
@@ -551,6 +560,7 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file) | |||
551 | 560 | ||
552 | req->out.argpages = 1; | 561 | req->out.argpages = 1; |
553 | req->out.page_zeroing = 1; | 562 | req->out.page_zeroing = 1; |
563 | req->out.page_replace = 1; | ||
554 | fuse_read_fill(req, file, pos, count, FUSE_READ); | 564 | fuse_read_fill(req, file, pos, count, FUSE_READ); |
555 | req->misc.read.attr_ver = fuse_get_attr_version(fc); | 565 | req->misc.read.attr_ver = fuse_get_attr_version(fc); |
556 | if (fc->async_read) { | 566 | if (fc->async_read) { |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 01cc462ff45d..9d0a51852d8a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -177,6 +177,9 @@ struct fuse_out { | |||
177 | /** Zero partially or not copied pages */ | 177 | /** Zero partially or not copied pages */ |
178 | unsigned page_zeroing:1; | 178 | unsigned page_zeroing:1; |
179 | 179 | ||
180 | /** Pages may be replaced with new ones */ | ||
181 | unsigned page_replace:1; | ||
182 | |||
180 | /** Number or arguments */ | 183 | /** Number or arguments */ |
181 | unsigned numargs; | 184 | unsigned numargs; |
182 | 185 | ||