Commit 9817d207 authored by Linus Torvalds's avatar Linus Torvalds

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
parents cf105601 7afa6fd0
...@@ -1104,6 +1104,8 @@ const struct file_operations def_blk_fops = { ...@@ -1104,6 +1104,8 @@ const struct file_operations def_blk_fops = {
.readv = generic_file_readv, .readv = generic_file_readv,
.writev = generic_file_write_nolock, .writev = generic_file_write_nolock,
.sendfile = generic_file_sendfile, .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) int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
......
...@@ -55,7 +55,8 @@ void pipe_wait(struct pipe_inode_info *pipe) ...@@ -55,7 +55,8 @@ void pipe_wait(struct pipe_inode_info *pipe)
} }
static int 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; unsigned long copy;
...@@ -64,8 +65,13 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) ...@@ -64,8 +65,13 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len)
iov++; iov++;
copy = min_t(unsigned long, len, iov->iov_len); copy = min_t(unsigned long, len, iov->iov_len);
if (atomic) {
if (__copy_from_user_inatomic(to, iov->iov_base, copy))
return -EFAULT;
} else {
if (copy_from_user(to, iov->iov_base, copy)) if (copy_from_user(to, iov->iov_base, copy))
return -EFAULT; return -EFAULT;
}
to += copy; to += copy;
len -= copy; len -= copy;
iov->iov_base += copy; iov->iov_base += copy;
...@@ -75,7 +81,8 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) ...@@ -75,7 +81,8 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len)
} }
static int 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; unsigned long copy;
...@@ -84,8 +91,13 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) ...@@ -84,8 +91,13 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len)
iov++; iov++;
copy = min_t(unsigned long, len, iov->iov_len); copy = min_t(unsigned long, len, iov->iov_len);
if (atomic) {
if (__copy_to_user_inatomic(iov->iov_base, from, copy))
return -EFAULT;
} else {
if (copy_to_user(iov->iov_base, from, copy)) if (copy_to_user(iov->iov_base, from, copy))
return -EFAULT; return -EFAULT;
}
from += copy; from += copy;
len -= copy; len -= copy;
iov->iov_base += copy; iov->iov_base += copy;
...@@ -94,13 +106,52 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) ...@@ -94,13 +106,52 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len)
return 0; 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, static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
struct page *page = buf->page; struct page *page = buf->page;
buf->flags &= ~PIPE_BUF_FLAG_STOLEN;
/* /*
* If nobody else uses this page, and we don't already have a * 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 * 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, ...@@ -112,38 +163,58 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
page_cache_release(page); page_cache_release(page);
} }
static void * anon_pipe_buf_map(struct file *file, struct pipe_inode_info *pipe, void *generic_pipe_buf_map(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) 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); return kmap(buf->page);
} }
static void anon_pipe_buf_unmap(struct pipe_inode_info *pipe, void generic_pipe_buf_unmap(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) struct pipe_buffer *buf, void *map_data)
{ {
if (buf->flags & PIPE_BUF_FLAG_ATOMIC) {
buf->flags &= ~PIPE_BUF_FLAG_ATOMIC;
kunmap_atomic(map_data, KM_USER0);
} else
kunmap(buf->page); kunmap(buf->page);
} }
static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, static int anon_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
buf->flags |= PIPE_BUF_FLAG_STOLEN; struct page *page = buf->page;
if (page_count(page) == 1) {
lock_page(page);
return 0; return 0;
}
return 1;
} }
static void anon_pipe_buf_get(struct pipe_inode_info *info, void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf)
struct pipe_buffer *buf)
{ {
page_cache_get(buf->page); 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 = { static struct pipe_buf_operations anon_pipe_buf_ops = {
.can_merge = 1, .can_merge = 1,
.map = anon_pipe_buf_map, .map = generic_pipe_buf_map,
.unmap = anon_pipe_buf_unmap, .unmap = generic_pipe_buf_unmap,
.pin = generic_pipe_buf_pin,
.release = anon_pipe_buf_release, .release = anon_pipe_buf_release,
.steal = anon_pipe_buf_steal, .steal = anon_pipe_buf_steal,
.get = anon_pipe_buf_get, .get = generic_pipe_buf_get,
}; };
static ssize_t static ssize_t
...@@ -174,22 +245,33 @@ pipe_readv(struct file *filp, const struct iovec *_iov, ...@@ -174,22 +245,33 @@ pipe_readv(struct file *filp, const struct iovec *_iov,
struct pipe_buf_operations *ops = buf->ops; struct pipe_buf_operations *ops = buf->ops;
void *addr; void *addr;
size_t chars = buf->len; size_t chars = buf->len;
int error; int error, atomic;
if (chars > total_len) if (chars > total_len)
chars = total_len; chars = total_len;
addr = ops->map(filp, pipe, buf); error = ops->pin(pipe, buf);
if (IS_ERR(addr)) { if (error) {
if (!ret) if (!ret)
ret = PTR_ERR(addr); error = ret;
break; 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)) { if (unlikely(error)) {
/*
* Just retry with the slow path if we failed.
*/
if (atomic) {
atomic = 0;
goto redo;
}
if (!ret) if (!ret)
ret = -EFAULT; ret = error;
break; break;
} }
ret += chars; ret += chars;
...@@ -293,21 +375,28 @@ pipe_writev(struct file *filp, const struct iovec *_iov, ...@@ -293,21 +375,28 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
int offset = buf->offset + buf->len; int offset = buf->offset + buf->len;
if (ops->can_merge && offset + chars <= PAGE_SIZE) { if (ops->can_merge && offset + chars <= PAGE_SIZE) {
int error, atomic = 1;
void *addr; void *addr;
int error;
addr = ops->map(filp, pipe, buf); error = ops->pin(pipe, buf);
if (IS_ERR(addr)) { if (error)
error = PTR_ERR(addr);
goto out; 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, error = pipe_iov_copy_from_user(offset + addr, iov,
chars); chars, atomic);
ops->unmap(pipe, buf); ops->unmap(pipe, buf, addr);
ret = error; ret = error;
do_wakeup = 1; do_wakeup = 1;
if (error) if (error) {
if (atomic) {
atomic = 0;
goto redo1;
}
goto out; goto out;
}
buf->len += chars; buf->len += chars;
total_len -= chars; total_len -= chars;
ret = chars; ret = chars;
...@@ -330,7 +419,8 @@ pipe_writev(struct file *filp, const struct iovec *_iov, ...@@ -330,7 +419,8 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1); int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1);
struct pipe_buffer *buf = pipe->bufs + newbuf; struct pipe_buffer *buf = pipe->bufs + newbuf;
struct page *page = pipe->tmp_page; struct page *page = pipe->tmp_page;
int error; char *src;
int error, atomic = 1;
if (!page) { if (!page) {
page = alloc_page(GFP_HIGHUSER); page = alloc_page(GFP_HIGHUSER);
...@@ -350,11 +440,27 @@ pipe_writev(struct file *filp, const struct iovec *_iov, ...@@ -350,11 +440,27 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
if (chars > total_len) if (chars > total_len)
chars = total_len; chars = total_len;
error = pipe_iov_copy_from_user(kmap(page), iov, chars); 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); kunmap(page);
if (unlikely(error)) { if (unlikely(error)) {
if (atomic) {
atomic = 0;
goto redo2;
}
if (!ret) if (!ret)
ret = -EFAULT; ret = error;
break; break;
} }
ret += chars; ret += chars;
......
...@@ -78,7 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, ...@@ -78,7 +78,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info,
return 1; return 1;
} }
buf->flags |= PIPE_BUF_FLAG_STOLEN | PIPE_BUF_FLAG_LRU; buf->flags |= PIPE_BUF_FLAG_LRU;
return 0; return 0;
} }
...@@ -87,11 +87,10 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, ...@@ -87,11 +87,10 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info,
{ {
page_cache_release(buf->page); page_cache_release(buf->page);
buf->page = NULL; 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, static int page_cache_pipe_buf_pin(struct pipe_inode_info *info,
struct pipe_inode_info *info,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
struct page *page = buf->page; struct page *page = buf->page;
...@@ -118,64 +117,44 @@ static void *page_cache_pipe_buf_map(struct file *file, ...@@ -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); unlock_page(page);
} }
return kmap(page); return 0;
error: error:
unlock_page(page); unlock_page(page);
return ERR_PTR(err); return 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);
} }
static struct pipe_buf_operations page_cache_pipe_buf_ops = { static struct pipe_buf_operations page_cache_pipe_buf_ops = {
.can_merge = 0, .can_merge = 0,
.map = page_cache_pipe_buf_map, .map = generic_pipe_buf_map,
.unmap = page_cache_pipe_buf_unmap, .unmap = generic_pipe_buf_unmap,
.pin = page_cache_pipe_buf_pin,
.release = page_cache_pipe_buf_release, .release = page_cache_pipe_buf_release,
.steal = page_cache_pipe_buf_steal, .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, static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf) struct pipe_buffer *buf)
{ {
if (!(buf->flags & PIPE_BUF_FLAG_GIFT))
return 1; return 1;
return 0;
} }
static struct pipe_buf_operations user_page_pipe_buf_ops = { static struct pipe_buf_operations user_page_pipe_buf_ops = {
.can_merge = 0, .can_merge = 0,
.map = user_page_pipe_buf_map, .map = generic_pipe_buf_map,
.unmap = user_page_pipe_buf_unmap, .unmap = generic_pipe_buf_unmap,
.pin = generic_pipe_buf_pin,
.release = page_cache_pipe_buf_release, .release = page_cache_pipe_buf_release,
.steal = user_page_pipe_buf_steal, .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, ...@@ -210,6 +189,9 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
buf->offset = spd->partial[page_nr].offset; buf->offset = spd->partial[page_nr].offset;
buf->len = spd->partial[page_nr].len; buf->len = spd->partial[page_nr].len;
buf->ops = spd->ops; buf->ops = spd->ops;
if (spd->flags & SPLICE_F_GIFT)
buf->flags |= PIPE_BUF_FLAG_GIFT;
pipe->nrbufs++; pipe->nrbufs++;
page_nr++; page_nr++;
ret += buf->len; ret += buf->len;
...@@ -325,6 +307,12 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, ...@@ -325,6 +307,12 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
*/ */
page = find_get_page(mapping, index); page = find_get_page(mapping, index);
if (!page) { 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. * page didn't exist, allocate one.
*/ */
...@@ -517,26 +505,16 @@ static int pipe_to_sendpage(struct pipe_inode_info *info, ...@@ -517,26 +505,16 @@ static int pipe_to_sendpage(struct pipe_inode_info *info,
{ {
struct file *file = sd->file; struct file *file = sd->file;
loff_t pos = sd->pos; loff_t pos = sd->pos;
ssize_t ret; int ret, more;
void *ptr;
int 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, ret = file->f_op->sendpage(file, buf->page, buf->offset,
&pos, more); sd->len, &pos, more);
}
buf->ops->unmap(info, buf);
return ret; return ret;
} }
...@@ -569,15 +547,14 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, ...@@ -569,15 +547,14 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
unsigned int offset, this_len; unsigned int offset, this_len;
struct page *page; struct page *page;
pgoff_t index; pgoff_t index;
char *src;
int ret; int ret;
/* /*
* make sure the data in this buffer is uptodate * make sure the data in this buffer is uptodate
*/ */
src = buf->ops->map(file, info, buf); ret = buf->ops->pin(info, buf);
if (IS_ERR(src)) if (unlikely(ret))
return PTR_ERR(src); return ret;
index = sd->pos >> PAGE_CACHE_SHIFT; index = sd->pos >> PAGE_CACHE_SHIFT;
offset = sd->pos & ~PAGE_CACHE_MASK; offset = sd->pos & ~PAGE_CACHE_MASK;
...@@ -587,9 +564,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, ...@@ -587,9 +564,10 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
this_len = PAGE_CACHE_SIZE - offset; 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 * If steal succeeds, buf->page is now pruned from the vm
* side (LRU and page cache) and we can reuse it. The page * 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, ...@@ -599,8 +577,12 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
goto find_page; goto find_page;
page = buf->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; goto find_page;
}
page_cache_get(page);
if (!(buf->flags & PIPE_BUF_FLAG_LRU)) if (!(buf->flags & PIPE_BUF_FLAG_LRU))
lru_cache_add(page); lru_cache_add(page);
...@@ -660,34 +642,36 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, ...@@ -660,34 +642,36 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
} else if (ret) } else if (ret)
goto out; goto out;
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN)) { if (buf->page != page) {
char *dst = kmap_atomic(page, KM_USER0); /*
* 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); 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_USER1);
buf->ops->unmap(info, buf, src);
} }
ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len);
if (ret == AOP_TRUNCATED_PAGE) { if (!ret) {
page_cache_release(page);
goto find_page;
} else if (ret)
goto out;
/* /*
* Return the number of bytes written. * Return the number of bytes written and mark page as
* accessed, we are now done!
*/ */
ret = this_len; ret = this_len;
mark_page_accessed(page); mark_page_accessed(page);
balance_dirty_pages_ratelimited(mapping); balance_dirty_pages_ratelimited(mapping);
} else if (ret == AOP_TRUNCATED_PAGE) {
page_cache_release(page);
goto find_page;
}
out: out:
if (!(buf->flags & PIPE_BUF_FLAG_STOLEN))
page_cache_release(page); page_cache_release(page);
unlock_page(page); unlock_page(page);
out_nomem: out_nomem:
buf->ops->unmap(info, buf);
return ret; return ret;
} }
...@@ -1095,7 +1079,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, ...@@ -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, static int get_iovec_page_array(const struct iovec __user *iov,
unsigned int nr_vecs, struct page **pages, unsigned int nr_vecs, struct page **pages,
struct partial_page *partial) struct partial_page *partial, int aligned)
{ {
int buffers = 0, error = 0; int buffers = 0, error = 0;
...@@ -1135,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov, ...@@ -1135,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov,
* in the user pages. * in the user pages.
*/ */
off = (unsigned long) base & ~PAGE_MASK; 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; npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (npages > PIPE_BUFFERS - buffers) if (npages > PIPE_BUFFERS - buffers)
npages = PIPE_BUFFERS - buffers; npages = PIPE_BUFFERS - buffers;
...@@ -1228,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, ...@@ -1228,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
else if (unlikely(!nr_segs)) else if (unlikely(!nr_segs))
return 0; 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) if (spd.nr_pages <= 0)
return spd.nr_pages; return spd.nr_pages;
...@@ -1336,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe, ...@@ -1336,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe,
obuf = opipe->bufs + nbuf; obuf = opipe->bufs + nbuf;
*obuf = *ibuf; *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) if (obuf->len > len)
obuf->len = len; obuf->len = len;
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
#define PIPE_BUFFERS (16) #define PIPE_BUFFERS (16)
#define PIPE_BUF_FLAG_STOLEN 0x01 #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */
#define PIPE_BUF_FLAG_LRU 0x02 #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */
#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
struct pipe_buffer { struct pipe_buffer {
struct page *page; struct page *page;
...@@ -15,10 +16,23 @@ struct pipe_buffer { ...@@ -15,10 +16,23 @@ struct pipe_buffer {
unsigned int flags; 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 { struct pipe_buf_operations {
int can_merge; int can_merge;
void * (*map)(struct file *, 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 (*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 *); void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
int (*steal)(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 *); void (*get)(struct pipe_inode_info *, struct pipe_buffer *);
...@@ -51,6 +65,12 @@ struct pipe_inode_info * alloc_pipe_info(struct inode * inode); ...@@ -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 inode * inode);
void __free_pipe_info(struct pipe_inode_info *); 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 * splice is tied to pipes as a transport (at least for now), so we'll just
* add the splice flags here. * add the splice flags here.
...@@ -60,6 +80,7 @@ void __free_pipe_info(struct pipe_inode_info *); ...@@ -60,6 +80,7 @@ void __free_pipe_info(struct pipe_inode_info *);
/* we may still block on the fd we splice */ /* we may still block on the fd we splice */
/* from/to, of course */ /* from/to, of course */
#define SPLICE_F_MORE (0x04) /* expect more data */ #define SPLICE_F_MORE (0x04) /* expect more data */
#define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */
/* /*
* Passed to the actors * Passed to the actors
......
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