mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
splice: implement default splice_read method
If f_op->splice_read() is not implemented, fall back to a plain read. Use vfs_readv() to read into previously allocated pages. This will allow splice and functions using splice, such as the loop device, to work on all filesystems. This includes "direct_io" files in fuse which bypass the page cache. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
parent
7c77f0b3f9
commit
6818173bd6
7 changed files with 140 additions and 24 deletions
|
@ -709,10 +709,6 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
|
||||||
if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
|
if (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))
|
||||||
goto out_putf;
|
goto out_putf;
|
||||||
|
|
||||||
/* new backing store needs to support loop (eg splice_read) */
|
|
||||||
if (!inode->i_fop->splice_read)
|
|
||||||
goto out_putf;
|
|
||||||
|
|
||||||
/* size of the new backing store needs to be the same */
|
/* size of the new backing store needs to be the same */
|
||||||
if (get_loop_size(lo, file) != get_loop_size(lo, old_file))
|
if (get_loop_size(lo, file) != get_loop_size(lo, old_file))
|
||||||
goto out_putf;
|
goto out_putf;
|
||||||
|
@ -788,12 +784,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
||||||
const struct address_space_operations *aops = mapping->a_ops;
|
const struct address_space_operations *aops = mapping->a_ops;
|
||||||
/*
|
|
||||||
* If we can't read - sorry. If we only can't write - well,
|
|
||||||
* it's going to be read-only.
|
|
||||||
*/
|
|
||||||
if (!file->f_op->splice_read)
|
|
||||||
goto out_putf;
|
|
||||||
if (aops->write_begin)
|
if (aops->write_begin)
|
||||||
lo_flags |= LO_FLAGS_USE_AOPS;
|
lo_flags |= LO_FLAGS_USE_AOPS;
|
||||||
if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
|
if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
|
||||||
|
|
|
@ -47,6 +47,8 @@ coda_file_splice_read(struct file *coda_file, loff_t *ppos,
|
||||||
struct pipe_inode_info *pipe, size_t count,
|
struct pipe_inode_info *pipe, size_t count,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
|
ssize_t (*splice_read)(struct file *, loff_t *,
|
||||||
|
struct pipe_inode_info *, size_t, unsigned int);
|
||||||
struct coda_file_info *cfi;
|
struct coda_file_info *cfi;
|
||||||
struct file *host_file;
|
struct file *host_file;
|
||||||
|
|
||||||
|
@ -54,10 +56,11 @@ coda_file_splice_read(struct file *coda_file, loff_t *ppos,
|
||||||
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
|
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
|
||||||
host_file = cfi->cfi_container;
|
host_file = cfi->cfi_container;
|
||||||
|
|
||||||
if (!host_file->f_op || !host_file->f_op->splice_read)
|
splice_read = host_file->f_op->splice_read;
|
||||||
return -EINVAL;
|
if (!splice_read)
|
||||||
|
splice_read = default_file_splice_read;
|
||||||
|
|
||||||
return host_file->f_op->splice_read(host_file, ppos, pipe, count,flags);
|
return splice_read(host_file, ppos, pipe, count, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
|
14
fs/pipe.c
14
fs/pipe.c
|
@ -302,6 +302,20 @@ int generic_pipe_buf_confirm(struct pipe_inode_info *info,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generic_pipe_buf_release - put a reference to a &struct pipe_buffer
|
||||||
|
* @pipe: the pipe that the buffer belongs to
|
||||||
|
* @buf: the buffer to put a reference to
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This function releases a reference to @buf.
|
||||||
|
*/
|
||||||
|
void generic_pipe_buf_release(struct pipe_inode_info *pipe,
|
||||||
|
struct pipe_buffer *buf)
|
||||||
|
{
|
||||||
|
page_cache_release(buf->page);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct pipe_buf_operations anon_pipe_buf_ops = {
|
static const struct pipe_buf_operations anon_pipe_buf_ops = {
|
||||||
.can_merge = 1,
|
.can_merge = 1,
|
||||||
.map = generic_pipe_buf_map,
|
.map = generic_pipe_buf_map,
|
||||||
|
|
|
@ -805,12 +805,6 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
|
||||||
goto out;
|
goto out;
|
||||||
if (!(in_file->f_mode & FMODE_READ))
|
if (!(in_file->f_mode & FMODE_READ))
|
||||||
goto fput_in;
|
goto fput_in;
|
||||||
retval = -EINVAL;
|
|
||||||
in_inode = in_file->f_path.dentry->d_inode;
|
|
||||||
if (!in_inode)
|
|
||||||
goto fput_in;
|
|
||||||
if (!in_file->f_op || !in_file->f_op->splice_read)
|
|
||||||
goto fput_in;
|
|
||||||
retval = -ESPIPE;
|
retval = -ESPIPE;
|
||||||
if (!ppos)
|
if (!ppos)
|
||||||
ppos = &in_file->f_pos;
|
ppos = &in_file->f_pos;
|
||||||
|
@ -834,6 +828,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
if (!out_file->f_op || !out_file->f_op->sendpage)
|
if (!out_file->f_op || !out_file->f_op->sendpage)
|
||||||
goto fput_out;
|
goto fput_out;
|
||||||
|
in_inode = in_file->f_path.dentry->d_inode;
|
||||||
out_inode = out_file->f_path.dentry->d_inode;
|
out_inode = out_file->f_path.dentry->d_inode;
|
||||||
retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count);
|
retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
|
|
120
fs/splice.c
120
fs/splice.c
|
@ -507,9 +507,116 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(generic_file_splice_read);
|
EXPORT_SYMBOL(generic_file_splice_read);
|
||||||
|
|
||||||
|
static const struct pipe_buf_operations default_pipe_buf_ops = {
|
||||||
|
.can_merge = 0,
|
||||||
|
.map = generic_pipe_buf_map,
|
||||||
|
.unmap = generic_pipe_buf_unmap,
|
||||||
|
.confirm = generic_pipe_buf_confirm,
|
||||||
|
.release = generic_pipe_buf_release,
|
||||||
|
.steal = generic_pipe_buf_steal,
|
||||||
|
.get = generic_pipe_buf_get,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t kernel_readv(struct file *file, const struct iovec *vec,
|
||||||
|
unsigned long vlen, loff_t offset)
|
||||||
|
{
|
||||||
|
mm_segment_t old_fs;
|
||||||
|
loff_t pos = offset;
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
|
old_fs = get_fs();
|
||||||
|
set_fs(get_ds());
|
||||||
|
/* The cast to a user pointer is valid due to the set_fs() */
|
||||||
|
res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos);
|
||||||
|
set_fs(old_fs);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t default_file_splice_read(struct file *in, loff_t *ppos,
|
||||||
|
struct pipe_inode_info *pipe, size_t len,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
unsigned int nr_pages;
|
||||||
|
unsigned int nr_freed;
|
||||||
|
size_t offset;
|
||||||
|
struct page *pages[PIPE_BUFFERS];
|
||||||
|
struct partial_page partial[PIPE_BUFFERS];
|
||||||
|
struct iovec vec[PIPE_BUFFERS];
|
||||||
|
pgoff_t index;
|
||||||
|
ssize_t res;
|
||||||
|
size_t this_len;
|
||||||
|
int error;
|
||||||
|
int i;
|
||||||
|
struct splice_pipe_desc spd = {
|
||||||
|
.pages = pages,
|
||||||
|
.partial = partial,
|
||||||
|
.flags = flags,
|
||||||
|
.ops = &default_pipe_buf_ops,
|
||||||
|
.spd_release = spd_release_page,
|
||||||
|
};
|
||||||
|
|
||||||
|
index = *ppos >> PAGE_CACHE_SHIFT;
|
||||||
|
offset = *ppos & ~PAGE_CACHE_MASK;
|
||||||
|
nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
|
||||||
|
for (i = 0; i < nr_pages && i < PIPE_BUFFERS && len; i++) {
|
||||||
|
struct page *page;
|
||||||
|
|
||||||
|
page = alloc_page(GFP_HIGHUSER);
|
||||||
|
error = -ENOMEM;
|
||||||
|
if (!page)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
this_len = min_t(size_t, len, PAGE_CACHE_SIZE - offset);
|
||||||
|
vec[i].iov_base = (void __user *) kmap(page);
|
||||||
|
vec[i].iov_len = this_len;
|
||||||
|
pages[i] = page;
|
||||||
|
spd.nr_pages++;
|
||||||
|
len -= this_len;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = kernel_readv(in, vec, spd.nr_pages, *ppos);
|
||||||
|
if (res < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
if (!res)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
nr_freed = 0;
|
||||||
|
for (i = 0; i < spd.nr_pages; i++) {
|
||||||
|
kunmap(pages[i]);
|
||||||
|
this_len = min_t(size_t, vec[i].iov_len, res);
|
||||||
|
partial[i].offset = 0;
|
||||||
|
partial[i].len = this_len;
|
||||||
|
if (!this_len) {
|
||||||
|
__free_page(pages[i]);
|
||||||
|
pages[i] = NULL;
|
||||||
|
nr_freed++;
|
||||||
|
}
|
||||||
|
res -= this_len;
|
||||||
|
}
|
||||||
|
spd.nr_pages -= nr_freed;
|
||||||
|
|
||||||
|
res = splice_to_pipe(pipe, &spd);
|
||||||
|
if (res > 0)
|
||||||
|
*ppos += res;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
err:
|
||||||
|
for (i = 0; i < spd.nr_pages; i++) {
|
||||||
|
kunmap(pages[i]);
|
||||||
|
__free_page(pages[i]);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(default_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(). Return the number of bytes sent.
|
* using sendpage(). Return the number of bytes sent.
|
||||||
|
@ -933,11 +1040,10 @@ static long do_splice_to(struct file *in, loff_t *ppos,
|
||||||
struct pipe_inode_info *pipe, size_t len,
|
struct pipe_inode_info *pipe, size_t len,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
|
ssize_t (*splice_read)(struct file *, loff_t *,
|
||||||
|
struct pipe_inode_info *, size_t, unsigned int);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (unlikely(!in->f_op || !in->f_op->splice_read))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (unlikely(!(in->f_mode & FMODE_READ)))
|
if (unlikely(!(in->f_mode & FMODE_READ)))
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
|
|
||||||
|
@ -945,7 +1051,11 @@ static long do_splice_to(struct file *in, loff_t *ppos,
|
||||||
if (unlikely(ret < 0))
|
if (unlikely(ret < 0))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return in->f_op->splice_read(in, ppos, pipe, len, flags);
|
splice_read = in->f_op->splice_read;
|
||||||
|
if (!splice_read)
|
||||||
|
splice_read = default_file_splice_read;
|
||||||
|
|
||||||
|
return splice_read(in, ppos, pipe, len, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2204,6 +2204,8 @@ extern int generic_segment_checks(const struct iovec *iov,
|
||||||
/* fs/splice.c */
|
/* fs/splice.c */
|
||||||
extern ssize_t generic_file_splice_read(struct file *, loff_t *,
|
extern ssize_t generic_file_splice_read(struct file *, loff_t *,
|
||||||
struct pipe_inode_info *, size_t, unsigned int);
|
struct pipe_inode_info *, size_t, unsigned int);
|
||||||
|
extern ssize_t default_file_splice_read(struct file *, loff_t *,
|
||||||
|
struct pipe_inode_info *, size_t, unsigned int);
|
||||||
extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
|
extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
|
||||||
struct file *, loff_t *, size_t, unsigned int);
|
struct file *, loff_t *, size_t, unsigned int);
|
||||||
extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
|
extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
|
||||||
|
|
|
@ -152,5 +152,6 @@ void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *, void
|
||||||
void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
|
void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
|
int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
|
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
|
void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue