From: Jens Axboe Date: Fri, 22 Jun 2007 12:54:34 +0000 (+0200) Subject: Add nettest/ tools X-Git-Url: https://git.kernel.dk/?p=splice.git;a=commitdiff_plain;h=211f748c3dfffacb1ac1e899a9a9e019d73d6432 Add nettest/ tools Signed-off-by: Jens Axboe --- diff --git a/.gitignore b/.gitignore index 5a45128..bd0150a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ splice-test4c splice-test4s vmsplice vmsplice2 +vmsplice-to-user splice-bench diff --git a/Makefile b/Makefile index c0c97e1..0cb0d69 100644 --- 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) + $(MAKE) -C nettest %.o: %.c $(CC) -o $*.o -c $(CFLAGS) $< @@ -13,6 +14,7 @@ depend: clean: -rm -f *.o $(PROGS) .depend + $(MAKE) -C nettest clean INSTALL = install prefix = /usr/local diff --git a/nettest/Makefile b/nettest/Makefile new file mode 100644 index 0000000..b7d6fc3 --- /dev/null +++ b/nettest/Makefile @@ -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 index 0000000..7ef52e8 --- /dev/null +++ b/nettest/README @@ -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 index 0000000..ba6cc06 --- /dev/null +++ b/nettest/crc32.c @@ -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 index 0000000..b7e5eee --- /dev/null +++ b/nettest/crc32.h @@ -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 index 0000000..01be07c --- /dev/null +++ b/nettest/fillfile.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#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 index 0000000..a5a8ba5 --- /dev/null +++ b/nettest/msg.h @@ -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 index 0000000..050c5e4 --- /dev/null +++ b/nettest/recv.c @@ -0,0 +1,376 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..aa4b051 --- /dev/null +++ b/nettest/sf.c @@ -0,0 +1,125 @@ +/* + * sendfile() a file + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..412fc3c --- /dev/null +++ b/nettest/xmit.c @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}