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 | ||
