[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>
This commit is contained in:
46
fs/splice.c
46
fs/splice.c
@@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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:
|
|||||||
* 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:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user