aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-03-30 08:15:30 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-30 15:28:18 -0500
commit5274f052e7b3dbd81935772eb551dfd0325dfa9d (patch)
treec79f813ec513660edb6f1e4a75cb366c6b84f53f
parent5d4fe2c1ce83c3e967ccc1ba3d580c1a5603a866 (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>
-rw-r--r--arch/i386/kernel/syscall_table.S1
-rw-r--r--arch/ia64/kernel/entry.S1
-rw-r--r--fs/Makefile2
-rw-r--r--fs/ext2/file.c2
-rw-r--r--fs/ext3/file.c2
-rw-r--r--fs/pipe.c33
-rw-r--r--fs/reiserfs/file.c2
-rw-r--r--fs/splice.c612
-rw-r--r--include/asm-i386/unistd.h3
-rw-r--r--include/asm-ia64/unistd.h3
-rw-r--r--include/asm-powerpc/unistd.h3
-rw-r--r--include/asm-x86_64/unistd.h4
-rw-r--r--include/linux/fs.h4
-rw-r--r--include/linux/syscalls.h2
-rw-r--r--net/socket.c6
15 files changed, 669 insertions, 11 deletions
diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S
index 326595f3fa4d..ce3ef4fa0551 100644
--- a/arch/i386/kernel/syscall_table.S
+++ b/arch/i386/kernel/syscall_table.S
@@ -312,3 +312,4 @@ ENTRY(sys_call_table)
312 .long sys_unshare /* 310 */ 312 .long sys_unshare /* 310 */
313 .long sys_set_robust_list 313 .long sys_set_robust_list
314 .long sys_get_robust_list 314 .long sys_get_robust_list
315 .long sys_splice
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index 0e3eda99e549..750e8e7fbdc3 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -1605,5 +1605,6 @@ sys_call_table:
1605 data8 sys_ni_syscall // reserved for pselect 1605 data8 sys_ni_syscall // reserved for pselect
1606 data8 sys_ni_syscall // 1295 reserved for ppoll 1606 data8 sys_ni_syscall // 1295 reserved for ppoll
1607 data8 sys_unshare 1607 data8 sys_unshare
1608 data8 sys_splice
1608 1609
1609 .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls 1610 .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
diff --git a/fs/Makefile b/fs/Makefile
index 080b3867be4d..f3a4f7077175 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -10,7 +10,7 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \
10 ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ 10 ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
11 attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ 11 attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
12 seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ 12 seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \
13 ioprio.o pnode.o drop_caches.o 13 ioprio.o pnode.o drop_caches.o splice.o
14 14
15obj-$(CONFIG_INOTIFY) += inotify.o 15obj-$(CONFIG_INOTIFY) += inotify.o
16obj-$(CONFIG_EPOLL) += eventpoll.o 16obj-$(CONFIG_EPOLL) += eventpoll.o
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 509cceca04db..23e2c7ccec1d 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -53,6 +53,8 @@ const struct file_operations ext2_file_operations = {
53 .readv = generic_file_readv, 53 .readv = generic_file_readv,
54 .writev = generic_file_writev, 54 .writev = generic_file_writev,
55 .sendfile = generic_file_sendfile, 55 .sendfile = generic_file_sendfile,
56 .splice_read = generic_file_splice_read,
57 .splice_write = generic_file_splice_write,
56}; 58};
57 59
58#ifdef CONFIG_EXT2_FS_XIP 60#ifdef CONFIG_EXT2_FS_XIP
diff --git a/fs/ext3/file.c b/fs/ext3/file.c
index 783a796220bb..1efefb630ea9 100644
--- a/fs/ext3/file.c
+++ b/fs/ext3/file.c
@@ -119,6 +119,8 @@ const struct file_operations ext3_file_operations = {
119 .release = ext3_release_file, 119 .release = ext3_release_file,
120 .fsync = ext3_sync_file, 120 .fsync = ext3_sync_file,
121 .sendfile = generic_file_sendfile, 121 .sendfile = generic_file_sendfile,
122 .splice_read = generic_file_splice_read,
123 .splice_write = generic_file_splice_write,
122}; 124};
123 125
124struct inode_operations ext3_file_inode_operations = { 126struct inode_operations ext3_file_inode_operations = {
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 @@
15#include <linux/pipe_fs_i.h> 15#include <linux/pipe_fs_i.h>
16#include <linux/uio.h> 16#include <linux/uio.h>
17#include <linux/highmem.h> 17#include <linux/highmem.h>
18#include <linux/pagemap.h>
18 19
19#include <asm/uaccess.h> 20#include <asm/uaccess.h>
20#include <asm/ioctls.h> 21#include <asm/ioctls.h>
@@ -94,11 +95,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buff
94{ 95{
95 struct page *page = buf->page; 96 struct page *page = buf->page;
96 97
97 if (info->tmp_page) { 98 /*
98 __free_page(page); 99 * If nobody else uses this page, and we don't already have a
100 * temporary page, let's keep track of it as a one-deep
101 * allocation cache
102 */
103 if (page_count(page) == 1 && !info->tmp_page) {
104 info->tmp_page = page;
99 return; 105 return;
100 } 106 }
101 info->tmp_page = page; 107
108 /*
109 * Otherwise just release our reference to it
110 */
111 page_cache_release(page);
102} 112}
103 113
104static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf) 114static 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,
152 chars = total_len; 162 chars = total_len;
153 163
154 addr = ops->map(filp, info, buf); 164 addr = ops->map(filp, info, buf);
165 if (IS_ERR(addr)) {
166 if (!ret)
167 ret = PTR_ERR(addr);
168 break;
169 }
155 error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); 170 error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars);
156 ops->unmap(info, buf); 171 ops->unmap(info, buf);
157 if (unlikely(error)) { 172 if (unlikely(error)) {
@@ -254,8 +269,16 @@ pipe_writev(struct file *filp, const struct iovec *_iov,
254 struct pipe_buf_operations *ops = buf->ops; 269 struct pipe_buf_operations *ops = buf->ops;
255 int offset = buf->offset + buf->len; 270 int offset = buf->offset + buf->len;
256 if (ops->can_merge && offset + chars <= PAGE_SIZE) { 271 if (ops->can_merge && offset + chars <= PAGE_SIZE) {
257 void *addr = ops->map(filp, info, buf); 272 void *addr;
258 int error = pipe_iov_copy_from_user(offset + addr, iov, chars); 273 int error;
274
275 addr = ops->map(filp, info, buf);
276 if (IS_ERR(addr)) {
277 error = PTR_ERR(addr);
278 goto out;
279 }
280 error = pipe_iov_copy_from_user(offset + addr, iov,
281 chars);
259 ops->unmap(info, buf); 282 ops->unmap(info, buf);
260 ret = error; 283 ret = error;
261 do_wakeup = 1; 284 do_wakeup = 1;
diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c
index 010094d14da6..cf6e1cf40351 100644
--- a/fs/reiserfs/file.c
+++ b/fs/reiserfs/file.c
@@ -1576,6 +1576,8 @@ const struct file_operations reiserfs_file_operations = {
1576 .sendfile = generic_file_sendfile, 1576 .sendfile = generic_file_sendfile,
1577 .aio_read = generic_file_aio_read, 1577 .aio_read = generic_file_aio_read,
1578 .aio_write = reiserfs_aio_write, 1578 .aio_write = reiserfs_aio_write,
1579 .splice_read = generic_file_splice_read,
1580 .splice_write = generic_file_splice_write,
1579}; 1581};
1580 1582
1581struct inode_operations reiserfs_file_inode_operations = { 1583struct inode_operations reiserfs_file_inode_operations = {
diff --git a/fs/splice.c b/fs/splice.c
new file mode 100644
index 000000000000..efa47c1c4e13
--- /dev/null
+++ b/fs/splice.c
@@ -0,0 +1,612 @@
1/*
2 * "splice": joining two ropes together by interweaving their strands.
3 *
4 * This is the "extended pipe" functionality, where a pipe is used as
5 * an arbitrary in-memory buffer. Think of a pipe as a small kernel
6 * buffer that you can use to transfer data from one end to the other.
7 *
8 * The traditional unix read/write is extended with a "splice()" operation
9 * that transfers data buffers to or from a pipe buffer.
10 *
11 * Named by Larry McVoy, original implementation from Linus, extended by
12 * Jens to support splicing to files and fixing the initial implementation
13 * bugs.
14 *
15 * Copyright (C) 2005 Jens Axboe <axboe@suse.de>
16 * Copyright (C) 2005 Linus Torvalds <torvalds@osdl.org>
17 *
18 */
19#include <linux/fs.h>
20#include <linux/file.h>
21#include <linux/pagemap.h>
22#include <linux/pipe_fs_i.h>
23#include <linux/mm_inline.h>
24
25/*
26 * Passed to the actors
27 */
28struct splice_desc {
29 unsigned int len, total_len; /* current and remaining length */