Add nettest/ tools
[splice.git] / nettest / xmit.c
CommitLineData
211f748c
JA
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
20static void **buffers;
21static int cur_buf, nr_bufs;
22
23static unsigned int msg_size = 4096;
24static int use_splice = 1;
25static unsigned long packets = -1;
26
27static unsigned long seed = 0x9e37fffffffc0001UL;
28
29unsigned 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
53unsigned 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
61static 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
67static 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
93static 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
116static 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
132static 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 */
161static 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
173static 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
183static 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
222static 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
239static 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
259static 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
285int 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}