#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 */
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))
--- /dev/null
+/*
+ * 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;
+}