Add nettest/ tools
authorJens Axboe <jens.axboe@oracle.com>
Fri, 22 Jun 2007 12:54:34 +0000 (14:54 +0200)
committerJens Axboe <jens.axboe@oracle.com>
Fri, 22 Jun 2007 12:54:34 +0000 (14:54 +0200)
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
.gitignore
Makefile
nettest/Makefile [new file with mode: 0644]
nettest/README [new file with mode: 0644]
nettest/crc32.c [new file with mode: 0644]
nettest/crc32.h [new file with mode: 0644]
nettest/fillfile.c [new file with mode: 0644]
nettest/msg.h [new file with mode: 0644]
nettest/recv.c [new file with mode: 0644]
nettest/sf.c [new file with mode: 0644]
nettest/xmit.c [new file with mode: 0644]

index 5a45128fd7a3b06f8fc58ec24a13fee475267687..bd0150a0f3605557f3e8b1f2349985879241a1a7 100644 (file)
@@ -11,4 +11,5 @@ splice-test4c
 splice-test4s
 vmsplice
 vmsplice2
 splice-test4s
 vmsplice
 vmsplice2
+vmsplice-to-user
 splice-bench
 splice-bench
index c0c97e1c564f2352038bcd477b9ada6c37eb5968..0cb0d69aed97b985cec264f443de064ab5795680 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ PROGS   = ktee ktee-net splice-cp splice-in splice-out splice-tonet splice-fromnet
 MANS   = splice.2 tee.2 vmsplice.2
 
 all: depend $(PROGS)
 MANS   = splice.2 tee.2 vmsplice.2
 
 all: depend $(PROGS)
+       $(MAKE) -C nettest
 
 %.o: %.c
        $(CC) -o $*.o -c $(CFLAGS) $<
 
 %.o: %.c
        $(CC) -o $*.o -c $(CFLAGS) $<
@@ -13,6 +14,7 @@ depend:
 
 clean:
        -rm -f *.o $(PROGS) .depend
 
 clean:
        -rm -f *.o $(PROGS) .depend
+       $(MAKE) -C nettest clean
 
 INSTALL = install
 prefix = /usr/local
 
 INSTALL = install
 prefix = /usr/local
diff --git a/nettest/Makefile b/nettest/Makefile
new file mode 100644 (file)
index 0000000..b7d6fc3
--- /dev/null
@@ -0,0 +1,29 @@
+#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
diff --git a/nettest/README b/nettest/README
new file mode 100644 (file)
index 0000000..7ef52e8
--- /dev/null
@@ -0,0 +1,11 @@
+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.
+
diff --git a/nettest/crc32.c b/nettest/crc32.c
new file mode 100644 (file)
index 0000000..ba6cc06
--- /dev/null
@@ -0,0 +1,84 @@
+/* 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;
+}
diff --git a/nettest/crc32.h b/nettest/crc32.h
new file mode 100644 (file)
index 0000000..b7e5eee
--- /dev/null
@@ -0,0 +1,23 @@
+/* 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
diff --git a/nettest/fillfile.c b/nettest/fillfile.c
new file mode 100644 (file)
index 0000000..01be07c
--- /dev/null
@@ -0,0 +1,100 @@
+#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);
+}
diff --git a/nettest/msg.h b/nettest/msg.h
new file mode 100644 (file)
index 0000000..a5a8ba5
--- /dev/null
@@ -0,0 +1,9 @@
+#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
diff --git a/nettest/recv.c b/nettest/recv.c
new file mode 100644 (file)
index 0000000..050c5e4
--- /dev/null
@@ -0,0 +1,376 @@
+#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);
+}
diff --git a/nettest/sf.c b/nettest/sf.c
new file mode 100644 (file)
index 0000000..aa4b051
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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;
+}
diff --git a/nettest/xmit.c b/nettest/xmit.c
new file mode 100644 (file)
index 0000000..412fc3c
--- /dev/null
@@ -0,0 +1,329 @@
+#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);
+}