Skip to contentSkip to conversation

splice support

RFC PATCH New Jens Axboe 1 participant 1 message Dec 19, 2005

Subsystems (2)
lore
Share & tools
lore

Jens Axboe

author
#0 lore raw

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
index 9b21a31..8b9d2e1 100644
@@ -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
index 5e61124..1fb5eb7 100644
@@ -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
index 4c26557..328f825 100644
@@ -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
index a484412..3a4b0c0 100644
@@ -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
index 98e7834..a0ea89b 100644
@@ -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
index 66aa0b9..36c0c18 100644
@@ -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
index 0f92e78..623ed31 100644
@@ -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
index 0991dfc..1ef5271 100644
@@ -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
index 2c42150..142b938 100644
@@ -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
index cc35b6a..9725b00 100644
@@ -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(str
 extern 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
index 1767073..966d535 100644
@@ -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;
}