Commit 016b661e authored by Jens Axboe's avatar Jens Axboe Committed by Jens Axboe

[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: default avatarJens Axboe <axboe@suse.de>
parent ba5f5d90
...@@ -439,14 +439,13 @@ EXPORT_SYMBOL(generic_file_splice_read); ...@@ -439,14 +439,13 @@ EXPORT_SYMBOL(generic_file_splice_read);
/* /*
* Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
* using sendpage(). * using sendpage(). Return the number of bytes sent.
*/ */
static int pipe_to_sendpage(struct pipe_inode_info *info, static int pipe_to_sendpage(struct pipe_inode_info *info,
struct pipe_buffer *buf, struct splice_desc *sd) struct pipe_buffer *buf, struct splice_desc *sd)
{ {
struct file *file = sd->file; struct file *file = sd->file;
loff_t pos = sd->pos; loff_t pos = sd->pos;
unsigned int offset;
ssize_t ret; ssize_t ret;
void *ptr; void *ptr;
int more; int more;
...@@ -461,16 +460,13 @@ static int pipe_to_sendpage(struct pipe_inode_info *info, ...@@ -461,16 +460,13 @@ static int pipe_to_sendpage(struct pipe_inode_info *info,
if (IS_ERR(ptr)) if (IS_ERR(ptr))
return PTR_ERR(ptr); return PTR_ERR(ptr);
offset = pos & ~PAGE_CACHE_MASK;
more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
ret = file->f_op->sendpage(file, buf->page, offset, sd->len, &pos,more); ret = file->f_op->sendpage(file, buf->page, buf->offset, sd->len,
&pos, more);
buf->ops->unmap(info, buf); buf->ops->unmap(info, buf);
if (ret == sd->len) return ret;
return 0;
return -EIO;
} }
/* /*
...@@ -499,7 +495,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, ...@@ -499,7 +495,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
struct file *file = sd->file; struct file *file = sd->file;
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
gfp_t gfp_mask = mapping_gfp_mask(mapping); gfp_t gfp_mask = mapping_gfp_mask(mapping);
unsigned int offset; unsigned int offset, this_len;
struct page *page; struct page *page;
pgoff_t index; pgoff_t index;
char *src; char *src;
...@@ -515,6 +511,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, ...@@ -515,6 +511,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
index = sd->pos >> PAGE_CACHE_SHIFT; index = sd->pos >> PAGE_CACHE_SHIFT;
offset = sd->pos & ~PAGE_CACHE_MASK; offset = sd->pos & ~PAGE_CACHE_MASK;
this_len = sd->len;
if (this_len + offset > PAGE_CACHE_SIZE)
this_len = PAGE_CACHE_SIZE - offset;
/* /*
* Reuse buf page, if SPLICE_F_MOVE is set. * Reuse buf page, if SPLICE_F_MOVE is set.
*/ */
...@@ -558,7 +558,7 @@ find_page: ...@@ -558,7 +558,7 @@ find_page:
* the full page. * the full page.
*/ */
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
if (sd->len < PAGE_CACHE_SIZE) { if (this_len < PAGE_CACHE_SIZE) {
ret = mapping->a_ops->readpage(file, page); ret = mapping->a_ops->readpage(file, page);
if (unlikely(ret)) if (unlikely(ret))
goto out; goto out;
...@@ -582,7 +582,7 @@ find_page: ...@@ -582,7 +582,7 @@ find_page:
} }
} }
ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len);
if (ret == AOP_TRUNCATED_PAGE) { if (ret == AOP_TRUNCATED_PAGE) {
page_cache_release(page); page_cache_release(page);
goto find_page; goto find_page;
...@@ -592,18 +592,22 @@ find_page: ...@@ -592,18 +592,22 @@ find_page:
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) {
char *dst = kmap_atomic(page, KM_USER0); char *dst = kmap_atomic(page, KM_USER0);
memcpy(dst + offset, src + buf->offset, sd->len); memcpy(dst + offset, src + buf->offset, this_len);
flush_dcache_page(page); flush_dcache_page(page);
kunmap_atomic(dst, KM_USER0); kunmap_atomic(dst, KM_USER0);
} }
ret = mapping->a_ops->commit_write(file, page, 0, sd->len); ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len);
if (ret == AOP_TRUNCATED_PAGE) { if (ret == AOP_TRUNCATED_PAGE) {
page_cache_release(page); page_cache_release(page);
goto find_page; goto find_page;
} else if (ret) } else if (ret)
goto out; goto out;
/*
* Return the number of bytes written.
*/
ret = this_len;
mark_page_accessed(page); mark_page_accessed(page);
balance_dirty_pages_ratelimited(mapping); balance_dirty_pages_ratelimited(mapping);
out: out:
...@@ -652,16 +656,22 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out, ...@@ -652,16 +656,22 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
sd.len = sd.total_len; sd.len = sd.total_len;
err = actor(pipe, buf, &sd); err = actor(pipe, buf, &sd);
if (err) { if (err <= 0) {
if (!ret && err != -ENODATA) if (!ret && err != -ENODATA)
ret = err; ret = err;
break; break;
} }
ret += sd.len; ret += err;
buf->offset += sd.len; buf->offset += err;
buf->len -= sd.len; buf->len -= err;
sd.len -= err;
sd.pos += err;
sd.total_len -= err;
if (sd.len)
continue;
if (!buf->len) { if (!buf->len) {
buf->ops = NULL; buf->ops = NULL;
...@@ -672,8 +682,6 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out, ...@@ -672,8 +682,6 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
do_wakeup = 1; do_wakeup = 1;
} }
sd.pos += sd.len;
sd.total_len -= sd.len;
if (!sd.total_len) if (!sd.total_len)
break; break;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment