2 * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <sys/socket.h>
29 #include "command-line.h"
30 #include "poll-loop.h"
31 #include "socket-util.h"
34 #include "openvswitch/vlog.h"
36 #define DEFAULT_PORT 6630
38 #define MAX_SOCKETS 65535
39 static int n_batches = 1;
40 static int n_sockets = 100;
42 static struct in_addr local_addr;
43 static unsigned short int local_min_port, local_max_port;
45 static struct in_addr remote_addr;
46 static unsigned short int remote_min_port, remote_max_port;
48 static double max_rate;
50 static double timeout;
52 static const struct ovs_cmdl_command *get_all_commands(void);
54 static void parse_options(int argc, char *argv[]);
55 static void usage(void);
58 do_poll(struct pollfd *fds, int nfds, int timeout)
63 retval = poll(fds, nfds, timeout);
64 } while (retval < 0 && errno == EINTR);
66 retval = WSAPoll(fds, nfds, timeout);
78 return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
82 main(int argc, char *argv[])
84 struct ovs_cmdl_context ctx = { .argc = 0, };
85 set_program_name(argv[0]);
86 vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_EMER);
87 parse_options(argc, argv);
88 ctx.argc = argc - optind;
89 ctx.argv = argv + optind;
90 ovs_cmdl_run_command(&ctx, get_all_commands());
95 parse_target(const char *s_, struct in_addr *addr,
96 unsigned short int *min, unsigned short int *max)
98 char *s = xstrdup(s_);
102 colon = strchr(s, ':');
108 error = lookup_hostname(s, addr);
110 ovs_fatal(error, "failed to look up IP address for \"%s\"", s_);
113 addr->s_addr = htonl(INADDR_ANY);
117 if (colon && colon[1] != '\0') {
118 const char *ports = colon + 1;
119 if (ovs_scan(ports, "%hu-%hu", min, max)) {
121 ovs_fatal(0, "%s: minimum is greater than maximum", s_);
123 } else if (ovs_scan(ports, "%hu", min)) {
126 ovs_fatal(0, "%s: number or range expected", s_);
134 parse_options(int argc, char *argv[])
136 static const struct option long_options[] = {
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'},
147 char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
149 local_addr.s_addr = htonl(INADDR_ANY);
150 local_min_port = local_max_port = 0;
152 remote_addr.s_addr = htonl(0);
153 remote_min_port = remote_max_port = 0;
158 c = getopt_long(argc, argv, short_options, long_options, NULL);
166 &local_addr, &local_min_port, &local_max_port);
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");
178 n_batches = atoi(optarg);
180 ovs_fatal(0, "--batches or -b argument must be at least 1");
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);
193 max_rate = atof(optarg);
194 if (max_rate <= 0.0) {
195 ovs_fatal(0, "--max-rate or -c argument must be positive");
200 timeout = atoi(optarg);
202 ovs_fatal(0, "-T or --timeout argument must be positive");
210 ovs_print_version(0, 0);
227 %s: Open vSwitch flow setup benchmark utility\n\
228 usage: %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\
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\
243 -h, --help display this help message\n\
244 -V, --version display version information\n",
245 program_name, program_name);
250 cmd_listen(struct ovs_cmdl_context *ctx OVS_UNUSED)
257 if (!local_min_port && !local_max_port) {
258 local_min_port = local_max_port = DEFAULT_PORT;
260 fds = xmalloc((1 + local_max_port - local_min_port) * sizeof *fds);
262 for (port = local_min_port; port <= local_max_port; port++) {
263 struct sockaddr_in sin;
264 unsigned int yes = 1;
268 /* Create socket, set SO_REUSEADDR. */
269 fd = socket(AF_INET, SOCK_STREAM, 0);
271 ovs_fatal(errno, "failed to create socket");
273 error = set_nonblocking(fd);
275 ovs_fatal(error, "failed to set non-blocking mode");
277 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
278 ovs_fatal(errno, "setsockopt(SO_REUSEADDR) failed");
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");
290 if (listen(fd, 10000) < 0) {
291 ovs_fatal(errno, "listen failed");
295 fds[n_fds].events = POLLIN;
302 retval = do_poll(fds, n_fds, -1);
304 ovs_fatal(errno, "poll failed");
307 for (i = 0; i < n_fds; i++) {
308 if (fds[i].revents & POLLIN) {
312 newfd = accept(fds[i].fd, NULL, NULL);
313 } while (newfd < 0 && errno == EINTR);
317 } else if (errno != EAGAIN) {
318 ovs_fatal(errno, "accept failed");
325 /* Increments '*value' within the range 'min...max' inclusive. Returns true
326 * if '*value' wraps around to 'min', otherwise false. */
328 increment(unsigned short int *value,
329 unsigned short int min, unsigned short int max)
341 next_ports(unsigned short int *local_port, unsigned short int *remote_port)
343 if (increment(local_port, local_min_port, local_max_port)) {
344 increment(remote_port, remote_min_port, remote_max_port);
349 bind_local_port(int fd, unsigned short int *local_port,
350 unsigned short int *remote_port)
354 if (!local_min_port && !local_max_port) {
355 next_ports(local_port, remote_port);
360 struct sockaddr_in local;
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
368 next_ports(local_port, remote_port);
369 } while (error == EADDRINUSE || error == EINTR);
371 ovs_fatal(error, "bind failed");
376 cmd_rate(struct ovs_cmdl_context *ctx OVS_UNUSED)
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;
386 if (!remote_addr.s_addr) {
387 ovs_fatal(0, "remote address must be specified with -r or --remote");
389 if (!remote_min_port && !remote_max_port) {
390 remote_min_port = remote_max_port = DEFAULT_PORT;
393 local_port = local_min_port;
394 remote_port = remote_min_port;
395 fds = xmalloc(n_sockets * sizeof *fds);
397 start = prev = time_in_msec();
400 long long int may_open;
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);
413 delay = 1000.0 / max_rate;
415 may_open = n_sockets;
419 while (may_open-- > 0 && n_fds < n_sockets) {
420 struct sockaddr_in remote;
424 fd = socket(AF_INET, SOCK_STREAM, 0);
426 ovs_fatal(errno, "socket failed");
429 error = set_nonblocking(fd);
431 ovs_fatal(error, "set_nonblocking failed");
434 bind_local_port(fd, &local_port, &remote_port);
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) {
443 fds[n_fds].events = POLLOUT;
444 fds[n_fds].revents = 0;
446 } else if (errno != ECONNREFUSED) {
447 ovs_fatal(errno, "connect");
450 /* Success, I guess. */
457 if (n_fds == n_sockets) {
461 error = do_poll(fds, n_fds, delay);
463 ovs_fatal(errno, "poll");
466 for (j = 0; j < n_fds; ) {
467 if (fds[j].revents) {
468 if (fds[j].revents & POLLERR) {
471 shutdown(fds[j].fd, 2);
473 fds[j] = fds[--n_fds];
480 now = time_in_msec();
481 if (now >= prev + 1000) {
482 long long int elapsed = now - start;
483 printf("%.3f s elapsed, %u OK, %u failed, avg %.1f/s\n",
484 elapsed / 1000.0, completed - failures, failures,
485 completed / (elapsed / 1000.0));
489 if (timeout && elapsed > timeout * 1000LL) {
497 timer_end(long long int start, bool error,
498 int *min, int *max, unsigned long long int *total)
500 int elapsed = time_in_msec() - start;
501 static int last_elapsed = INT_MIN;
502 char c = error ? '!' : '.';
504 if (last_elapsed != elapsed) {
505 if (last_elapsed != INT_MIN) {
508 printf("%5d %c", elapsed, c);
510 last_elapsed = elapsed;
516 if (elapsed < *min) {
519 if (elapsed > *max) {
526 cmd_latency(struct ovs_cmdl_context *ctx OVS_UNUSED)
528 unsigned short int local_port;
529 unsigned short int remote_port;
532 unsigned long long int total = 0;
535 if (!remote_addr.s_addr) {
536 ovs_fatal(0, "remote address must be specified with -r or --rate");
538 if (!remote_min_port && !remote_max_port) {
539 remote_min_port = remote_max_port = DEFAULT_PORT;
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];
550 start = time_in_msec();
552 for (j = 0; j < n_sockets; j++) {
553 struct sockaddr_in remote;
557 fd = socket(AF_INET, SOCK_STREAM, 0);
559 ovs_fatal(errno, "socket failed");
562 error = set_nonblocking(fd);
564 ovs_fatal(error, "set_nonblocking failed");
567 bind_local_port(fd, &local_port, &remote_port);
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) {
576 fds[n_fds].events = POLLOUT;
577 fds[n_fds].revents = 0;
579 } else if (errno != ECONNREFUSED) {
580 ovs_fatal(errno, "connect");
583 /* Success, I guess. */
585 timer_end(start, 0, &min, &max, &total);
592 error = do_poll(fds, n_fds, -1);
594 ovs_fatal(errno, "poll");
597 for (j = 0; j < n_fds; ) {
598 if (fds[j].revents) {
600 fds[j].revents & (POLLERR|POLLHUP) ? 1 : 0,
603 fds[j] = fds[--n_fds];
612 printf("min %d ms, max %d ms, avg %llu ms\n",
613 min, max, total / (1ULL * n_sockets * n_batches));
617 cmd_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
622 static const struct ovs_cmdl_command all_commands[] = {
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 },
630 static const struct ovs_cmdl_command *get_all_commands(void)