diff options
author | Jens Axboe | 2006-03-30 15:15:30 +0200 |
---|---|---|
committer | Linus Torvalds | 2006-03-30 12:28:18 -0800 |
commit | 5274f052e7b3dbd81935772eb551dfd0325dfa9d (patch) | |
tree | c79f813ec513660edb6f1e4a75cb366c6b84f53f /fs/pipe.c | |
parent | 5d4fe2c1ce83c3e967ccc1ba3d580c1a5603a866 (diff) |
[PATCH] Introduce sys_splice() system call
This adds support for the sys_splice system call. Using a pipe as a
transport, it can connect to files or sockets (latter as output only).
From the splice.c comments:
"splice": joining two ropes together by interweaving their strands.
This is the "extended pipe" functionality, where a pipe is used as
an arbitrary in-memory buffer. Think of a pipe as a small kernel
buffer that you can use to transfer data from one end to the other.
The traditional unix read/write is extended with a "splice()" operation
that transfers data buffers to or from a pipe buffer.
Named by Larry McVoy, original implementation from Linus, extended by
Jens to support splicing to files and fixing the initial implementation
bugs.
Signed-off-by: Jens Axboe <axboe@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/pipe.c')
-rw-r--r-- | fs/pipe.c | 33 |
1 files changed, 28 insertions, 5 deletions
diff --git a/fs/pipe.c b/fs/pipe.c index e2f4f1d9ffc2..2414bf270db6 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -15,6 +15,7 @@ #include <linux/pipe_fs_i.h> #include <linux/uio.h> #include <linux/highmem.h> +#include <linux/pagemap.h> #include <asm/uaccess.h> #include <asm/ioctls.h> @@ -94,11 +95,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buff { struct page *page = buf->page; - if (info->tmp_page) { - __free_page(page); + /* + * 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 + * allocation cache + */ + if (page_count(page) == 1 && !info->tmp_page) { + info->tmp_page = page; return; } - info->tmp_page = page; + + /* + * Otherwise just release our reference to it + */ + page_cache_release(page); } static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf) @@ -152,6 +162,11 @@ pipe_readv(struct file *filp, const struct iovec *_iov, chars = total_len; addr = ops->map(filp, info, buf); + if (IS_ERR(addr)) { + if (!ret) + ret = PTR_ERR(addr); + break; + } error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); ops->unmap(info, buf); if (unlikely(error)) { @@ -254,8 +269,16 @@ pipe_writev(struct file *filp, const struct iovec *_iov, struct pipe_buf_operations *ops = buf->ops; int offset = buf->offset + buf->len; if (ops->can_merge && offset + chars <= PAGE_SIZE) { - void *addr = ops->map(filp, info, buf); - int error = pipe_iov_copy_from_user(offset + addr, iov, chars); + void *addr; + int error; + + addr = ops->map(filp, info, buf); + if (IS_ERR(addr)) { + error = PTR_ERR(addr); + goto out; + } + error = pipe_iov_copy_from_user(offset + addr, iov, + chars); ops->unmap(info, buf); ret = error; do_wakeup = 1; |