mirror of
https://github.com/torvalds/linux.git
synced 2024-11-05 03:21:32 +00:00
Merge branch 'splice' of git://brick.kernel.dk/data/git/linux-2.6-block
* 'splice' of git://brick.kernel.dk/data/git/linux-2.6-block: [PATCH] vmsplice: allow user to pass in gift pages [PATCH] pipe: enable atomic copying of pipe data to/from user space [PATCH] splice: call handle_ra_miss() on failure to lookup page [PATCH] Add ->splice_read/splice_write to def_blk_fops [PATCH] pipe: introduce ->pin() buffer operation [PATCH] splice: fix bugs in pipe_to_file() [PATCH] splice: fix bugs with stealing regular pipe pages
This commit is contained in:
commit
9817d207dc
@ -1104,6 +1104,8 @@ const struct file_operations def_blk_fops = {
|
||||
.readv = generic_file_readv,
|
||||
.writev = generic_file_write_nolock,
|
||||
.sendfile = generic_file_sendfile,
|
||||
.splice_read = generic_file_splice_read,
|
||||
.splice_write = generic_file_splice_write,
|
||||
};
|
||||
|
||||
int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
|
||||
|
190
fs/pipe.c
190
fs/pipe.c
@ -55,7 +55,8 @@ void pipe_wait(struct pipe_inode_info *pipe)
|
||||
}
|
||||
|
||||
static int
|
||||
pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len)
|
||||
pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len,
|
||||
int atomic)
|
||||
{
|
||||
unsigned long copy;
|
||||
|
||||
@ -64,8 +65,13 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len)
|
||||
iov++;
|
||||
copy = min_t(unsigned long, len, iov->iov_len);
|
||||
|
||||
if (copy_from_user(to, iov->iov_base, copy))
|
||||
return -EFAULT;
|
||||
if (atomic) {
|
||||
if (__copy_from_user_inatomic(to, iov->iov_base, copy))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_from_user(to, iov->iov_base, copy))
|
||||
return -EFAULT;
|
||||
}
|
||||
to += copy;
|
||||
len -= copy;
|
||||
iov->iov_base += copy;
|
||||
@ -75,7 +81,8 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len)
|
||||
}
|
||||
|
||||
static int
|
||||
pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len)
|
||||
pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len,
|
||||
int atomic)
|
||||
{
|
||||
unsigned long copy;
|
||||
|
||||
@ -84,8 +91,13 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len)
|
||||
iov++;
|
||||
copy = min_t(unsigned long, len, iov->iov_len);
|
||||
|
||||
if (copy_to_user(iov->iov_base, from, copy))
|
||||
return -EFAULT;
|
||||
if (atomic) {
|
||||
if (__copy_to_user_inatomic(iov->iov_base, from, copy))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_to_user(iov->iov_base, from, copy))
|
||||
return -EFAULT;
|
||||
}
|
||||
from += copy;
|
||||
len -= copy;
|
||||
iov->iov_base += copy;
|
||||
@ -94,13 +106,52 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to pre-fault in the user memory, so we can use atomic copies.
|
||||
* Returns the number of bytes not faulted in.
|
||||
*/
|
||||
static int iov_fault_in_pages_write(struct iovec *iov, unsigned long len)
|
||||
{
|
||||
while (!iov->iov_len)
|
||||
iov++;
|
||||
|
||||
while (len > 0) {
|
||||
unsigned long this_len;
|
||||
|
||||
this_len = min_t(unsigned long, len, iov->iov_len);
|
||||
if (fault_in_pages_writeable(iov->iov_base, this_len))
|
||||
break;
|
||||
|
||||
len -= this_len;
|
||||
iov++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pre-fault in the user memory, so we can use atomic copies.
|
||||
*/
|
||||
static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len)
|
||||
{
|
||||
while (!iov->iov_len)
|
||||
iov++;
|
||||
|
||||
while (len > 0) {
|
||||
unsigned long this_len;
|
||||
|
||||
this_len = min_t(unsigned long, len, iov->iov_len);
|
||||
fault_in_pages_readable(iov->iov_base, this_len);
|
||||
len -= this_len;
|
||||
iov++;
|
||||
}
|
||||
}
|
||||
|
||||
static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
struct page *page = buf->page;
|
||||
|
||||
buf->flags &= ~PIPE_BUF_FLAG_STOLEN;
|
||||
|
||||
/*
|
||||
* If nobody else uses this page, and we don't already have a
|
||||
* temporary page, let's keep track of it as a one-deep
|
||||
@ -112,38 +163,58 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
|
||||
page_cache_release(page);
|
||||
}
|
||||
|
||||
static void * anon_pipe_buf_map(struct file *file, struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf, int atomic)
|
||||
{
|
||||
if (atomic) {
|
||||
buf->flags |= PIPE_BUF_FLAG_ATOMIC;
|
||||
return kmap_atomic(buf->page, KM_USER0);
|
||||
}
|
||||
|
||||
return kmap(buf->page);
|
||||
}
|
||||
|
||||
static void anon_pipe_buf_unmap(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf, void *map_data)
|
||||
{
|
||||
kunmap(buf->page);
|
||||
if (buf->flags & PIPE_BUF_FLAG_ATOMIC) {
|
||||
buf->flags &= ~PIPE_BUF_FLAG_ATOMIC;
|
||||
kunmap_atomic(map_data, KM_USER0);
|
||||
} else
|
||||
kunmap(buf->page);
|
||||
}
|
||||
|
||||
static int anon_pipe_buf_steal(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
buf->flags |= PIPE_BUF_FLAG_STOLEN;
|
||||
return 0;
|
||||
struct page *page = buf->page;
|
||||
|
||||
if (page_count(page) == 1) {
|
||||
lock_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void anon_pipe_buf_get(struct pipe_inode_info *info,
|
||||
struct pipe_buffer *buf)
|
||||
void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf)
|
||||
{
|
||||
page_cache_get(buf->page);
|
||||
}
|
||||
|
||||
int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pipe_buf_operations anon_pipe_buf_ops = {
|
||||
.can_merge = 1,
|
||||
.map = anon_pipe_buf_map,
|
||||
.unmap = anon_pipe_buf_unmap,
|
||||
.map = generic_pipe_buf_map,
|
||||
.unmap = generic_pipe_buf_unmap,
|
||||
.pin = generic_pipe_buf_pin,
|
||||
.release = anon_pipe_buf_release,
|
||||
.steal = anon_pipe_buf_steal,
|
||||
.get = anon_pipe_buf_get,
|
||||
.get = generic_pipe_buf_get,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
@ -174,22 +245,33 @@ pipe_readv(struct file *filp, const struct iovec *_iov,
|
||||
struct pipe_buf_operations *ops = buf->ops;
|
||||
void *addr;
|
||||
size_t chars = buf->len;
|
||||
int error;
|
||||
int error, atomic;
|
||||
|
||||
if (chars > total_len)
|
||||
chars = total_len;
|
||||
|
||||
addr = ops->map(filp, pipe, buf);
|
||||
if (IS_ERR(addr)) {
|
||||
error = ops->pin(pipe, buf);
|
||||
if (error) {
|
||||
if (!ret)
|
||||
ret = PTR_ERR(addr);
|
||||
error = ret;
|
||||
break;
|
||||
}
|
||||
error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars);
|
||||
ops->unmap(pipe, buf);
|
||||
|
||||
atomic = !iov_fault_in_pages_write(iov, chars);
|
||||
redo:
|
||||
addr = ops->map(pipe, buf, atomic);
|
||||
error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic);
|
||||
ops->unmap(pipe, buf, addr);
|
||||
if (unlikely(error)) {
|
||||
/*
|
||||
* Just retry with the slow path if we failed.
|
||||
*/
|
||||
if (atomic) {
|
||||
atomic = 0;
|
||||
goto redo;
|
||||
}
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
ret = error;
|
||||
break;
|
||||
}
|
||||
ret += chars;
|
||||
@ -293,21 +375,28 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
|
||||
int offset = buf->offset + buf->len;
|
||||
|
||||
if (ops->can_merge && offset + chars <= PAGE_SIZE) {
|
||||
int error, atomic = 1;
|
||||
void *addr;
|
||||
int error;
|
||||
|
||||
addr = ops->map(filp, pipe, buf);
|
||||
if (IS_ERR(addr)) {
|
||||
error = PTR_ERR(addr);
|
||||
goto out;
|
||||
}
|
||||
error = pipe_iov_copy_from_user(offset + addr, iov,
|
||||
chars);
|
||||
ops->unmap(pipe, buf);
|
||||
ret = error;
|
||||
do_wakeup = 1;
|
||||
error = ops->pin(pipe, buf);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
iov_fault_in_pages_read(iov, chars);
|
||||
redo1:
|
||||
addr = ops->map(pipe, buf, atomic);
|
||||
error = pipe_iov_copy_from_user(offset + addr, iov,
|
||||
chars, atomic);
|
||||
ops->unmap(pipe, buf, addr);
|
||||
ret = error;
|
||||
do_wakeup = 1;
|
||||
if (error) {
|
||||
if (atomic) {
|
||||
atomic = 0;
|
||||
goto redo1;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
buf->len += chars;
|
||||
total_len -= chars;
|
||||
ret = chars;
|
||||
@ -330,7 +419,8 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
|
||||
int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1);
|
||||
struct pipe_buffer *buf = pipe->bufs + newbuf;
|
||||
struct page *page = pipe->tmp_page;
|
||||
int error;
|
||||
char *src;
|
||||
int error, atomic = 1;
|
||||
|
||||
if (!page) {
|
||||
page = alloc_page(GFP_HIGHUSER);
|
||||
@ -350,11 +440,27 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
|
||||
if (chars > total_len)
|
||||
chars = total_len;
|
||||
|
||||
error = pipe_iov_copy_from_user(kmap(page), iov, chars);
|
||||
kunmap(page);
|
||||
iov_fault_in_pages_read(iov, chars);
|
||||
redo2:
|
||||
if (atomic)
|
||||
src = kmap_atomic(page, KM_USER0);
|
||||
else
|
||||
src = kmap(page);
|
||||
|
||||
error = pipe_iov_copy_from_user(src, iov, chars,
|
||||
atomic);
|
||||
if (atomic)
|
||||
kunmap_atomic(src, KM_USER0);
|
||||
else
|
||||
kunmap(page);
|
||||
|
||||
if (unlikely(error)) {
|
||||
if (atomic) {
|
||||
atomic = 0;
|
||||
goto redo2;
|
||||
}
|
||||
if (!ret)
|
||||
ret = -EFAULT;
|
||||
ret = error;
|
||||
break;
|
||||
}
|
||||
ret += chars;
|
||||
|
166
fs/splice.c
166
fs/splice.c
@ -78,7 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info,
|
||||
return 1;
|
||||
}
|
||||
|
||||
buf->flags |= PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU;
|
||||
buf->flags |= PIPE_BUF_FLAG_LRU;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -87,12 +87,11 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info,
|
||||
{
|
||||
page_cache_release(buf->page);
|
||||
buf->page = NULL;
|
||||
buf->flags &= ~(PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU);
|
||||
buf->flags &= ~PIPE_BUF_FLAG_LRU;
|
||||
}
|
||||
|
||||
static void *page_cache_pipe_buf_map(struct file *file,
|
||||
struct pipe_inode_info *info,
|
||||
struct pipe_buffer *buf)
|
||||
static int page_cache_pipe_buf_pin(struct pipe_inode_info *info,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
struct page *page = buf->page;
|
||||
int err;
|
||||
@ -118,64 +117,44 @@ static void *page_cache_pipe_buf_map(struct file *file,
|
||||
}
|
||||
|
||||
/*
|
||||
* Page is ok afterall, fall through to mapping.
|
||||
* Page is ok afterall, we are done.
|
||||
*/
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
return kmap(page);
|
||||
return 0;
|
||||
error:
|
||||
unlock_page(page);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
kunmap(buf->page);
|
||||
}
|
||||
|
||||
static void *user_page_pipe_buf_map(struct file *file,
|
||||
struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
return kmap(buf->page);
|
||||
}
|
||||
|
||||
static void user_page_pipe_buf_unmap(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
kunmap(buf->page);
|
||||
}
|
||||
|
||||
static void page_cache_pipe_buf_get(struct pipe_inode_info *info,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
page_cache_get(buf->page);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pipe_buf_operations page_cache_pipe_buf_ops = {
|
||||
.can_merge = 0,
|
||||
.map = page_cache_pipe_buf_map,
|
||||
.unmap = page_cache_pipe_buf_unmap,
|
||||
.map = generic_pipe_buf_map,
|
||||
.unmap = generic_pipe_buf_unmap,
|
||||
.pin = page_cache_pipe_buf_pin,
|
||||
.release = page_cache_pipe_buf_release,
|
||||
.steal = page_cache_pipe_buf_steal,
|
||||
.get = page_cache_pipe_buf_get,
|
||||
.get = generic_pipe_buf_get,
|
||||
};
|
||||
|
||||
static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
return 1;
|
||||
if (!(buf->flags & PIPE_BUF_FLAG_GIFT))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pipe_buf_operations user_page_pipe_buf_ops = {
|
||||
.can_merge = 0,
|
||||
.map = user_page_pipe_buf_map,
|
||||
.unmap = user_page_pipe_buf_unmap,
|
||||
.map = generic_pipe_buf_map,
|
||||
.unmap = generic_pipe_buf_unmap,
|
||||
.pin = generic_pipe_buf_pin,
|
||||
.release = page_cache_pipe_buf_release,
|
||||
.steal = user_page_pipe_buf_steal,
|
||||
.get = page_cache_pipe_buf_get,
|
||||
.get = generic_pipe_buf_get,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -210,6 +189,9 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
|
||||
buf->offset = spd->partial[page_nr].offset;
|
||||
buf->len = spd->partial[page_nr].len;
|
||||
buf->ops = spd->ops;
|
||||
if (spd->flags & SPLICE_F_GIFT)
|
||||
buf->flags |= PIPE_BUF_FLAG_GIFT;
|
||||
|
||||
pipe->nrbufs++;
|
||||
page_nr++;
|
||||
ret += buf->len;
|
||||
@ -325,6 +307,12 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
|
||||
*/
|
||||
page = find_get_page(mapping, index);
|
||||
if (!page) {
|
||||
/*
|
||||
* Make sure the read-ahead engine is notified
|
||||
* about this failure.
|
||||
*/
|
||||
handle_ra_miss(mapping, &in->f_ra, index);
|
||||
|
||||
/*
|
||||
* page didn't exist, allocate one.
|
||||
*/
|
||||
@ -517,26 +505,16 @@ static int pipe_to_sendpage(struct pipe_inode_info *info,
|
||||
{
|
||||
struct file *file = sd->file;
|
||||
loff_t pos = sd->pos;
|
||||
ssize_t ret;
|
||||
void *ptr;
|
||||
int more;
|
||||
int ret, more;
|
||||
|
||||
/*
|
||||
* Sub-optimal, but we are limited by the pipe ->map. We don't
|
||||
* need a kmap'ed buffer here, we just want to make sure we
|
||||
* have the page pinned if the pipe page originates from the
|
||||
* page cache.
|
||||
*/
|
||||
ptr = buf->ops->map(file, info, buf);
|
||||
if (IS_ERR(ptr))
|
||||
return PTR_ERR(ptr);
|
||||
ret = buf->ops->pin(info, buf);
|
||||
if (!ret) {
|
||||
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, buf->offset,
|
||||
sd->len, &pos, more);
|
||||
}
|
||||
|
||||
ret = file->f_op->sendpage(file, buf->page, buf->offset, sd->len,
|
||||
&pos, more);
|
||||
|
||||
buf->ops->unmap(info, buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -569,15 +547,14 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
|
||||
unsigned int offset, this_len;
|
||||
struct page *page;
|
||||
pgoff_t index;
|
||||
char *src;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* make sure the data in this buffer is uptodate
|
||||
*/
|
||||
src = buf->ops->map(file, info, buf);
|
||||
if (IS_ERR(src))
|
||||
return PTR_ERR(src);
|
||||
ret = buf->ops->pin(info, buf);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
index = sd->pos >> PAGE_CACHE_SHIFT;
|
||||
offset = sd->pos & ~PAGE_CACHE_MASK;
|
||||
@ -587,9 +564,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
|
||||
this_len = PAGE_CACHE_SIZE - offset;
|
||||
|
||||
/*
|
||||
* Reuse buf page, if SPLICE_F_MOVE is set.
|
||||
* Reuse buf page, if SPLICE_F_MOVE is set and we are doing a full
|
||||
* page.
|
||||
*/
|
||||
if (sd->flags & SPLICE_F_MOVE) {
|
||||
if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) {
|
||||
/*
|
||||
* If steal succeeds, buf->page is now pruned from the vm
|
||||
* side (LRU and page cache) and we can reuse it. The page
|
||||
@ -599,8 +577,12 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
|
||||
goto find_page;
|
||||
|
||||
page = buf->page;
|
||||
if (add_to_page_cache(page, mapping, index, gfp_mask))
|
||||
if (add_to_page_cache(page, mapping, index, gfp_mask)) {
|
||||
unlock_page(page);
|
||||
goto find_page;
|
||||
}
|
||||
|
||||
page_cache_get(page);
|
||||
|
||||
if (!(buf->flags & PIPE_BUF_FLAG_LRU))
|
||||
lru_cache_add(page);
|
||||
@ -660,34 +642,36 @@ find_page:
|
||||
} else if (ret)
|
||||
goto out;
|
||||
|
||||
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) {
|
||||
char *dst = kmap_atomic(page, KM_USER0);
|
||||
if (buf->page != page) {
|
||||
/*
|
||||
* Careful, ->map() uses KM_USER0!
|
||||
*/
|
||||
char *src = buf->ops->map(info, buf, 1);
|
||||
char *dst = kmap_atomic(page, KM_USER1);
|
||||
|
||||
memcpy(dst + offset, src + buf->offset, this_len);
|
||||
flush_dcache_page(page);
|
||||
kunmap_atomic(dst, KM_USER0);
|
||||
kunmap_atomic(dst, KM_USER1);
|
||||
buf->ops->unmap(info, buf, src);
|
||||
}
|
||||
|
||||
ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len);
|
||||
if (ret == AOP_TRUNCATED_PAGE) {
|
||||
if (!ret) {
|
||||
/*
|
||||
* Return the number of bytes written and mark page as
|
||||
* accessed, we are now done!
|
||||
*/
|
||||
ret = this_len;
|
||||
mark_page_accessed(page);
|
||||
balance_dirty_pages_ratelimited(mapping);
|
||||
} else if (ret == AOP_TRUNCATED_PAGE) {
|
||||
page_cache_release(page);
|
||||
goto find_page;
|
||||
} else if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Return the number of bytes written.
|
||||
*/
|
||||
ret = this_len;
|
||||
mark_page_accessed(page);
|
||||
balance_dirty_pages_ratelimited(mapping);
|
||||
}
|
||||
out:
|
||||
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN))
|
||||
page_cache_release(page);
|
||||
|
||||
page_cache_release(page);
|
||||
unlock_page(page);
|
||||
out_nomem:
|
||||
buf->ops->unmap(info, buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1095,7 +1079,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
|
||||
*/
|
||||
static int get_iovec_page_array(const struct iovec __user *iov,
|
||||
unsigned int nr_vecs, struct page **pages,
|
||||
struct partial_page *partial)
|
||||
struct partial_page *partial, int aligned)
|
||||
{
|
||||
int buffers = 0, error = 0;
|
||||
|
||||
@ -1135,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov,
|
||||
* in the user pages.
|
||||
*/
|
||||
off = (unsigned long) base & ~PAGE_MASK;
|
||||
|
||||
/*
|
||||
* If asked for alignment, the offset must be zero and the
|
||||
* length a multiple of the PAGE_SIZE.
|
||||
*/
|
||||
error = -EINVAL;
|
||||
if (aligned && (off || len & ~PAGE_MASK))
|
||||
break;
|
||||
|
||||
npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
if (npages > PIPE_BUFFERS - buffers)
|
||||
npages = PIPE_BUFFERS - buffers;
|
||||
@ -1228,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
|
||||
else if (unlikely(!nr_segs))
|
||||
return 0;
|
||||
|
||||
spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial);
|
||||
spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial,
|
||||
flags & SPLICE_F_GIFT);
|
||||
if (spd.nr_pages <= 0)
|
||||
return spd.nr_pages;
|
||||
|
||||
@ -1336,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe,
|
||||
obuf = opipe->bufs + nbuf;
|
||||
*obuf = *ibuf;
|
||||
|
||||
/*
|
||||
* Don't inherit the gift flag, we need to
|
||||
* prevent multiple steals of this page.
|
||||
*/
|
||||
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
|
||||
|
||||
if (obuf->len > len)
|
||||
obuf->len = len;
|
||||
|
||||
|
@ -5,8 +5,9 @@
|
||||
|
||||
#define PIPE_BUFFERS (16)
|
||||
|
||||
#define PIPE_BUF_FLAG_STOLEN 0x01
|
||||
#define PIPE_BUF_FLAG_LRU 0x02
|
||||
#define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */
|
||||
#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */
|
||||
#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
|
||||
|
||||
struct pipe_buffer {
|
||||
struct page *page;
|
||||
@ -15,10 +16,23 @@ struct pipe_buffer {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* Note on the nesting of these functions:
|
||||
*
|
||||
* ->pin()
|
||||
* ->steal()
|
||||
* ...
|
||||
* ->map()
|
||||
* ...
|
||||
* ->unmap()
|
||||
*
|
||||
* That is, ->map() must be called on a pinned buffer, same goes for ->steal().
|
||||
*/
|
||||
struct pipe_buf_operations {
|
||||
int can_merge;
|
||||
void * (*map)(struct file *, struct pipe_inode_info *, struct pipe_buffer *);
|
||||
void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *);
|
||||
void * (*map)(struct pipe_inode_info *, struct pipe_buffer *, int);
|
||||
void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *, void *);
|
||||
int (*pin)(struct pipe_inode_info *, struct pipe_buffer *);
|
||||
void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
|
||||
int (*steal)(struct pipe_inode_info *, struct pipe_buffer *);
|
||||
void (*get)(struct pipe_inode_info *, struct pipe_buffer *);
|
||||
@ -51,6 +65,12 @@ struct pipe_inode_info * alloc_pipe_info(struct inode * inode);
|
||||
void free_pipe_info(struct inode * inode);
|
||||
void __free_pipe_info(struct pipe_inode_info *);
|
||||
|
||||
/* Generic pipe buffer ops functions */
|
||||
void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *, int);
|
||||
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 *);
|
||||
int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *);
|
||||
|
||||
/*
|
||||
* splice is tied to pipes as a transport (at least for now), so we'll just
|
||||
* add the splice flags here.
|
||||
@ -60,6 +80,7 @@ void __free_pipe_info(struct pipe_inode_info *);
|
||||
/* we may still block on the fd we splice */
|
||||
/* from/to, of course */
|
||||
#define SPLICE_F_MORE (0x04) /* expect more data */
|
||||
#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */
|
||||
|
||||
/*
|
||||
* Passed to the actors
|
||||
|
Loading…
Reference in New Issue
Block a user