splice-test4s
vmsplice
vmsplice2
+vmsplice-to-user
splice-bench
MANS = splice.2 tee.2 vmsplice.2
all: depend $(PROGS)
+ $(MAKE) -C nettest
%.o: %.c
$(CC) -o $*.o -c $(CFLAGS) $<
clean:
-rm -f *.o $(PROGS) .depend
+ $(MAKE) -C nettest clean
INSTALL = install
prefix = /usr/local
--- /dev/null
+#CC = /opt/intel/cce/9.1.045/bin/icc
+CC = gcc
+OPTFLAGS= -O2 -g $(EXTFLAGS)
+CFLAGS = -Wall -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $(OPTFLAGS)
+PROGS = xmit recv sf fillfile
+
+all: $(PROGS)
+
+xmit: crc32.o xmit.o
+ $(CC) $(CFLAGS) -o $@ $(filter %.o,$^)
+recv: crc32.o recv.o
+ $(CC) $(CFLAGS) -o $@ $(filter %.o,$^)
+
+sf: sf.o
+ $(CC) $(CFLAGS) -o $@ $(filter %.o,$^)
+
+fillfile: crc32.o fillfile.o
+ $(CC) $(CFLAGS) -o $@ $(filter %.o,$^)
+
+clean:
+ -rm -f *.o .depend cscope.out $(PROGS) core.* core
+depend:
+ @$(CC) -MM $(ALL_CFLAGS) *.c 1> .depend
+
+$(PROGS): depend
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
--- /dev/null
+Simple networking test stuff, to compare splice/vmsplice with
+send()/recv(). The basic setup is transmitting and receiving
+buffers of data with crc checksums attached. Speed and sys/usr
+stats are shown, so both performance and data integrity can
+be tested.
+
+The basic pair is xmit/recv, which talk to each other. It's
+important to use the same message size, or it wont work. sf can
+also talk to recv. To do that, you need to prepare a data file
+with fillfile first.
+
--- /dev/null
+/* crc32 -- calculate and POSIX.2 checksum
+ Copyright (C) 92, 1995-1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "crc32.h"
+
+static const unsigned long crctab[256] = {
+ 0x0,
+ 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B,
+ 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6,
+ 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
+ 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC,
+ 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F,
+ 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A,
+ 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
+ 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58,
+ 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033,
+ 0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE,
+ 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
+ 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4,
+ 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0,
+ 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5,
+ 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
+ 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07,
+ 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C,
+ 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1,
+ 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
+ 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B,
+ 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698,
+ 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D,
+ 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
+ 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F,
+ 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34,
+ 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80,
+ 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
+ 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A,
+ 0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629,
+ 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C,
+ 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
+ 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E,
+ 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65,
+ 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8,
+ 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
+ 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2,
+ 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71,
+ 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74,
+ 0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
+ 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21,
+ 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A,
+ 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087,
+ 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
+ 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D,
+ 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE,
+ 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB,
+ 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
+ 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09,
+ 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662,
+ 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF,
+ 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
+};
+
+unsigned long crc32(const void *buffer, unsigned long length)
+{
+ const unsigned char *cp = (const unsigned char *) buffer;
+ unsigned long crc = 0;
+
+ while (length--)
+ crc = (crc << 8) ^ crctab[((crc >> 24) ^ *(cp++)) & 0xFF];
+
+ return crc;
+}
--- /dev/null
+/* crc32 -- calculate and POSIX.2 checksum
+ Copyright (C) 92, 1995-1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+extern unsigned long crc32(const void * const, unsigned long);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "msg.h"
+#include "crc32.h"
+#include "../splice.h"
+
+static unsigned int msg_size = 4096;
+static unsigned int file_size = 128;
+static unsigned long seed = 0x9e37fffffffc0001UL;
+
+static int usage(const char *name)
+{
+ fprintf(stderr, "%s: [-s( msg size)] [-z(filesize (mb))] file\n", name);
+ return 1;
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int c, index = 1;
+
+ while ((c = getopt(argc, argv, "s:z:")) != -1) {
+ switch (c) {
+ case 's':
+ msg_size = atoi(optarg);
+ index++;
+ break;
+ case 'z':
+ file_size = atoi(optarg);
+ index++;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ printf("msg_size=%u, file_size=%umb\n", msg_size, file_size);
+ return index;
+}
+
+static void fill_buf(struct msg *m, unsigned int len)
+{
+ void *p = m;
+ unsigned int left;
+ unsigned long *val;
+
+ m->msg_size = len;
+ len -= sizeof(*m);
+ p += sizeof(*m);
+
+ left = len;
+ val = p;
+ while (left) {
+ if (left < sizeof(*val))
+ break;
+ *val = rand() * seed;
+ val++;
+ left -= sizeof(*val);
+ }
+
+ m->crc32 = crc32(p, len);
+}
+
+static int fill_file(int fd)
+{
+ struct msg *m = malloc(msg_size);
+ unsigned long long fs = (unsigned long long) file_size * 1024 * 1024ULL;
+
+ while (fs) {
+ if (fs < msg_size)
+ break;
+
+ fill_buf(m, msg_size);
+ write(fd, m, msg_size);
+ fs -= msg_size;
+ }
+
+ close(fd);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, index;
+
+ if (argc < 2)
+ return usage(argv[0]);
+
+ index = parse_options(argc, argv);
+ if (index == -1 || index + 1 > argc)
+ return usage(argv[0]);
+
+ fd = open(argv[index], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0)
+ return error("open output file");
+
+ return fill_file(fd);
+}
--- /dev/null
+#ifndef MSG_H
+#define MSG_H
+
+struct msg {
+ unsigned long msg_size; /* length of crc data */
+ unsigned long crc32; /* data checksum */
+}; /* real data follows this message */
+
+#endif
--- /dev/null
+#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 <errno.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "../splice.h"
+#include "crc32.h"
+#include "msg.h"
+
+static unsigned int msg_size = 4096;
+static int use_splice = 1;
+static int splice_move;
+
+static int usage(const char *name)
+{
+ fprintf(stderr, "%s: [-s(ize)] [-m(ove)] [-r(ecv)] port\n", name);
+ return 1;
+}
+
+unsigned long mtime_since(struct timeval *s, struct timeval *e)
+{
+ long sec, usec, ret;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = e->tv_usec - s->tv_usec;
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000UL;
+ usec /= 1000UL;
+ ret = sec + usec;
+
+ /*
+ * time warp bug on some kernels?
+ */
+ if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+unsigned long mtime_since_now(struct timeval *s)
+{
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ return mtime_since(s, &t);
+}
+
+static int get_connect(int fd, struct sockaddr_in *addr)
+{
+ socklen_t socklen = sizeof(*addr);
+ int ret, connfd;
+
+ fprintf(stderr, "Waiting for connect...\n");
+
+ do {
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+
+ ret = poll(&pfd, 1, -1);
+ if (ret < 0)
+ return error("poll");
+ else if (!ret)
+ continue;
+
+ connfd = accept(fd, (struct sockaddr *) addr, &socklen);
+ if (connfd < 0)
+ return error("accept");
+ break;
+ } while (1);
+
+ fprintf(stderr, "Got connect!\n");
+
+ return connfd;
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int c, index = 1;
+
+ while ((c = getopt(argc, argv, "s:mr")) != -1) {
+ switch (c) {
+ case 's':
+ msg_size = atoi(optarg);
+ index++;
+ break;
+ case 'm':
+ splice_move = 1;
+ index++;
+ break;
+ case 'r':
+ use_splice = 0;
+ index++;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return index;
+}
+
+static int verify_crc(struct msg *m)
+{
+ unsigned long crc;
+ void *data = m;
+
+ data += sizeof(*m);
+ crc = crc32(data, m->msg_size - sizeof(*m));
+
+ if (crc == m->crc32)
+ return 0;
+
+ fprintf(stderr, "crc error: got %lx, wanted %lx\n", crc, m->crc32);
+ return 1;
+}
+
+static int do_recv(int fd, void *buf, unsigned int len)
+{
+ while (len) {
+ int ret = recv(fd, buf, len, MSG_WAITALL);
+
+ if (ret < 0)
+ return error("recv");
+ else if (!ret)
+ break;
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return len;
+}
+
+static int normal_recv_loop(int fd)
+{
+ struct msg *m;
+
+ m = malloc(msg_size);
+
+ while (1) {
+ if (do_recv(fd, m, msg_size))
+ break;
+
+ if (m->msg_size != msg_size) {
+ fprintf(stderr, "Bad packet length: wanted %u, got %lu\n", msg_size, m->msg_size);
+ break;
+ }
+
+ /*
+ * now verify data
+ */
+ if (verify_crc(m))
+ break;
+ }
+
+ free(m);
+ return 0;
+}
+
+static int splice_in(int sockfd, int pipefd, unsigned int size)
+{
+ while (size) {
+ int ret = ssplice(sockfd, NULL, pipefd, NULL, size, 0);
+
+ if (ret < 0)
+ return error("splice from net");
+ else if (!ret)
+ break;
+
+ size -= ret;
+ }
+
+ if (size)
+ fprintf(stderr, "splice: %u resid\n", size);
+
+ return size;
+}
+
+static int vmsplice_unmap(int pipefd, void *buf, unsigned int len)
+{
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = len,
+ };
+
+ if (svmsplice(pipefd, &iov, 1, SPLICE_F_UNMAP) < 0)
+ return error("vmsplice unmap");
+
+ return 0;
+}
+
+static int vmsplice_out(void **buf, int pipefd, unsigned int len)
+{
+ struct iovec iov = {
+ .iov_base = *buf,
+ .iov_len = len,
+ };
+ int ret, flags = 0;
+
+ if (splice_move)
+ flags |= SPLICE_F_MOVE;
+
+ while (len) {
+ ret = svmsplice(pipefd, &iov, 1, flags);
+ if (ret < 0)
+ return error("vmsplice");
+ else if (!ret)
+ break;
+
+ *buf = iov.iov_base;
+
+ len -= ret;
+ if (len) {
+ if (splice_move)
+ break;
+ iov.iov_len -= ret;
+ iov.iov_base += ret;
+ }
+ }
+
+ if (len)
+ fprintf(stderr, "vmsplice: %u resid\n", len);
+
+ return len;
+}
+
+static int splice_recv_loop(int fd)
+{
+ struct msg *m;
+ void *buf;
+ int pipes[2];
+
+ if (pipe(pipes) < 0)
+ return error("pipe");
+
+ if (!splice_move)
+ m = malloc(msg_size);
+ else
+ m = NULL;
+
+ while (1) {
+ /*
+ * fill pipe with network data
+ */
+ if (splice_in(fd, pipes[1], msg_size))
+ break;
+
+ /*
+ * move data to our address space
+ */
+ if (!splice_move)
+ buf = m;
+ else
+ buf = NULL;
+
+ if (vmsplice_out(&buf, pipes[0], msg_size))
+ break;
+
+ m = buf;
+
+ if (m->msg_size != msg_size) {
+ fprintf(stderr, "Bad packet length: wanted %u, got %lu\n", msg_size, m->msg_size);
+ break;
+ }
+
+ /*
+ * now verify data
+ */
+ if (verify_crc(m))
+ break;
+
+ if (splice_move && vmsplice_unmap(pipes[0], buf, msg_size))
+ break;
+ }
+
+ if (!splice_move)
+ free(m);
+
+ close(pipes[0]);
+ close(pipes[1]);
+ return 0;
+}
+
+static int recv_loop(int fd)
+{
+ struct rusage ru_s, ru_e;
+ struct timeval start;
+ unsigned long ut, st, rt;
+ int ret;
+
+ gettimeofday(&start, NULL);
+ getrusage(RUSAGE_SELF, &ru_s);
+
+ if (use_splice)
+ ret = splice_recv_loop(fd);
+ else
+ ret = normal_recv_loop(fd);
+
+ getrusage(RUSAGE_SELF, &ru_e);
+
+ ut = mtime_since(&ru_s.ru_utime, &ru_e.ru_utime);
+ st = mtime_since(&ru_s.ru_stime, &ru_e.ru_stime);
+ rt = mtime_since_now(&start);
+
+ printf("usr=%lu, sys=%lu, real=%lu\n", ut, st, rt);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr;
+ unsigned short port;
+ int connfd, fd, opt, index;
+
+ if (argc < 2)
+ return usage(argv[0]);
+
+ index = parse_options(argc, argv);
+ if (index == -1 || index + 1 > argc)
+ return usage(argv[0]);
+
+ printf("recv: msg=%ukb, ", msg_size >> 10);
+ if (use_splice) {
+ printf("splice() ");
+ if (splice_move)
+ printf("zero map ");
+ else
+ printf("addr map ");
+ } else
+ printf("recv()");
+ printf("\n");
+
+ port = atoi(argv[index]);
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd < 0)
+ return error("socket");
+
+ opt = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
+ return error("setsockopt");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ return error("bind");
+ if (listen(fd, 1) < 0)
+ return error("listen");
+
+ connfd = get_connect(fd, &addr);
+ if (connfd < 0)
+ return connfd;
+
+ return recv_loop(connfd);
+}
--- /dev/null
+/*
+ * sendfile() a file
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+static int loop;
+
+static inline int error(const char *n)
+{
+ perror(n);
+ return -1;
+}
+
+static int usage(char *name)
+{
+ fprintf(stderr, "%s: file target port\n", name);
+ return 1;
+}
+
+static int do_sf(int filefd, int sockfd)
+{
+ struct stat sb;
+ int ret;
+
+ if (fstat(filefd, &sb) < 0)
+ return error("stat input file");
+ if (!sb.st_size)
+ return -1;
+
+ if (lseek(filefd, 0, SEEK_SET) < 0)
+ return error("lseek");
+
+ while (sb.st_size) {
+ ret = sendfile(sockfd, filefd, NULL, sb.st_size);
+ if (ret < 0)
+ return error("sendfile");
+ else if (!ret)
+ break;
+
+ sb.st_size -= ret;
+ }
+
+ return 0;
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int c, index = 1;
+
+ while ((c = getopt(argc, argv, "l")) != -1) {
+ switch (c) {
+ case 'l':
+ loop = 1;
+ index++;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return index;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr;
+ unsigned short port;
+ int fd, filefd, index;
+
+ if (argc < 4)
+ return usage(argv[0]);
+
+ index = parse_options(argc, argv);
+ if (index == -1 || index + 1 > argc)
+ return usage(argv[0]);
+
+ filefd = open(argv[index], O_RDONLY);
+ if (filefd < 0)
+ return error("open input file");
+
+ port = atoi(argv[index + 2]);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if (inet_aton(argv[index + 1], &addr.sin_addr) != 1) {
+ struct hostent *hent = gethostbyname(argv[index + 1]);
+
+ if (!hent)
+ return error("gethostbyname");
+
+ memcpy(&addr.sin_addr, hent->h_addr, 4);
+ }
+
+ printf("Connecting to %s/%d\n", argv[index + 1], port);
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return error("socket");
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ return error("connect");
+
+ do {
+ if (do_sf(filefd, fd))
+ break;
+ } while (loop);
+
+ close(fd);
+ close(filefd);
+ return 0;
+}
--- /dev/null
+#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 <errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "../splice.h"
+#include "crc32.h"
+#include "msg.h"
+
+static void **buffers;
+static int cur_buf, nr_bufs;
+
+static unsigned int msg_size = 4096;
+static int use_splice = 1;
+static unsigned long packets = -1;
+
+static unsigned long seed = 0x9e37fffffffc0001UL;
+
+unsigned long mtime_since(struct timeval *s, struct timeval *e)
+{
+ long sec, usec, ret;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = e->tv_usec - s->tv_usec;
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000UL;
+ usec /= 1000UL;
+ ret = sec + usec;
+
+ /*
+ * time warp bug on some kernels?
+ */
+ if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+unsigned long mtime_since_now(struct timeval *s)
+{
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ return mtime_since(s, &t);
+}
+
+static int usage(char *name)
+{
+ fprintf(stderr, "%s: [-s(ize)] [-p(ackets to send)] [-n(ormal send())] target port\n", name);
+ return 1;
+}
+
+static int parse_options(int argc, char *argv[])
+{
+ int c, index = 1;
+
+ while ((c = getopt(argc, argv, "s:np:")) != -1) {
+ switch (c) {
+ case 's':
+ msg_size = atoi(optarg);
+ index++;
+ break;
+ case 'n':
+ use_splice = 0;
+ index++;
+ break;
+ case 'p':
+ packets = atoi(optarg);
+ index++;
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return index;
+}
+
+static void fill_buf(struct msg *m, unsigned int len)
+{
+ void *p = m;
+ unsigned int left;
+ unsigned long *val;
+
+ m->msg_size = len;
+ len -= sizeof(*m);
+ p += sizeof(*m);
+
+ left = len;
+ val = p;
+ while (left) {
+ if (left < sizeof(*val))
+ break;
+ *val = rand() * seed;
+ val++;
+ left -= sizeof(*val);
+ }
+
+ m->crc32 = crc32(p, len);
+}
+
+static int splice_out(int sockfd, int pipefd, unsigned int len)
+{
+ while (len) {
+ int ret = ssplice(pipefd, NULL, sockfd, NULL, len, 0);
+
+ if (ret < 0)
+ return error("splice to network");
+ else if (!ret)
+ break;
+
+ len -= ret;
+ }
+
+ return len;
+}
+
+static int vmsplice_in(void *buf, int pipefd, unsigned int len)
+{
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = len,
+ };
+
+ while (len) {
+ int ret = svmsplice(pipefd, &iov, 1, 0);
+
+ if (ret < 0)
+ return error("vmsplice");
+ else if (!ret)
+ break;
+
+ len -= ret;
+ if (len) {
+ iov.iov_base += ret;
+ iov.iov_len -= ret;
+ }
+ }
+
+ return len;
+}
+
+/*
+ * Keep four pipes of buffers, that should be enough to ensure that
+ * we don't reuse
+ */
+static void setup_buffers(void)
+{
+ int i;
+
+ nr_bufs = 4 * SPLICE_SIZE / msg_size;
+
+ buffers = malloc(sizeof(void *) * nr_bufs);
+
+ for (i = 0; i < nr_bufs; i++)
+ posix_memalign(&buffers[i], 4096, msg_size);
+}
+
+static void free_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < nr_bufs; i++)
+ free(buffers[i]);
+
+ free(buffers);
+}
+
+static int splice_send_loop(int fd)
+{
+ struct msg *m = NULL;
+ int pipes[2];
+
+ if (pipe(pipes) < 0)
+ return error("pipe");
+
+ setup_buffers();
+
+ while (packets--) {
+ m = buffers[cur_buf];
+ if (++cur_buf == nr_bufs)
+ cur_buf = 0;
+
+ /*
+ * fill with random data and crc sum it
+ */
+ fill_buf(m, msg_size);
+
+ /*
+ * map data to our pipe
+ */
+ if (vmsplice_in(m, pipes[1], msg_size))
+ break;
+
+ /*
+ * then transmit pipe to network
+ */
+ if (splice_out(fd, pipes[0], msg_size))
+ break;
+ }
+
+ free_buffers();
+ close(pipes[0]);
+ close(pipes[1]);
+ return 0;
+}
+
+static int do_send(int fd, void *buf, unsigned int len)
+{
+ while (len) {
+ int ret = send(fd, buf, len, 0);
+
+ if (ret < 0)
+ return error("send");
+ else if (!ret)
+ break;
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return len;
+}
+
+static int normal_send_loop(int fd)
+{
+ struct msg *m;
+
+ m = malloc(msg_size);
+
+ while (packets--) {
+ /*
+ * fill with random data and crc sum it
+ */
+ fill_buf(m, msg_size);
+
+ if (do_send(fd, m, msg_size))
+ break;
+ }
+
+ free(m);
+ return 0;
+}
+
+static int send_loop(int fd)
+{
+ struct rusage ru_s, ru_e;
+ unsigned long ut, st, rt;
+ struct timeval start;
+ int ret;
+
+ gettimeofday(&start, NULL);
+ getrusage(RUSAGE_SELF, &ru_s);
+
+ if (use_splice)
+ ret = splice_send_loop(fd);
+ else
+ ret = normal_send_loop(fd);
+
+ getrusage(RUSAGE_SELF, &ru_e);
+
+ ut = mtime_since(&ru_s.ru_utime, &ru_e.ru_utime);
+ st = mtime_since(&ru_s.ru_stime, &ru_e.ru_stime);
+ rt = mtime_since_now(&start);
+
+ printf("usr=%lu, sys=%lu, real=%lu\n", ut, st, rt);
+ fflush(stdout);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr;
+ unsigned short port;
+ int fd, index;
+
+ if (argc < 3)
+ return usage(argv[0]);
+
+ index = parse_options(argc, argv);
+ if (index == -1 || index + 1 > argc)
+ return usage(argv[0]);
+
+ port = atoi(argv[index + 1]);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if (inet_aton(argv[index], &addr.sin_addr) != 1) {
+ struct hostent *hent = gethostbyname(argv[index]);
+
+ if (!hent)
+ return error("gethostbyname");
+
+ memcpy(&addr.sin_addr, hent->h_addr, 4);
+ }
+
+ printf("xmit: msg=%ukb, ", msg_size >> 10);
+ if (use_splice)
+ printf("vmsplice() -> splice()\n");
+ else
+ printf("send()\n");
+
+ printf("Connecting to %s/%d\n", argv[index], port);
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return error("socket");
+
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ return error("connect");
+
+ return send_loop(fd);
+}