Add nettest/ tools
[splice.git] / nettest / xmit.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <netdb.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <signal.h>
8 #include <netinet/in.h>
9 #include <arpa/inet.h>
10 #include <string.h>
11 #include <sys/time.h>
12 #include <errno.h>
13 #include <sys/time.h>
14 #include <sys/resource.h>
15
16 #include "../splice.h"
17 #include "crc32.h"
18 #include "msg.h"
19
20 static void **buffers;
21 static int cur_buf, nr_bufs;
22
23 static unsigned int msg_size = 4096;
24 static int use_splice = 1;
25 static unsigned long packets = -1;
26
27 static unsigned long seed = 0x9e37fffffffc0001UL;
28
29 unsigned long mtime_since(struct timeval *s, struct timeval *e)
30 {
31         long sec, usec, ret;
32
33         sec = e->tv_sec - s->tv_sec;
34         usec = e->tv_usec - s->tv_usec;
35         if (sec > 0 && usec < 0) {
36                 sec--;
37                 usec += 1000000;
38         }
39
40         sec *= 1000UL;
41         usec /= 1000UL;
42         ret = sec + usec;
43
44         /*
45          * time warp bug on some kernels?
46          */
47         if (ret < 0)
48                 ret = 0;
49
50         return ret;
51 }
52
53 unsigned long mtime_since_now(struct timeval *s)
54 {
55         struct timeval t;
56
57         gettimeofday(&t, NULL);
58         return mtime_since(s, &t);
59 }
60
61 static int usage(char *name)
62 {
63         fprintf(stderr, "%s: [-s(ize)] [-p(ackets to send)] [-n(ormal send())] target port\n", name);
64         return 1;
65 }
66
67 static int parse_options(int argc, char *argv[])
68 {
69         int c, index = 1;
70
71         while ((c = getopt(argc, argv, "s:np:")) != -1) {
72                 switch (c) {
73                 case 's':
74                         msg_size = atoi(optarg);
75                         index++;
76                         break;
77                 case 'n':
78                         use_splice = 0;
79                         index++;
80                         break;
81                 case 'p':
82                         packets = atoi(optarg);
83                         index++;
84                         break;
85                 default:
86                         return -1;
87                 }
88         }
89
90         return index;
91 }
92
93 static void fill_buf(struct msg *m, unsigned int len)
94 {
95         void *p = m;
96         unsigned int left;
97         unsigned long *val;
98
99         m->msg_size = len;
100         len -= sizeof(*m);
101         p += sizeof(*m);
102
103         left = len;
104         val = p;
105         while (left) {
106                 if (left < sizeof(*val))
107                         break;
108                 *val = rand() * seed;
109                 val++;
110                 left -= sizeof(*val);
111         }
112
113         m->crc32 = crc32(p, len);
114 }
115
116 static int splice_out(int sockfd, int pipefd, unsigned int len)
117 {
118         while (len) {
119                 int ret = ssplice(pipefd, NULL, sockfd, NULL, len, 0);
120
121                 if (ret < 0)
122                         return error("splice to network");
123                 else if (!ret)
124                         break;
125
126                 len -= ret;
127         }
128
129         return len;
130 }
131
132 static int vmsplice_in(void *buf, int pipefd, unsigned int len)
133 {
134         struct iovec iov = {
135                 .iov_base = buf,
136                 .iov_len = len,
137         };
138
139         while (len) {
140                 int ret = svmsplice(pipefd, &iov, 1, 0);
141
142                 if (ret < 0)
143                         return error("vmsplice");
144                 else if (!ret)
145                         break;
146
147                 len -= ret;
148                 if (len) {
149                         iov.iov_base += ret;
150                         iov.iov_len -= ret;
151                 }
152         }
153
154         return len;
155 }
156
157 /*
158  * Keep four pipes of buffers, that should be enough to ensure that
159  * we don't reuse
160  */
161 static void setup_buffers(void)
162 {
163         int i;
164
165         nr_bufs = 4 * SPLICE_SIZE / msg_size;
166
167         buffers = malloc(sizeof(void *) * nr_bufs);
168
169         for (i = 0; i < nr_bufs; i++)
170                 posix_memalign(&buffers[i], 4096, msg_size);
171 }
172
173 static void free_buffers(void)
174 {
175         int i;
176
177         for (i = 0; i < nr_bufs; i++)
178                 free(buffers[i]);
179
180         free(buffers);
181 }
182
183 static int splice_send_loop(int fd)
184 {
185         struct msg *m = NULL;
186         int pipes[2];
187
188         if (pipe(pipes) < 0)
189                 return error("pipe");
190
191         setup_buffers();
192
193         while (packets--) {
194                 m = buffers[cur_buf];
195                 if (++cur_buf == nr_bufs)
196                         cur_buf = 0;
197
198                 /*
199                  * fill with random data and crc sum it
200                  */
201                 fill_buf(m, msg_size);
202
203                 /*
204                  * map data to our pipe
205                  */
206                 if (vmsplice_in(m, pipes[1], msg_size))
207                         break;
208
209                 /*
210                  * then transmit pipe to network
211                  */
212                 if (splice_out(fd, pipes[0], msg_size))
213                         break;
214         }
215
216         free_buffers();
217         close(pipes[0]);
218         close(pipes[1]);
219         return 0;
220 }
221
222 static int do_send(int fd, void *buf, unsigned int len)
223 {
224         while (len) {
225                 int ret = send(fd, buf, len, 0);
226
227                 if (ret < 0)
228                         return error("send");
229                 else if (!ret)
230                         break;
231
232                 len -= ret;
233                 buf += ret;
234         }
235
236         return len;
237 }
238
239 static int normal_send_loop(int fd)
240 {
241         struct msg *m;
242
243         m = malloc(msg_size);
244
245         while (packets--) {
246                 /*
247                  * fill with random data and crc sum it
248                  */
249                 fill_buf(m, msg_size);
250
251                 if (do_send(fd, m, msg_size))
252                         break;
253         }
254
255         free(m);
256         return 0;
257 }
258
259 static int send_loop(int fd)
260 {
261         struct rusage ru_s, ru_e;
262         unsigned long ut, st, rt;
263         struct timeval start;
264         int ret;
265
266         gettimeofday(&start, NULL);
267         getrusage(RUSAGE_SELF, &ru_s);
268
269         if (use_splice)
270                 ret = splice_send_loop(fd);
271         else
272                 ret = normal_send_loop(fd);
273
274         getrusage(RUSAGE_SELF, &ru_e);
275
276         ut = mtime_since(&ru_s.ru_utime, &ru_e.ru_utime);
277         st = mtime_since(&ru_s.ru_stime, &ru_e.ru_stime);
278         rt = mtime_since_now(&start);
279
280         printf("usr=%lu, sys=%lu, real=%lu\n", ut, st, rt);
281         fflush(stdout);
282         return ret;
283 }
284
285 int main(int argc, char *argv[])
286 {
287         struct sockaddr_in addr;
288         unsigned short port;
289         int fd, index;
290
291         if (argc < 3)
292                 return usage(argv[0]);
293
294         index = parse_options(argc, argv);
295         if (index == -1 || index + 1 > argc)
296                 return usage(argv[0]);
297
298         port = atoi(argv[index + 1]);
299
300         memset(&addr, 0, sizeof(addr));
301         addr.sin_family = AF_INET;
302         addr.sin_port = htons(port);
303
304         if (inet_aton(argv[index], &addr.sin_addr) != 1) {
305                 struct hostent *hent = gethostbyname(argv[index]);
306
307                 if (!hent)
308                         return error("gethostbyname");
309
310                 memcpy(&addr.sin_addr, hent->h_addr, 4);
311         }
312
313         printf("xmit: msg=%ukb, ", msg_size >> 10);
314         if (use_splice)
315                 printf("vmsplice() -> splice()\n");
316         else
317                 printf("send()\n");
318
319         printf("Connecting to %s/%d\n", argv[index], port);
320
321         fd = socket(AF_INET, SOCK_STREAM, 0);
322         if (fd < 0)
323                 return error("socket");
324
325         if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
326                 return error("connect");
327
328         return send_loop(fd);
329 }