diff options
| author | Jens Axboe <axboe@suse.de> | 2006-04-25 09:42:00 -0400 |
|---|---|---|
| committer | Jens Axboe <axboe@nelson.home.kernel.dk> | 2006-04-26 04:33:34 -0400 |
| commit | 016b661e2f717168e600f3c85f29e1a49f88e004 (patch) | |
| tree | 3ddf0f8a49c752cdd1187b5633c04400fcf42619 | |
| parent | ba5f5d90c45a30e4e9a1bd136acf1b3973c905c8 (diff) | |
[PATCH] splice: fix offset problems
Make the move_from_pipe() actors return number of bytes processed, then
move_from_pipe() can decide more cleverly when to move on to the next
buffer.
This fixes problems with pipe offset and differing file offset.
Signed-off-by: Jens Axboe <axboe@suse.de>
| -rw-r--r-- | fs/splice.c | 46 |
1 files changed, 27 insertions, 19 deletions
diff --git a/fs/splice.c b/fs/splice.c index 4aa67254740f..8c6030c762e2 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
| @@ -439,14 +439,13 @@ EXPORT_SYMBOL(generic_file_splice_read); | |||
| 439 | 439 | ||
| 440 | /* | 440 | /* |
| 441 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' | 441 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' |
| 442 | * using sendpage(). | 442 | * using sendpage(). Return the number of bytes sent. |
| 443 | */ | 443 | */ |
| 444 | static int pipe_to_sendpage(struct pipe_inode_info *info, | 444 | static int pipe_to_sendpage(struct pipe_inode_info *info, |
| 445 | struct pipe_buffer *buf, struct splice_desc *sd) | 445 | struct pipe_buffer *buf, struct splice_desc *sd) |
| 446 | { | 446 | { |
| 447 | struct file *file = sd->file; | 447 | struct file *file = sd->file; |
| 448 | loff_t pos = sd->pos; | 448 | loff_t pos = sd->pos; |
| 449 | unsigned int offset; | ||
| 450 | ssize_t ret; | 449 | ssize_t ret; |
| 451 | void *ptr; | 450 | void *ptr; |
| 452 | int more; | 451 | int more; |
| @@ -461,16 +460,13 @@ static int pipe_to_sendpage(struct pipe_inode_info *info, | |||
| 461 | if (IS_ERR(ptr)) | 460 | if (IS_ERR(ptr)) |
| 462 | return PTR_ERR(ptr); | 461 | return PTR_ERR(ptr); |
| 463 | 462 | ||
| 464 | offset = pos & ~PAGE_CACHE_MASK; | ||
| 465 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; | 463 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; |
| 466 | 464 | ||
| 467 | ret = file->f_op->sendpage(file, buf->page, offset, sd->len, &pos,more); | 465 | ret = file->f_op->sendpage(file, buf->page, buf->offset, sd->len, |
| 466 | &pos, more); | ||
| 468 | 467 | ||
| 469 | buf->ops->unmap(info, buf); | 468 | buf->ops->unmap(info, buf); |
| 470 | if (ret == sd->len) | 469 | return ret; |
| 471 | return 0; | ||
| 472 | |||
| 473 | return -EIO; | ||
| 474 | } | 470 | } |
| 475 | 471 | ||
| 476 | /* | 472 | /* |
| @@ -499,7 +495,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
| 499 | struct file *file = sd->file; | 495 | struct file *file = sd->file; |
| 500 | struct address_space *mapping = file->f_mapping; | 496 | struct address_space *mapping = file->f_mapping; |
| 501 | gfp_t gfp_mask = mapping_gfp_mask(mapping); | 497 | gfp_t gfp_mask = mapping_gfp_mask(mapping); |
| 502 | unsigned int offset; | 498 | unsigned int offset, this_len; |
| 503 | struct page *page; | 499 | struct page *page; |
| 504 | pgoff_t index; | 500 | pgoff_t index; |
| 505 | char *src; | 501 | char *src; |
| @@ -515,6 +511,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
| 515 | index = sd->pos >> PAGE_CACHE_SHIFT; | 511 | index = sd->pos >> PAGE_CACHE_SHIFT; |
| 516 | offset = sd->pos & ~PAGE_CACHE_MASK; | 512 | offset = sd->pos & ~PAGE_CACHE_MASK; |
| 517 | 513 | ||
| 514 | this_len = sd->len; | ||
| 515 | if (this_len + offset > PAGE_CACHE_SIZE) | ||
| 516 | this_len = PAGE_CACHE_SIZE - offset; | ||
| 517 | |||
| 518 | /* | 518 | /* |
| 519 | * Reuse buf page, if SPLICE_F_MOVE is set. | 519 | * Reuse buf page, if SPLICE_F_MOVE is set. |
| 520 | */ | 520 | */ |
| @@ -558,7 +558,7 @@ find_page: | |||
| 558 | * the full page. | 558 | * the full page. |
| 559 | */ | 559 | */ |
| 560 | if (!PageUptodate(page)) { | 560 | if (!PageUptodate(page)) { |
| 561 | if (sd->len < PAGE_CACHE_SIZE) { | 561 | if (this_len < PAGE_CACHE_SIZE) { |
| 562 | ret = mapping->a_ops->readpage(file, page); | 562 | ret = mapping->a_ops->readpage(file, page); |
| 563 | if (unlikely(ret)) | 563 | if (unlikely(ret)) |
| 564 | goto out; | 564 | goto out; |
| @@ -582,7 +582,7 @@ find_page: | |||
| 582 | } | 582 | } |
| 583 | } | 583 | } |
| 584 | 584 | ||
| 585 | ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); | 585 | ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len); |
| 586 | if (ret == AOP_TRUNCATED_PAGE) { | 586 | if (ret == AOP_TRUNCATED_PAGE) { |
| 587 | page_cache_release(page); | 587 | page_cache_release(page); |
| 588 | goto find_page; | 588 | goto find_page; |
| @@ -592,18 +592,22 @@ find_page: | |||
| 592 | if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { | 592 | if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { |
| 593 | char *dst = kmap_atomic(page, KM_USER0); | 593 | char *dst = kmap_atomic(page, KM_USER0); |
| 594 | 594 | ||
| 595 | memcpy(dst + offset, src + buf->offset, sd->len); | 595 | memcpy(dst + offset, src + buf->offset, this_len); |
| 596 | flush_dcache_page(page); | 596 | flush_dcache_page(page); |
| 597 | kunmap_atomic(dst, KM_USER0); | 597 | kunmap_atomic(dst, KM_USER0); |
| 598 | } | 598 | } |
| 599 | 599 | ||
| 600 | ret = mapping->a_ops->commit_write(file, page, 0, sd->len); | 600 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); |
| 601 | if (ret == AOP_TRUNCATED_PAGE) { | 601 | if (ret == AOP_TRUNCATED_PAGE) { |
| 602 | page_cache_release(page); | 602 | page_cache_release(page); |
| 603 | goto find_page; | 603 | goto find_page; |
| 604 | } else if (ret) | 604 | } else if (ret) |
| 605 | goto out; | 605 | goto out; |
| 606 | 606 | ||
| 607 | /* | ||
| 608 | * Return the number of bytes written. | ||
| 609 | */ | ||
| 610 | ret = this_len; | ||
| 607 | mark_page_accessed(page); | 611 | mark_page_accessed(page); |
| 608 | balance_dirty_pages_ratelimited(mapping); | 612 | balance_dirty_pages_ratelimited(mapping); |
| 609 | out: | 613 | out: |
| @@ -652,16 +656,22 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out, | |||
| 652 | sd.len = sd.total_len; | 656 | sd.len = sd.total_len; |
| 653 | 657 | ||
| 654 | err = actor(pipe, buf, &sd); | 658 | err = actor(pipe, buf, &sd); |
| 655 | if (err) { | 659 | if (err <= 0) { |
| 656 | if (!ret && err != -ENODATA) | 660 | if (!ret && err != -ENODATA) |
| 657 | ret = err; | 661 | ret = err; |
| 658 | 662 | ||
| 659 | break; | 663 | break; |
| 660 | } | 664 | } |
| 661 | 665 | ||
| 662 | ret += sd.len; | 666 | ret += err; |
| 663 | buf->offset += sd.len; | 667 | buf->offset += err; |
| 664 | buf->len -= sd.len; | 668 | buf->len -= err; |
| 669 | |||
| 670 | sd.len -= err; | ||
| 671 | sd.pos += err; | ||
| 672 | sd.total_len -= err; | ||
| 673 | if (sd.len) | ||
| 674 | continue; | ||
| 665 | 675 | ||
| 666 | if (!buf->len) { | 676 | if (!buf->len) { |
| 667 | buf->ops = NULL; | 677 | buf->ops = NULL; |
| @@ -672,8 +682,6 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out, | |||
| 672 | do_wakeup = 1; | 682 | do_wakeup = 1; |
| 673 | } | 683 | } |
| 674 | 684 | ||
| 675 | sd.pos += sd.len; | ||
| 676 | sd.total_len -= sd.len; | ||
| 677 | if (!sd.total_len) | 685 | if (!sd.total_len) |
| 678 | break; | 686 | break; |
| 679 | } | 687 | } |
