aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-04-25 09:42:00 -0400
committerJens Axboe <axboe@nelson.home.kernel.dk>2006-04-26 04:33:34 -0400
commit016b661e2f717168e600f3c85f29e1a49f88e004 (patch)
tree3ddf0f8a49c752cdd1187b5633c04400fcf42619
parentba5f5d90c45a30e4e9a1bd136acf1b3973c905c8 (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.c46
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 */
444static int pipe_to_sendpage(struct pipe_inode_info *info, 444static 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);
609out: 613out:
@@ -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 }