diff options
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 37 |
1 files changed, 19 insertions, 18 deletions
diff --git a/fs/splice.c b/fs/splice.c index 9df28d30efa0..1633778f3652 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -78,7 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, | |||
78 | return 1; | 78 | return 1; |
79 | } | 79 | } |
80 | 80 | ||
81 | buf->flags |= PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU; | 81 | buf->flags |= PIPE_BUF_FLAG_LRU; |
82 | return 0; | 82 | return 0; |
83 | } | 83 | } |
84 | 84 | ||
@@ -87,7 +87,7 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, | |||
87 | { | 87 | { |
88 | page_cache_release(buf->page); | 88 | page_cache_release(buf->page); |
89 | buf->page = NULL; | 89 | buf->page = NULL; |
90 | buf->flags &= ~(PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU); | 90 | buf->flags &= ~PIPE_BUF_FLAG_LRU; |
91 | } | 91 | } |
92 | 92 | ||
93 | static void *page_cache_pipe_buf_map(struct file *file, | 93 | static void *page_cache_pipe_buf_map(struct file *file, |
@@ -587,9 +587,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
587 | this_len = PAGE_CACHE_SIZE - offset; | 587 | this_len = PAGE_CACHE_SIZE - offset; |
588 | 588 | ||
589 | /* | 589 | /* |
590 | * Reuse buf page, if SPLICE_F_MOVE is set. | 590 | * Reuse buf page, if SPLICE_F_MOVE is set and we are doing a full |
591 | * page. | ||
591 | */ | 592 | */ |
592 | if (sd->flags & SPLICE_F_MOVE) { | 593 | if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { |
593 | /* | 594 | /* |
594 | * If steal succeeds, buf->page is now pruned from the vm | 595 | * If steal succeeds, buf->page is now pruned from the vm |
595 | * side (LRU and page cache) and we can reuse it. The page | 596 | * side (LRU and page cache) and we can reuse it. The page |
@@ -604,6 +605,8 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
604 | goto find_page; | 605 | goto find_page; |
605 | } | 606 | } |
606 | 607 | ||
608 | page_cache_get(page); | ||
609 | |||
607 | if (!(buf->flags & PIPE_BUF_FLAG_LRU)) | 610 | if (!(buf->flags & PIPE_BUF_FLAG_LRU)) |
608 | lru_cache_add(page); | 611 | lru_cache_add(page); |
609 | } else { | 612 | } else { |
@@ -662,7 +665,7 @@ find_page: | |||
662 | } else if (ret) | 665 | } else if (ret) |
663 | goto out; | 666 | goto out; |
664 | 667 | ||
665 | if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { | 668 | if (buf->page != page) { |
666 | char *dst = kmap_atomic(page, KM_USER0); | 669 | char *dst = kmap_atomic(page, KM_USER0); |
667 | 670 | ||
668 | memcpy(dst + offset, src + buf->offset, this_len); | 671 | memcpy(dst + offset, src + buf->offset, this_len); |
@@ -671,22 +674,20 @@ find_page: | |||
671 | } | 674 | } |
672 | 675 | ||
673 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); | 676 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); |
674 | if (ret == AOP_TRUNCATED_PAGE) { | 677 | if (!ret) { |
678 | /* | ||
679 | * Return the number of bytes written and mark page as | ||
680 | * accessed, we are now done! | ||
681 | */ | ||
682 | ret = this_len; | ||
683 | mark_page_accessed(page); | ||
684 | balance_dirty_pages_ratelimited(mapping); | ||
685 | } else if (ret == AOP_TRUNCATED_PAGE) { | ||
675 | page_cache_release(page); | 686 | page_cache_release(page); |
676 | goto find_page; | 687 | goto find_page; |
677 | } else if (ret) | 688 | } |
678 | goto out; | ||
679 | |||
680 | /* | ||
681 | * Return the number of bytes written. | ||
682 | */ | ||
683 | ret = this_len; | ||
684 | mark_page_accessed(page); | ||
685 | balance_dirty_pages_ratelimited(mapping); | ||
686 | out: | 689 | out: |
687 | if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) | 690 | page_cache_release(page); |
688 | page_cache_release(page); | ||
689 | |||
690 | unlock_page(page); | 691 | unlock_page(page); |
691 | out_nomem: | 692 | out_nomem: |
692 | buf->ops->unmap(info, buf); | 693 | buf->ops->unmap(info, buf); |