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 /fs | |
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>
Diffstat (limited to 'fs')
-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 | } |