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