]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-benchmark.c
vlog: Rename the currently used term 'facility' as 'destination'.
[mirror_ovs.git] / utilities / ovs-benchmark.c
1 /*
2 * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18
19 #include <errno.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <poll.h>
23 #include <stdlib.h>
24 #include <sys/socket.h>
25 #include <sys/time.h>
26 #include <stddef.h>
27 #include <unistd.h>
28
29 #include "command-line.h"
30 #include "poll-loop.h"
31 #include "socket-util.h"
32 #include "timeval.h"
33 #include "util.h"
34 #include "openvswitch/vlog.h"
35
36 #define DEFAULT_PORT 6630
37
38 #define MAX_SOCKETS 65535
39 static int n_batches = 1;
40 static int n_sockets = 100;
41
42 static struct in_addr local_addr;
43 static unsigned short int local_min_port, local_max_port;
44
45 static struct in_addr remote_addr;
46 static unsigned short int remote_min_port, remote_max_port;
47
48 static double max_rate;
49
50 static double timeout;
51
52 static const struct command *get_all_commands(void);
53
54 static void parse_options(int argc, char *argv[]);
55 static void usage(void);
56
57 static int
58 do_poll(struct pollfd *fds, int nfds, int timeout)
59 {
60 int retval;
61 #ifndef _WIN32
62 do {
63 retval = poll(fds, nfds, timeout);
64 } while (retval < 0 && errno == EINTR);
65 #else
66 retval = WSAPoll(fds, nfds, timeout);
67 #endif
68 return retval;
69 }
70
71 static long long int
72 time_in_msec(void)
73 {
74 struct timeval tv;
75
76 xgettimeofday(&tv);
77
78 return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
79 }
80
81 int
82 main(int argc, char *argv[])
83 {
84 set_program_name(argv[0]);
85 vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_EMER);
86 parse_options(argc, argv);
87 run_command(argc - optind, argv + optind, get_all_commands());
88 return 0;
89 }
90
91 static void
92 parse_target(const char *s_, struct in_addr *addr,
93 unsigned short int *min, unsigned short int *max)
94 {
95 char *s = xstrdup(s_);
96 char *colon;
97 int error;
98
99 colon = strchr(s, ':');
100 if (colon) {
101 *colon = '\0';
102 }
103
104 if (*s != '\0') {
105 error = lookup_hostname(s, addr);
106 if (error) {
107 ovs_fatal(error, "failed to look up IP address for \"%s\"", s_);
108 }
109 } else {
110 addr->s_addr = htonl(INADDR_ANY);
111 }
112
113 *min = *max = 0;
114 if (colon && colon[1] != '\0') {
115 const char *ports = colon + 1;
116 if (ovs_scan(ports, "%hu-%hu", min, max)) {
117 if (*min > *max) {
118 ovs_fatal(0, "%s: minimum is greater than maximum", s_);
119 }
120 } else if (ovs_scan(ports, "%hu", min)) {
121 *max = *min;
122 } else {
123 ovs_fatal(0, "%s: number or range expected", s_);
124 }
125 }
126
127 free(s);
128 }
129
130 static void
131 parse_options(int argc, char *argv[])
132 {
133 static const struct option long_options[] = {
134 {"local", required_argument, NULL, 'l'},
135 {"remote", required_argument, NULL, 'r'},
136 {"batches", required_argument, NULL, 'b'},
137 {"sockets", required_argument, NULL, 's'},
138 {"max-rate", required_argument, NULL, 'c'},
139 {"timeout", required_argument, NULL, 'T'},
140 {"help", no_argument, NULL, 'h'},
141 {"version", no_argument, NULL, 'V'},
142 {NULL, 0, NULL, 0},
143 };
144 char *short_options = long_options_to_short_options(long_options);
145
146 local_addr.s_addr = htonl(INADDR_ANY);
147 local_min_port = local_max_port = 0;
148
149 remote_addr.s_addr = htonl(0);
150 remote_min_port = remote_max_port = 0;
151
152 for (;;) {
153 int c;
154
155 c = getopt_long(argc, argv, short_options, long_options, NULL);
156 if (c == -1) {
157 break;
158 }
159
160 switch (c) {
161 case 'l':
162 parse_target(optarg,
163 &local_addr, &local_min_port, &local_max_port);
164 break;
165
166 case 'r':
167 parse_target(optarg,
168 &remote_addr, &remote_min_port, &remote_max_port);
169 if (remote_addr.s_addr == htonl(INADDR_ANY)) {
170 ovs_fatal(0, "remote IP address is required");
171 }
172 break;
173
174 case 'b':
175 n_batches = atoi(optarg);
176 if (n_batches < 0) {
177 ovs_fatal(0, "--batches or -b argument must be at least 1");
178 }
179 break;
180
181 case 's':
182 n_sockets = atoi(optarg);
183 if (n_sockets < 1 || n_sockets > MAX_SOCKETS) {
184 ovs_fatal(0, "--sockets or -s argument must be between 1 "
185 "and %d (inclusive)", MAX_SOCKETS);
186 }
187 break;
188
189 case 'c':
190 max_rate = atof(optarg);
191 if (max_rate <= 0.0) {
192 ovs_fatal(0, "--max-rate or -c argument must be positive");
193 }
194 break;
195
196 case 'T':
197 timeout = atoi(optarg);
198 if (!timeout) {
199 ovs_fatal(0, "-T or --timeout argument must be positive");
200 }
201 break;
202
203 case 'h':
204 usage();
205
206 case 'V':
207 ovs_print_version(0, 0);
208 exit(EXIT_SUCCESS);
209
210 case '?':
211 exit(EXIT_FAILURE);
212
213 default:
214 abort();
215 }
216 }
217 free(short_options);
218 }
219
220 static void
221 usage(void)
222 {
223 printf("\
224 %s: Open vSwitch flow setup benchmark utility\n\
225 usage: %s [OPTIONS] COMMAND [ARG...]\n\
226 latency connect many times all at once\n\
227 rate measure sustained flow setup rate\n\
228 listen accept TCP connections\n\
229 help display this help message\n\
230 \n\
231 Command options:\n\
232 -l, --local [IP][:PORTS] use local IP and range of PORTS\n\
233 -r, --remote IP[:PORTS] connect to remote IP and PORTS\n\
234 -s, --sockets N number of sockets for \"rate\" or \"latency\"\n\
235 -b, --batches N number of connection batches for \"latency\"\n\
236 -c, --max-rate NPERSEC connection rate limit for \"rate\"\n\
237 -T, --timeout MAXSECS max number of seconds to run for \"rate\"\n\
238 \n\
239 Other options:\n\
240 -h, --help display this help message\n\
241 -V, --version display version information\n",
242 program_name, program_name);
243 exit(EXIT_SUCCESS);
244 }
245
246 static void
247 cmd_listen(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
248 {
249 struct pollfd *fds;
250 int n_fds;
251 int port;
252 int i;
253
254 if (!local_min_port && !local_max_port) {
255 local_min_port = local_max_port = DEFAULT_PORT;
256 }
257 fds = xmalloc((1 + local_max_port - local_min_port) * sizeof *fds);
258 n_fds = 0;
259 for (port = local_min_port; port <= local_max_port; port++) {
260 struct sockaddr_in sin;
261 unsigned int yes = 1;
262 int error;
263 int fd;
264
265 /* Create socket, set SO_REUSEADDR. */
266 fd = socket(AF_INET, SOCK_STREAM, 0);
267 if (fd < 0) {
268 ovs_fatal(errno, "failed to create socket");
269 }
270 error = set_nonblocking(fd);
271 if (error) {
272 ovs_fatal(error, "failed to set non-blocking mode");
273 }
274 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
275 ovs_fatal(errno, "setsockopt(SO_REUSEADDR) failed");
276 }
277
278 /* Bind. */
279 sin.sin_family = AF_INET;
280 sin.sin_addr = remote_addr;
281 sin.sin_port = htons(port);
282 if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
283 ovs_fatal(errno, "bind failed");
284 }
285
286 /* Listen. */
287 if (listen(fd, 10000) < 0) {
288 ovs_fatal(errno, "listen failed");
289 }
290
291 fds[n_fds].fd = fd;
292 fds[n_fds].events = POLLIN;
293 n_fds++;
294 }
295
296 for (;;) {
297 int retval;
298
299 retval = do_poll(fds, n_fds, -1);
300 if (retval < 0) {
301 ovs_fatal(errno, "poll failed");
302 }
303
304 for (i = 0; i < n_fds; i++) {
305 if (fds[i].revents & POLLIN) {
306 int newfd;
307
308 do {
309 newfd = accept(fds[i].fd, NULL, NULL);
310 } while (newfd < 0 && errno == EINTR);
311
312 if (newfd >= 0) {
313 close(newfd);
314 } else if (errno != EAGAIN) {
315 ovs_fatal(errno, "accept failed");
316 }
317 }
318 }
319 }
320 }
321
322 /* Increments '*value' within the range 'min...max' inclusive. Returns true
323 * if '*value' wraps around to 'min', otherwise false. */
324 static bool
325 increment(unsigned short int *value,
326 unsigned short int min, unsigned short int max)
327 {
328 if (*value < max) {
329 ++*value;
330 return false;
331 } else {
332 *value = min;
333 return true;
334 }
335 }
336
337 static void
338 next_ports(unsigned short int *local_port, unsigned short int *remote_port)
339 {
340 if (increment(local_port, local_min_port, local_max_port)) {
341 increment(remote_port, remote_min_port, remote_max_port);
342 }
343 }
344
345 static void
346 bind_local_port(int fd, unsigned short int *local_port,
347 unsigned short int *remote_port)
348 {
349 int error;
350
351 if (!local_min_port && !local_max_port) {
352 next_ports(local_port, remote_port);
353 return;
354 }
355
356 do {
357 struct sockaddr_in local;
358
359 memset(&local, 0, sizeof local);
360 local.sin_family = AF_INET;
361 local.sin_addr = local_addr;
362 local.sin_port = htons(*local_port);
363 error = (bind(fd, (struct sockaddr *) &local, sizeof local) < 0
364 ? errno : 0);
365 next_ports(local_port, remote_port);
366 } while (error == EADDRINUSE || error == EINTR);
367 if (error) {
368 ovs_fatal(error, "bind failed");
369 }
370 }
371
372 static void
373 cmd_rate(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
374 {
375 unsigned short int local_port;
376 unsigned short int remote_port;
377 unsigned int completed = 0;
378 unsigned int failures = 0;
379 long long int start, prev;
380 struct pollfd *fds;
381 int n_fds;
382
383 if (!remote_addr.s_addr) {
384 ovs_fatal(0, "remote address must be specified with -r or --remote");
385 }
386 if (!remote_min_port && !remote_max_port) {
387 remote_min_port = remote_max_port = DEFAULT_PORT;
388 }
389
390 local_port = local_min_port;
391 remote_port = remote_min_port;
392 fds = xmalloc(n_sockets * sizeof *fds);
393 n_fds = 0;
394 start = prev = time_in_msec();
395 for (;;) {
396 long long int now;
397 long long int may_open;
398 int delay;
399 int error;
400 int j;
401
402 if (max_rate > 0) {
403 long long int cur_total = completed + n_fds;
404 long long int max_total = (time_in_msec() - start) * (max_rate / 1000.0);
405 if (max_total > cur_total) {
406 may_open = MIN(n_sockets, max_total - cur_total);
407 } else {
408 may_open = 0;
409 }
410 delay = 1000.0 / max_rate;
411 } else {
412 may_open = n_sockets;
413 delay = 1000;
414 }
415
416 while (may_open-- > 0 && n_fds < n_sockets) {
417 struct sockaddr_in remote;
418 int error;
419 int fd;
420
421 fd = socket(AF_INET, SOCK_STREAM, 0);
422 if (fd < 0) {
423 ovs_fatal(errno, "socket failed");
424 }
425
426 error = set_nonblocking(fd);
427 if (error) {
428 ovs_fatal(error, "set_nonblocking failed");
429 }
430
431 bind_local_port(fd, &local_port, &remote_port);
432
433 memset(&remote, 0, sizeof remote);
434 remote.sin_family = AF_INET;
435 remote.sin_addr = remote_addr;
436 remote.sin_port = htons(remote_port);
437 if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
438 if (errno == EINPROGRESS) {
439 fds[n_fds].fd = fd;
440 fds[n_fds].events = POLLOUT;
441 fds[n_fds].revents = 0;
442 n_fds++;
443 } else if (errno != ECONNREFUSED) {
444 ovs_fatal(errno, "connect");
445 }
446 } else {
447 /* Success, I guess. */
448 shutdown(fd, 2);
449 close(fd);
450 completed++;
451 }
452 }
453
454 if (n_fds == n_sockets) {
455 delay = 1000;
456 }
457
458 error = do_poll(fds, n_fds, delay);
459 if (error) {
460 ovs_fatal(errno, "poll");
461 }
462
463 for (j = 0; j < n_fds; ) {
464 if (fds[j].revents) {
465 if (fds[j].revents & POLLERR) {
466 failures++;
467 }
468 shutdown(fds[j].fd, 2);
469 close(fds[j].fd);
470 fds[j] = fds[--n_fds];
471 completed++;
472 } else {
473 j++;
474 }
475 }
476
477 now = time_in_msec();
478 if (now >= prev + 1000) {
479 long long int elapsed = now - start;
480 printf("%.3f s elapsed, %u OK, %u failed, avg %.1f/s\n",
481 elapsed / 1000.0, completed - failures, failures,
482 completed / (elapsed / 1000.0));
483 fflush(stdout);
484 prev = now;
485
486 if (timeout && elapsed > timeout * 1000LL) {
487 break;
488 }
489 }
490 }
491 }
492
493 static void
494 timer_end(long long int start, bool error,
495 int *min, int *max, unsigned long long int *total)
496 {
497 int elapsed = time_in_msec() - start;
498 static int last_elapsed = INT_MIN;
499 char c = error ? '!' : '.';
500
501 if (last_elapsed != elapsed) {
502 if (last_elapsed != INT_MIN) {
503 putchar('\n');
504 }
505 printf("%5d %c", elapsed, c);
506 fflush(stdout);
507 last_elapsed = elapsed;
508 } else {
509 putchar(c);
510 fflush(stdout);
511 }
512
513 if (elapsed < *min) {
514 *min = elapsed;
515 }
516 if (elapsed > *max) {
517 *max = elapsed;
518 }
519 *total += elapsed;
520 }
521
522 static void
523 cmd_latency(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
524 {
525 unsigned short int local_port;
526 unsigned short int remote_port;
527 int min = INT_MAX;
528 int max = 0;
529 unsigned long long int total = 0;
530 int i;
531
532 if (!remote_addr.s_addr) {
533 ovs_fatal(0, "remote address must be specified with -r or --rate");
534 }
535 if (!remote_min_port && !remote_max_port) {
536 remote_min_port = remote_max_port = DEFAULT_PORT;
537 }
538
539 local_port = local_min_port;
540 remote_port = remote_min_port;
541 for (i = 0; i < n_batches; i++) {
542 struct pollfd fds[MAX_SOCKETS];
543 long long int start;
544 int n_fds;
545 int j;
546
547 start = time_in_msec();
548 n_fds = 0;
549 for (j = 0; j < n_sockets; j++) {
550 struct sockaddr_in remote;
551 int error;
552 int fd;
553
554 fd = socket(AF_INET, SOCK_STREAM, 0);
555 if (fd < 0) {
556 ovs_fatal(errno, "socket failed");
557 }
558
559 error = set_nonblocking(fd);
560 if (error) {
561 ovs_fatal(error, "set_nonblocking failed");
562 }
563
564 bind_local_port(fd, &local_port, &remote_port);
565
566 memset(&remote, 0, sizeof remote);
567 remote.sin_family = AF_INET;
568 remote.sin_addr = remote_addr;
569 remote.sin_port = htons(remote_port);
570 if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
571 if (errno == EINPROGRESS) {
572 fds[n_fds].fd = fd;
573 fds[n_fds].events = POLLOUT;
574 fds[n_fds].revents = 0;
575 n_fds++;
576 } else if (errno != ECONNREFUSED) {
577 ovs_fatal(errno, "connect");
578 }
579 } else {
580 /* Success, I guess. */
581 close(fd);
582 timer_end(start, 0, &min, &max, &total);
583 }
584 }
585
586 while (n_fds > 0) {
587 int error;
588
589 error = do_poll(fds, n_fds, -1);
590 if (error) {
591 ovs_fatal(errno, "poll");
592 }
593
594 for (j = 0; j < n_fds; ) {
595 if (fds[j].revents) {
596 timer_end(start,
597 fds[j].revents & (POLLERR|POLLHUP) ? 1 : 0,
598 &min, &max, &total);
599 close(fds[j].fd);
600 fds[j] = fds[--n_fds];
601 } else {
602 j++;
603 }
604 }
605 }
606 putchar('\n');
607 }
608
609 printf("min %d ms, max %d ms, avg %llu ms\n",
610 min, max, total / (1ULL * n_sockets * n_batches));
611 }
612
613 static void
614 cmd_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
615 {
616 usage();
617 }
618
619 static const struct command all_commands[] = {
620 { "listen", NULL, 0, 0, cmd_listen },
621 { "rate", NULL, 0, 0, cmd_rate },
622 { "latency", NULL, 0, 0, cmd_latency },
623 { "help", NULL, 0, 0, cmd_help },
624 { NULL, NULL, 0, 0, NULL },
625 };
626
627 static const struct command *get_all_commands(void)
628 {
629 return all_commands;
630 }