Initial commit
authorJens Axboe <axboe@suse.de>
Wed, 19 Apr 2006 11:27:06 +0000 (13:27 +0200)
committerJens Axboe <axboe@suse.de>
Wed, 19 Apr 2006 11:27:06 +0000 (13:27 +0200)
Makefile [new file with mode: 0644]
ktee.c [new file with mode: 0644]
splice-cp.c [new file with mode: 0644]
splice-in.c [new file with mode: 0644]
splice-net.c [new file with mode: 0644]
splice-out.c [new file with mode: 0644]
splice-test4c.c [new file with mode: 0644]
splice-test4s.c [new file with mode: 0644]
splice.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..82d4ec4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+CC     = gcc
+CFLAGS = -Wall -O2 -g
+ALL_CFLAGS = $(CFLAGS) -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+PROGS  = ktee splice-cp splice-in splice-out splice-net splice-test4c splice-test4s
+
+all: depend $(PROGS)
+
+%.o: %.c
+       $(CC) -o $*.o -c $(ALL_CFLAGS) $<
+
+depend:
+       @$(CC) -MM $(ALL_CFLAGS) *.c 1> .depend
+
+clean:
+       -rm -f *.o $(PROGS) .depend
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/ktee.c b/ktee.c
new file mode 100644 (file)
index 0000000..7b05361
--- /dev/null
+++ b/ktee.c
@@ -0,0 +1,77 @@
+/*
+ * A tee implementation using sys_tee. If only one argument is given,
+ * stdin output is stored in that file and sent to stdout as well. If a
+ * second argument is given, that must be in the form if host:port - in
+ * that case, output is stored in file and sent over the network to the
+ * given host at given port.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "splice.h"
+
+static int do_splice(int infd, int outfd, unsigned int len, char *msg)
+{
+       while (len) {
+               int written = splice(infd, NULL, outfd, NULL, len, 0);
+
+               if (written <= 0)
+                       return error(msg);
+
+               len -= written;
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       struct stat sb;
+       int fd;
+
+       if (argc < 2) {
+               fprintf(stderr, "%s: outfile\n", argv[0]);
+               return 1;
+       }
+
+       if (fstat(STDIN_FILENO, &sb) < 0)
+               return error("stat");
+       if (!S_ISFIFO(sb.st_mode)) {
+               fprintf(stderr, "stdout must be a pipe\n");
+               return 1;
+       }
+
+       fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+       if (fd < 0)
+               return error("open output");
+
+       do {
+               int tee_len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK);
+
+               if (tee_len < 0) {
+                       if (errno == EAGAIN) {
+                               usleep(1000);
+                               continue;
+                       }
+                       return error("tee");
+               } else if (!tee_len)
+                       break;
+
+               /*
+                * Send output to file, also consumes input pipe.
+                */
+               if (do_splice(STDIN_FILENO, fd, tee_len, "splice-file"))
+                       break;
+       } while (1);
+
+       return 0;
+}
diff --git a/splice-cp.c b/splice-cp.c
new file mode 100644 (file)
index 0000000..ec640f3
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Splice cp a file
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "splice.h"
+
+#define BS     SPLICE_SIZE
+
+int main(int argc, char *argv[])
+{
+       int in_fd, out_fd, pfds[2];
+       struct stat sb;
+
+       if (argc < 3) {
+               printf("%s: infile outfile\n", argv[0]);
+               return 1;
+       }
+
+       in_fd = open(argv[1], O_RDONLY);
+       if (in_fd < 0) {
+               perror("open in");
+               return 1;
+       }
+
+       if (fstat(in_fd, &sb) < 0) {
+               perror("stat");
+               return 1;
+       }
+
+       out_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+       if (out_fd < 0) {
+               perror("open out");
+               return 1;
+       }
+
+       if (pipe(pfds) < 0) {
+               perror("pipe");
+               return 1;
+       }
+
+       do {
+               int this_len = min(BS, sb.st_size);
+               int ret = splice(in_fd, NULL, pfds[1], NULL, this_len, SPLICE_F_NONBLOCK);
+
+               if (ret <= 0)
+                       return error("splice-in");
+
+               sb.st_size -= ret;
+               while (ret > 0) {
+                       int written = splice(pfds[0], NULL, out_fd, NULL, ret, 0);
+                       if (written <= 0)
+                               return error("splice-out");
+                       ret -= written;
+               }
+       } while (sb.st_size);
+
+       close(in_fd);
+       close(pfds[1]);
+       close(out_fd);
+       close(pfds[0]);
+       return 0;
+}
diff --git a/splice-in.c b/splice-in.c
new file mode 100644 (file)
index 0000000..8a8cd61
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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>
+
+#include "splice.h"
+
+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, NULL, STDOUT_FILENO, NULL, sb.st_size, 0);
+
+               if (ret < 0) {
+                       perror("splice");
+                       break;
+               } else if (!ret)
+                       break;
+
+               sb.st_size -= ret;
+       } while (1);
+
+       close(fd);
+       return 0;
+}
diff --git a/splice-net.c b/splice-net.c
new file mode 100644 (file)
index 0000000..f8ba43b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Splice stdin to net
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "splice.h"
+
+static struct timeval start_time;
+static unsigned long long kb_sent;
+static unsigned long iters;
+
+static unsigned long mtime_since(struct timeval *s, struct timeval *e)
+{
+        double sec, usec;
+
+        sec = e->tv_sec - s->tv_sec;
+        usec = e->tv_usec - s->tv_usec;
+        if (sec > 0 && usec < 0) {
+                sec--;
+                usec += 1000000;
+        }
+
+        sec *= (double) 1000;
+        usec /= (double) 1000;
+
+        return sec + usec;
+}
+
+static unsigned long mtime_since_now(struct timeval *s)
+{
+        struct timeval t;
+
+        gettimeofday(&t, NULL);
+        return mtime_since(s, &t);
+}
+
+void show_rate(int sig)
+{
+       unsigned long msecs = mtime_since_now(&start_time);
+
+       printf("Throughput: %LuMiB/sec (%Lu MiB in %lu msecs)\n", kb_sent / msecs, kb_sent, msecs);
+       printf("avg put: %Lu\n", kb_sent / iters);
+}
+
+int main(int argc, char *argv[])
+{
+       struct sockaddr_in addr;
+       unsigned short port;
+       int fd, pfd[2], ffd, ret;
+       unsigned long long b_sent = 0;
+       int bla = 1;
+
+       if (argc < 4) {
+               printf("%s: file target port\n", argv[0]);
+               return 1;
+       }
+
+       port = atoi(argv[3]);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(port);
+
+       if (inet_aton(argv[2], &addr.sin_addr) != 1) {
+               struct hostent *hent = gethostbyname(argv[2]);
+
+               if (!hent) {
+                       perror("gethostbyname");
+                       return 1;
+               }
+
+               memcpy(&addr.sin_addr, hent->h_addr, 4);
+       }
+
+       printf("Connecting to %s/%d\n", argv[2], port);
+
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (fd < 0) {
+               perror("socket");
+               return 1;
+       }
+
+       if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("connect");
+               return 1;
+       }
+
+       if (pipe(pfd) < 0) {
+               perror("pipe");
+               return 1;
+       }
+
+       ffd = open(argv[1], O_RDWR);
+       if (ffd < 0) {
+               perror("open input");
+               return 1;
+       }
+
+       signal(SIGINT, show_rate);
+       gettimeofday(&start_time, NULL);
+
+       do {
+               if (kb_sent >= 128*1024) {
+                       b_sent += 40000;
+                       ret = ftruncate(ffd, b_sent);
+                       printf("trunc file a little %d\n", ret);
+                       bla = 0;
+               }
+                       
+               ret = splice(ffd, NULL, pfd[1], NULL, SPLICE_SIZE, 0x02);
+
+               if (!bla)
+                       printf("spliced %d\n", ret);
+
+               if (ret < 0) {
+                       perror("splice");
+                       break;
+               } else if (!ret) {
+                       break;
+               }
+               b_sent += ret;
+               kb_sent += ret >> 10;
+               iters++;
+               while (ret > 0) {
+                       int flags = 0;
+                       int written = splice(pfd[0], NULL, fd, NULL, ret, flags);
+                       if (written < 0) {
+                               perror("splice-out");
+                               break;
+                       } else if (!written)
+                               break;
+                       ret -= written;
+               }
+       } while (kb_sent < 512 * 1024 && bla);
+
+       show_rate(0);
+       close(fd);
+       return 0;
+}
diff --git a/splice-out.c b/splice-out.c
new file mode 100644 (file)
index 0000000..1080594
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Splice stdout to file
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "splice.h"
+
+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, NULL, fd, NULL, SPLICE_SIZE, 0);
+
+               if (ret < 0) {
+                       perror("splice");
+                       break;
+               } else if (!ret)
+                       break;
+       } while (1);
+
+       close(fd);
+       return 0;
+}
diff --git a/splice-test4c.c b/splice-test4c.c
new file mode 100644 (file)
index 0000000..1cbc078
--- /dev/null
@@ -0,0 +1,387 @@
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/sendfile.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "splice.h"
+
+#define TARGET_HOSTNAME "localhost"
+
+#define BYTES (128*1024*1024UL)
+#define BUFSIZE (64*1024)
+
+#define NR (BYTES/BUFSIZE)
+
+#define SENDFILE_LOOPS 10
+#define SPLICE_LOOPS 10
+#define SPLICE_PIPE_LOOPS 10
+
+static int sendfile_loops = SENDFILE_LOOPS;
+static int splice_loops = SPLICE_LOOPS;
+static int splice_pipe_loops = SPLICE_PIPE_LOOPS;
+
+static volatile long long *cycles, cycles_per_sec;
+
+static struct timeval start_time;
+static double start_cycles;
+static double cpu_pct;
+
+static void start_timing(const char *desc)
+{
+       printf("%-20s: ", desc);
+       fflush(stdout);
+       gettimeofday(&start_time, NULL);
+       /*
+        * Give the lowprio cycles thread a chance to run and thus
+        * we get an accurate timestamp:
+        */
+       sched_yield();
+       start_cycles = (double)*cycles;
+}
+
+static double end_timing(unsigned long long bytes, double *rate)
+{
+       static long long total;
+       struct timeval end_time;
+       double usecs;
+       double end_cycles, cpu_cycles;
+
+       gettimeofday(&end_time, NULL);
+       end_cycles = (double)*cycles;
+
+       usecs = (double) (end_time.tv_sec - start_time.tv_sec);
+       usecs *= 1000000.0;
+       usecs += (double) (end_time.tv_usec - start_time.tv_usec);
+       total += bytes;
+
+       cpu_cycles = end_cycles - start_cycles;
+       cpu_pct = 100.0 -
+               cpu_cycles / cycles_per_sec / ( usecs / 1000000.0 ) * 100.0;
+
+       *rate = (double) bytes / usecs / (1024*1024) * 1000000;
+
+       printf("%.2fMB/s (%.1fMB total, %.2f%% CPU)\n", *rate,
+               (double) total / (1024*1024),
+               cpu_pct
+       );
+
+       return cpu_pct;
+}
+
+static void calibrate_loops(void)
+{
+       long long l0, l1;
+       int i;
+
+       cycles_per_sec = 0;
+       printf("calibrating cycles: "); fflush(stdout);
+
+       /*
+        * Make sure we start on a precise timer IRQ boundary:
+        */
+       usleep(50000);
+
+       for (i = 0; i < 10; i++) {
+               sched_yield();
+               l0 = *cycles;
+               usleep(200000);
+               l1 = *cycles;
+               cycles_per_sec = max(cycles_per_sec, l1-l0);
+       }
+       cycles_per_sec *= 5;
+
+       printf("%Ld cycles/sec\n", cycles_per_sec);
+}
+
+static int child(struct sockaddr *addr, int len)
+{
+       static char buffer[BUFSIZE];
+       int sk;
+       double c1, c2, c3;
+       int fd;
+       struct sockaddr_in s_to;
+       struct hostent *hp;
+       double r1, r2, r3, r4, r5;
+       int i, pipefd[2];
+       loff_t off = 0;
+
+       r1 = r2 = r3 = r4 = r5 = 0;
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (!sk)
+               return error("socket");
+       hp = gethostbyname (TARGET_HOSTNAME);
+       BUG_ON(!hp);
+       bzero ((char *) &s_to, sizeof (s_to));
+       bcopy ((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
+       s_to.sin_family = hp->h_addrtype;
+       s_to.sin_port = htons(1111);
+
+       calibrate_loops();
+
+       fprintf(stdout, "BUFSIZE = %d\n", BUFSIZE);
+       fflush(stdout);
+
+       if (getenv("EMPTY")) {
+               if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+                       return error("connect");
+
+               start_timing("Empty buffer");
+               for (i = 0; i < NR; i++)
+                       write(sk, buffer, BUFSIZE);
+               end_timing(NR*BUFSIZE, &r1);
+
+               fd = open("largefile", O_RDONLY);
+               if (fd < 0)
+                       return error("largefile");
+
+               start_timing("Read/write loop");
+               for (i = 0; i < NR; i++) {
+                       if (read(fd, buffer, BUFSIZE) != BUFSIZE)
+                               return error("largefile read");
+                       write(sk, buffer, BUFSIZE);
+               }
+               end_timing(NR*BUFSIZE, &r2);
+               close(fd);
+               close(sk);
+       }
+
+       if (getenv("SF")) {
+               start_timing("sendfile");
+sendfile_again:
+               sk = socket(PF_INET, SOCK_STREAM, 0);
+               if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+                       return error("connect");
+
+               fd = open("largefile", O_RDONLY);
+               if (fd < 0)
+                       return error("largefile");
+
+               i = NR*BUFSIZE;
+               do {
+                       int ret = sendfile(sk, fd, NULL, i);
+                       i -= ret;
+               } while (i);
+
+               close(fd);
+               close(sk);
+               if (--sendfile_loops)
+                       goto sendfile_again;
+               c1 = end_timing(NR*BUFSIZE*SENDFILE_LOOPS, &r3);
+       } else
+               c1 = 0;
+
+       if (getenv("SPLICE_PIPE")) {
+               start_timing("splice-pipe");
+splice_pipe_again:
+               sk = socket(PF_INET, SOCK_STREAM, 0);
+               if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+                       return error("connect");
+
+               fd = open("largefile", O_RDONLY);
+               if (fd < 0)
+                       return error("largefile");
+               if (pipe(pipefd) < 0)
+                       return error("pipe");
+
+               i = NR*BUFSIZE;
+               off = 0;
+               do {
+                       int ret = splice(fd, &off, pipefd[1], NULL, min(i, BUFSIZE), SPLICE_F_NONBLOCK);
+                       if (ret <= 0)
+                               return error("splice-pipe-in");
+                       i -= ret;
+                       while (ret > 0) {
+                               int flags = i ? SPLICE_F_MORE : 0;
+                               int written = splice(pipefd[0], NULL, sk, NULL, ret, flags);
+                               if (written <= 0)
+                                       return error("splice-pipe-out");
+                               ret -= written;
+                       }
+               } while (i);
+
+               close(fd);
+               close(sk);
+               close(pipefd[0]);
+               close(pipefd[1]);
+               if (--splice_pipe_loops)
+                       goto splice_pipe_again;
+               c2 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r4);
+       } else
+               c2 = 0;
+
+       if (getenv("SPLICE")) {
+               start_timing("splice");
+splice_again:
+               sk = socket(PF_INET, SOCK_STREAM, 0);
+               if (connect(sk, (struct sockaddr *)&s_to, len) < 0)
+                       return error("connect");
+
+               fd = open("largefile", O_RDONLY);
+               if (fd < 0)
+                       return error("largefile");
+
+               i = NR*BUFSIZE;
+               off = 0;
+               do {
+                       int flags = BUFSIZE < i ? SPLICE_F_MORE : 0;
+                       int ret;
+
+                       ret = splice(fd, &off, sk, NULL, min(i, BUFSIZE), flags);
+
+                       if (ret <= 0)
+                               return error("splice");
+                       i -= ret;
+               } while (i);
+
+               close(fd);
+               close(sk);
+               if (--splice_loops)
+                       goto splice_again;
+               c3 = end_timing(NR*BUFSIZE*SPLICE_LOOPS, &r5);
+       } else
+               c3 = 0;
+
+       /*
+        * c1/r3 - sendfile
+        * c2/r4 - splice-pipe
+        * c3/r5 - splice
+        */
+
+       if (c1 && c2)
+               printf("sendfile is %.2f%% more efficient than splice-pipe.\n",
+                       (c2 - c1) / c1 * 100.0 );
+       if (c1 && c3)
+               printf("sendfile is %.2f%% more efficient than splice.\n",
+                       (c3 - c1) / c1 * 100.0 );
+       if (c2 && c3)
+               printf("splice is %.2f%% more efficient splice-pipe.\n",
+                       (c2 - c3) / c3 * 100.0 );
+       if (r3 && r4)
+               printf("sendfile is %.2f%% faster than splice-pipe.\n",
+                       (r3 - r4) / r4 * 100.0 );
+       if (r3 && r5)
+               printf("sendfile is %.2f%% faster than splice.\n",
+                       (r3 - r5) / r5 * 100.0 );
+       if (r4 && r5)
+               printf("splice is %.2f%% faster than splice-pipe.\n",
+                       (r5 - r4) / r4 * 100.0 );
+
+       return 0;
+}
+
+
+static void setup_shared_var(void)
+{
+       char zerobuff [4096] = { 0, };
+       int ret, fd;
+
+       fd = creat(".tmp_mmap", 0700);
+       BUG_ON(fd == -1);
+       close(fd);
+
+       fd = open(".tmp_mmap", O_RDWR|O_CREAT|O_TRUNC);
+       BUG_ON(fd == -1);
+       ret = write(fd, zerobuff, 4096);
+       BUG_ON(ret != 4096);
+
+       cycles = (void *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+       BUG_ON(cycles == (void *)-1);
+
+       close(fd);
+}
+
+#define SCHED_BATCH 3
+
+#ifdef __ia64__
+# define rdtscll(val)                                  \
+do {                                                   \
+       val = *__mm_clock_dev;                          \
+} while (0)
+#elif defined(__i386__)
+# define rdtscll(val)                                  \
+do {                                                   \
+       __asm__ __volatile__("rdtsc" : "=A" (val));     \
+} while (0)
+#else
+# define rdtscll(val) \
+       do { (val) = 0LL; } while (0)
+#endif
+
+/*
+ * Keep lowprio looping - to meausure the number of idle cycles
+ * available. It's tricky: we do a series of RDTSC calls, and
+ * if the delay to the last measurement was less than 500 cycles,
+ * we conclude that only this loop ran.
+ */
+static void lowprio_cycle_soak_loop(void)
+{
+        struct sched_param p = { sched_priority: 0 };
+       unsigned long long t0, t1, delta;
+
+       /*
+        * We are a nice +19 SCHED_BATCH task:
+        */
+       BUG_ON(sched_setscheduler(0, SCHED_BATCH, &p) != 0);
+       nice(40);
+
+       rdtscll(t0);
+       while (cycles >= 0) {
+               rdtscll(t1);
+               delta = t1-t0;
+               if (delta < 500)
+                       *cycles += delta;
+               t0 = t1;
+       }
+}
+
+int main(int argc, char **argv)
+{
+       unsigned int sk, len;
+       struct sockaddr addr;
+       pid_t pid;
+
+       setup_shared_var();
+
+       signal(SIGCHLD, SIG_IGN);
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0) {
+               perror("socket");
+               exit(1);
+       }
+       if (listen(sk, 1) < 0) {
+               perror("listen");
+               exit(1);
+       }
+       len = sizeof(addr);
+       if (getsockname(sk, &addr, &len) < 0) {
+               perror("getsockname");
+               exit(1);
+       }
+       pid = fork();
+       if (!pid) {
+               lowprio_cycle_soak_loop();
+               exit(0);
+       }
+       nice(-20);
+       child(&addr, len);
+       kill(pid, SIGHUP);
+       exit(0);
+}
diff --git a/splice-test4s.c b/splice-test4s.c
new file mode 100644 (file)
index 0000000..925df5d
--- /dev/null
@@ -0,0 +1,81 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sendfile.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static int error(const char *n)
+{
+       perror(n);
+       return -1;
+}
+
+static int parent(int listen_sk)
+{
+       unsigned long received;
+       struct sockaddr addr;
+       unsigned int len = sizeof(addr);
+       int sk;
+
+again:
+       sk = accept(listen_sk, &addr, &len);
+       if (sk < 0)
+               return error("accept");
+
+       /* read forever */
+       received = 0;
+       for (;;) {
+               int ret = recv(sk, NULL, 128*1024*1024, MSG_TRUNC);
+               if (ret > 0) {
+                       received += ret;
+                       continue;
+               }
+               if (!ret)
+                       break;
+               if (errno == EAGAIN || errno == EINTR)
+                       continue;
+               break;
+       }
+
+       printf("Received %f MB of data\n", (double) received / (1024*1024));
+       close(sk);
+       goto again;
+}
+
+int main(int argc, char **argv)
+{
+       unsigned int sk, len;
+       struct sockaddr addr;
+       struct sockaddr_in saddr_in;
+
+       signal(SIGCHLD, SIG_IGN);
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0) {
+               perror("socket");
+               exit(1);
+       }
+       saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
+       saddr_in.sin_port = htons(1111);
+
+       if (bind(sk, (struct sockaddr*)&saddr_in, sizeof(saddr_in)) < 0) {
+                fprintf(stderr,"bind failed\n");
+                exit(1);
+        }
+
+       if (listen(sk, 1) < 0) {
+               perror("listen");
+               exit(1);
+       }
+       len = sizeof(addr);
+       if (getsockname(sk, &addr, &len) < 0) {
+               perror("getsockname");
+               exit(1);
+       }
+       return parent(sk);
+}
diff --git a/splice.h b/splice.h
new file mode 100644 (file)
index 0000000..0cdd1ca
--- /dev/null
+++ b/splice.h
@@ -0,0 +1,60 @@
+#ifndef SPLICE_H
+#define SPLICE_H
+
+#if defined(__i386__)
+#define __NR_splice    313
+#define __NR_tee       315
+#elif defined(__x86_64__)
+#define __NR_splice    275
+#define __NR_tee       276
+#elif defined(__powerpc__) || defined(__powerpc64__)
+#define __NR_splice    283
+#define __NR_tee       284
+#elif defined(__ia64__)
+#define __NR_splice    1297
+#define __NR_tee       1301
+#else
+#error unsupported arch
+#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 */
+                                /* from/to, of course */
+#define SPLICE_F_MORE  (0x04)  /* expect more data */
+
+static inline int splice(int fdin, loff_t *off_in, int fdout, loff_t *off_out,
+                        size_t len, unsigned long flags)
+{
+       return syscall(__NR_splice, fdin, off_in, fdout, off_out, len, flags);
+
+}
+
+static inline int tee(int fdin, int fdout, size_t len, unsigned int flags)
+{
+       return syscall(__NR_tee, fdin, fdout, len, flags);
+}
+
+#define SPLICE_SIZE    (64*1024)
+
+#define BUG_ON(c) assert(!(c))
+
+#define min(x,y) ({ \
+        typeof(x) _x = (x);     \
+        typeof(y) _y = (y);     \
+        (void) (&_x == &_y);            \
+        _x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+        typeof(x) _x = (x);     \
+        typeof(y) _y = (y);     \
+        (void) (&_x == &_y);            \
+        _x > _y ? _x : _y; })
+
+static inline int error(const char *n)
+{
+       perror(n);
+       return -1;
+}
+
+#endif