diff options
| author | Jens Axboe <axboe@suse.de> | 2006-03-30 08:15:30 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-30 15:28:18 -0500 |
| commit | 5274f052e7b3dbd81935772eb551dfd0325dfa9d (patch) | |
| tree | c79f813ec513660edb6f1e4a75cb366c6b84f53f | |
| 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>
| -rw-r--r-- | arch/i386/kernel/syscall_table.S | 1 | ||||
| -rw-r--r-- | arch/ia64/kernel/entry.S | 1 | ||||
| -rw-r--r-- | fs/Makefile | 2 | ||||
| -rw-r--r-- | fs/ext2/file.c | 2 | ||||
| -rw-r--r-- | fs/ext3/file.c | 2 | ||||
| -rw-r--r-- | fs/pipe.c | 33 | ||||
| -rw-r--r-- | fs/reiserfs/file.c | 2 | ||||
| -rw-r--r-- | fs/splice.c | 612 | ||||
| -rw-r--r-- | include/asm-i386/unistd.h | 3 | ||||
| -rw-r--r-- | include/asm-ia64/unistd.h | 3 | ||||
| -rw-r--r-- | include/asm-powerpc/unistd.h | 3 | ||||
| -rw-r--r-- | include/asm-x86_64/unistd.h | 4 | ||||
| -rw-r--r-- | include/linux/fs.h | 4 | ||||
| -rw-r--r-- | include/linux/syscalls.h | 2 | ||||
| -rw-r--r-- | net/socket.c | 6 |
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 | ||
| 15 | obj-$(CONFIG_INOTIFY) += inotify.o | 15 | obj-$(CONFIG_INOTIFY) += inotify.o |
| 16 | obj-$(CONFIG_EPOLL) += eventpoll.o | 16 | obj-$(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 | ||
| 124 | struct inode_operations ext3_file_inode_operations = { | 126 | struct inode_operations ext3_file_inode_operations = { |
| @@ -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 | ||
| 104 | static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf) | 114 | 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, | |||
| 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 | ||
| 1581 | struct inode_operations reiserfs_file_inode_operations = { | 1583 | struct 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 | */ | ||
| 28 | struct splice_desc { | ||
| 29 | unsigned int len, total_len; /* current and remaining length */ | ||
