[PATCH] Add vmsplice
authorJens Axboe <axboe@suse.de>
Fri, 21 Apr 2006 07:44:23 +0000 (09:44 +0200)
committerJens Axboe <axboe@suse.de>
Fri, 21 Apr 2006 07:44:23 +0000 (09:44 +0200)
.gitignore
Makefile
splice.h
vmsplice.c [new file with mode: 0644]

index 5934fdedf81f6295abafdb71b5d0ce3886628032..0b94c0960e9c7311e142db2a6b9f3600da5760e7 100644 (file)
@@ -8,3 +8,4 @@ splice-net
 splice-out
 splice-test4c
 splice-test4s
+vmsplice
index 29d76a4cdc6374ea36d2273ea57d4a1b6f2a8bd4..c9618c1bb1459c1f9a723d0d7e45168c7816a3dc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 CC     = gcc
 CFLAGS = -Wall -O2 -g -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
-PROGS  = ktee ktee-net splice-cp splice-in splice-out splice-net splice-test4c splice-test4s
+PROGS  = ktee ktee-net splice-cp splice-in splice-out splice-net splice-test4c splice-test4s vmsplice
 
 all: depend $(PROGS)
 
index 0cdd1ca2869b5b91697f569d8686bf15820a2a99..e1a059278b751914e0455e2d1166680bfc30af9b 100644 (file)
--- a/splice.h
+++ b/splice.h
@@ -4,19 +4,28 @@
 #if defined(__i386__)
 #define __NR_splice    313
 #define __NR_tee       315
+#define __NR_vmsplice  316
 #elif defined(__x86_64__)
 #define __NR_splice    275
 #define __NR_tee       276
+#define __NR_vmsplice  278
 #elif defined(__powerpc__) || defined(__powerpc64__)
 #define __NR_splice    283
 #define __NR_tee       284
+#define __NR_vmsplice  285
 #elif defined(__ia64__)
 #define __NR_splice    1297
 #define __NR_tee       1301
+#define __NR_vmsplice  1301
 #else
 #error unsupported arch
 #endif
 
+#ifndef F_SETPSZ
+#define        F_SETPSZ        15
+#define F_GETPSZ       16
+#endif
+
 #define SPLICE_F_MOVE  (0x01)  /* move pages instead of copying */
 #define SPLICE_F_NONBLOCK (0x02) /* don't block on the pipe splicing (but */
                                 /* we may still block on the fd we splice */
@@ -35,6 +44,11 @@ static inline int tee(int fdin, int fdout, size_t len, unsigned int flags)
        return syscall(__NR_tee, fdin, fdout, len, flags);
 }
 
+static inline int vmsplice(int fd, void *buffer, size_t len, unsigned int flags)
+{
+       return syscall(__NR_vmsplice, fd, buffer, len, flags);
+}
+
 #define SPLICE_SIZE    (64*1024)
 
 #define BUG_ON(c) assert(!(c))
diff --git a/vmsplice.c b/vmsplice.c
new file mode 100644 (file)
index 0000000..be77534
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Use vmsplice to fill some user memory into a pipe. vmsplice writes
+ * to stdout, so that must be a pipe.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "splice.h"
+
+#define ALIGN_BUF
+
+#ifdef ALIGN_BUF
+#define ALIGN_MASK     (65535) /* 64k-1, should just be PAGE_SIZE - 1 */
+#define ALIGN(buf)     (void *) (((unsigned long) (buf) + ALIGN_MASK) & ~ALIGN_MASK)
+#else
+#define ALIGN_MASK     (0)
+#define ALIGN(buf)     (buf)
+#endif
+
+int do_vmsplice(int fd, void *buffer, int len)
+{
+       struct pollfd pfd = { .fd = fd, .events = POLLOUT, };
+       int written;
+
+       while (len) {
+               /*
+                * in a real app you'd be more clever with poll of course,
+                * here we are basically just blocking on output room and
+                * not using the free time for anything interesting.
+                */
+               if (poll(&pfd, 1, -1) < 0)
+                       return error("poll");
+
+               written = vmsplice(fd, buffer, min(SPLICE_SIZE, len), 0);
+
+               if (written <= 0)
+                       return error("vmsplice");
+
+               len -= written;
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned char *buffer;
+       struct stat sb;
+       long page_size;
+       int i, ret;
+
+       if (fstat(STDOUT_FILENO, &sb) < 0)
+               return error("stat");
+       if (!S_ISFIFO(sb.st_mode)) {
+               fprintf(stderr, "stdout must be a pipe\n");
+               return 1;
+       }
+
+       ret = fcntl(STDOUT_FILENO, F_GETPSZ);
+       if (ret < 0)
+               return error("F_GETPSZ");
+
+       page_size = sysconf(_SC_PAGESIZE);
+       if (page_size < 0)
+               return error("_SC_PAGESIZE");
+
+       fprintf(stderr, "Pipe size: %d pages / %ld bytes\n", ret, ret * page_size);
+
+       buffer = ALIGN(malloc(2 * SPLICE_SIZE + ALIGN_MASK));
+       for (i = 0; i < 2 * SPLICE_SIZE; i++)
+               buffer[i] = (i & 0xff);
+
+       do {
+               /*
+                * vmsplice the first half of the buffer into the pipe
+                */
+               if (do_vmsplice(STDOUT_FILENO, buffer, SPLICE_SIZE))
+                       break;
+
+               /*
+                * first half is now in pipe, but we don't quite know when
+                * we can reuse it.
+                */
+
+               /*
+                * vmsplice second half
+                */
+               if (do_vmsplice(STDOUT_FILENO, buffer + SPLICE_SIZE, SPLICE_SIZE))
+                       break;
+
+               /*
+                * We still don't know when we can reuse the second half of
+                * the buffer, but we do now know that all parts of the first
+                * half have been consumed from the pipe - so we can reuse that.
+                */
+       } while (0);
+
+       return 0;
+}