splice support
Subsystems (2)
RFC PATCH 1/1 splice support
Hi,
I decided to try and get the sys_splice that Linus posted close to a year ago more complete, this is the result so far. It supports splicing pages between pipes and files, in both directions. I'm attaching to simple user space test tools to splice to and from a pipe:
o splice-in <file> : splice pages from file to stdout
o splice-out <file> : splice pages from stdin to file
So if you wanted, you could copy a file to newfile by doing:
# splice-in file | splice-out newfile
which would move the pages to newfiles address space.
Now this isn't complete of course, I'm just posting this in the spirit of posting early. Changes that I envision:
- The vfs interface is nasty, it's coupled to actual pipes now which isn't the intention. This needs to be split so the ->splice_read and ->splice_write merely fill the pipe buffers and the splice.c code handles the actual pipe (if there is one).
- sys_splice() should handle "any" file descriptor, so we can support file <-> splicing and file -> socket splicing. We require a real pipe now, because of the above vfs interface badness.
- Add ability to splice to a socket, using ->sendpage().
- The ->copy_page variable is ugly.
Probably more, but these are the most important ones. Patch is against 2.6.15-rc6, it also resides in the 'splice' branch of the block layer git repo (yeah it's not really block io, but it's io at least :-)
Subject: [PATCH] Initial support for sys_splice()
From: Jens Axboe <[email protected]>
Date: 1134983694 +0100
---
arch/i386/kernel/syscall_table.S | 1 arch/ppc/kernel/misc.S | 1 fs/Makefile | 2 fs/ext2/file.c | 2 fs/ext3/file.c | 2 fs/pipe.c | 16 + fs/splice.c | 526 ++++++++++++++++++++++++++++++++++++++ include/asm-i386/unistd.h | 3 include/asm-powerpc/unistd.h | 3 include/asm-x86_64/apic.h | 2 include/asm-x86_64/system.h | 2 include/asm-x86_64/unistd.h | 4 include/linux/fs.h | 4 include/linux/pipe_fs_i.h | 2 14 files changed, 561 insertions(+), 9 deletions(-) create mode 100644 fs/splice.c
05634a4999f9b79f42b98b1e1fb42fc0404bfe66
arch/i386/kernel/syscall_table.S +1 -0
@@ -294,3 +294,4 @@ ENTRY(sys_call_table) .long sys_inotify_init .long sys_inotify_add_watch .long sys_inotify_rm_watch + .long sys_splice
arch/ppc/kernel/misc.S +1 -0
@@ -1403,3 +1403,4 @@ _GLOBAL(sys_call_table) .long sys_inotify_init /* 275 */ .long sys_inotify_add_watch .long sys_inotify_rm_watch + .long sys_splice
fs/Makefile +1 -1
@@ -10,7 +10,7 @@ obj-y := open.o read_write.o file_table. ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ - ioprio.o pnode.o + ioprio.o pnode.o splice.o obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o
fs/ext2/file.c +2 -0
@@ -53,6 +53,8 @@ struct file_operations ext2_file_operati .readv = generic_file_readv, .writev = generic_file_writev, .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, };#ifdef CONFIG_EXT2_FS_XIP
fs/ext3/file.c +2 -0
@@ -119,6 +119,8 @@ struct file_operations ext3_file_operati .release = ext3_release_file, .fsync = ext3_sync_file, .sendfile = generic_file_sendfile, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, };struct inode_operations ext3_file_inode_operations = {
fs/pipe.c +13 -3
@@ -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 {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)
fs/splice.c new file +526 -0
526 lines not on this page · raw message
include/asm-i386/unistd.h +2 -1
@@ -299,8 +299,9 @@#define __NR_inotify_init 291#define __NR_inotify_add_watch 292#define __NR_inotify_rm_watch 293 +#define __NR_splice 294 -#define NR_syscalls 294 +#define NR_syscalls 295/* * user-visible error numbers are in the range -1 - -128: see
include/asm-powerpc/unistd.h +2 -1
@@ -296,8 +296,9 @@#define __NR_inotify_init 275#define __NR_inotify_add_watch 276#define __NR_inotify_rm_watch 277 +#define __NR_splice 278 -#define __NR_syscalls 278 +#define __NR_syscalls 279#ifdef __KERNEL__#define __NR__exit __NR_exit
include/asm-x86_64/unistd.h +3 -1
@@ -571,8 +571,10 @@ __SYSCALL(__NR_inotify_init, sys_inotify __SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch)#define __NR_inotify_rm_watch 255 __SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch) +#define __NR_splice 256 +__SYSCALL(__NR_splice, sys_splice) -#define __NR_syscall_max __NR_inotify_rm_watch +#define __NR_syscall_max __NR_splice#ifndef __NO_STUBS/* user-visible error numbers are in the range -1 - -4095 */
include/linux/fs.h +4 -0
@@ -995,6 +995,8 @@ struct file_operations {int (*check_flags)(int );int (*dir_notify)(struct file *filp,unsigned long arg);int (*flock) (struct file *,int ,struct file_lock *); +ssize_t (*splice_write)(struct inode *,struct file *,size_t ,unsigned long ); +ssize_t (*splice_read)(struct file *,struct inode *,size_t ,unsigned long ); };struct inode_operations {
@@ -1538,6 +1540,8 @@ extern ssize_t generic_file_sendfile(strextern void do_generic_mapping_read (struct address_space *mapping,struct file_ra_state *,struct file *,loff_t *,read_descriptor_t *,read_actor_t ); +extern ssize_t generic_file_splice_read (struct file *,struct inode *,size_t ,unsigned long ); +extern ssize_t generic_file_splice_write (struct inode *,struct file *,size_t ,unsigned long );extern void file_ra_state_init (struct file_ra_state *ra,struct address_space *mapping); extern ssize_t generic_file_readv(struct file *filp, const struct iovec *iov,
include/linux/pipe_fs_i.h +2 -0
@@ -8,6 +8,7 @@struct pipe_buffer {struct page *page;unsigned int offset, len; +unsigned int copy_page;struct pipe_buf_operations *ops; };
@@ -16,6 +17,7 @@ struct pipe_buf_operations {void * (*map)(struct file *,struct pipe_inode_info *,struct pipe_buffer *);void (*unmap)(struct pipe_inode_info *,struct pipe_buffer *);void (*release)(struct pipe_inode_info *,struct pipe_buffer *); +int (*claim)(struct pipe_inode_info *,struct pipe_buffer *); };struct pipe_inode_info {
Signature · 4 lines
0.99.9.GIT -- Jens Axboe
/*
* Splice argument file to stdout
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__i386__)
#define __NR_splice 294
#elif defined(__x86_64__)
#define __NR_splice 256
#elif defined(__powerpc__) || defined(__powerpc64__)
#define __NR_splice 278
#else
#error unsupported arch
#endif
static inline int splice(int fdin, int fdout, size_t len, unsigned long flags)
{
return syscall(__NR_splice, fdin, fdout, len, flags);
}int main(int argc, char *argv[])
{
struct stat sb;
int fd;
if (argc < 2) {
printf("%s: infile\n", argv[0]);
return 1;
}
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
if (fstat(fd, &sb) < 0) {
perror("stat");
return 1;
}
do {
int ret = splice(fd, STDOUT_FILENO, sb.st_size, 0);
if (ret < 0) {
perror("splice");
break;
} else if (!ret)
break;
sb.st_size -= ret;
} while (1);
close(fd);
return 0;
}/*
* Splice argument file to stdout
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define SPLICE_SIZE (64*1024)
#if defined(__i386__)
#define __NR_splice 294
#elif defined(__x86_64__)
#define __NR_splice 256
#elif defined(__powerpc__) || defined(__powerpc64__)
#define __NR_splice 278
#else
#error unsupported arch
#endif
static inline int splice(int fdin, int fdout, size_t len, unsigned long flags)
{
return syscall(__NR_splice, fdin, fdout, len, flags);
}int main(int argc, char *argv[])
{
int fd;
if (argc < 2) {
printf("%s: outfile\n", argv[0]);
return 1;
}
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
do {
int ret = splice(STDIN_FILENO, fd, SPLICE_SIZE, 0);
if (ret < 0) {
perror("splice");
break;
} else if (!ret)
break;
} while (1);
close(fd);
return 0;
}