mirror of
https://github.com/openvswitch/ovs
synced 2025-10-17 14:28:02 +00:00
MSVC can't handle either one. Signed-off-by: Linda Sun <lsun@vmware.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
625 lines
17 KiB
C
625 lines
17 KiB
C
/*
|
|
* Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <limits.h>
|
|
#include <poll.h>
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <stddef.h>
|
|
#include <unistd.h>
|
|
|
|
#include "command-line.h"
|
|
#include "poll-loop.h"
|
|
#include "socket-util.h"
|
|
#include "timeval.h"
|
|
#include "util.h"
|
|
#include "vlog.h"
|
|
|
|
#define DEFAULT_PORT 6630
|
|
|
|
#define MAX_SOCKETS 65535
|
|
static int n_batches = 1;
|
|
static int n_sockets = 100;
|
|
|
|
static struct in_addr local_addr;
|
|
static unsigned short int local_min_port, local_max_port;
|
|
|
|
static struct in_addr remote_addr;
|
|
static unsigned short int remote_min_port, remote_max_port;
|
|
|
|
static double max_rate;
|
|
|
|
static double timeout;
|
|
|
|
static const struct command *get_all_commands(void);
|
|
|
|
static void parse_options(int argc, char *argv[]);
|
|
static void usage(void);
|
|
|
|
static long long int
|
|
time_in_msec(void)
|
|
{
|
|
struct timeval tv;
|
|
|
|
if (gettimeofday(&tv, NULL) < 0) {
|
|
ovs_fatal(errno, "gettimeofday");
|
|
}
|
|
|
|
return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
set_program_name(argv[0]);
|
|
vlog_set_levels(NULL, VLF_ANY_FACILITY, VLL_EMER);
|
|
parse_options(argc, argv);
|
|
run_command(argc - optind, argv + optind, get_all_commands());
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
parse_target(const char *s_, struct in_addr *addr,
|
|
unsigned short int *min, unsigned short int *max)
|
|
{
|
|
char *s = xstrdup(s_);
|
|
char *colon;
|
|
int error;
|
|
|
|
colon = strchr(s, ':');
|
|
if (colon) {
|
|
*colon = '\0';
|
|
}
|
|
|
|
if (*s != '\0') {
|
|
error = lookup_hostname(s, addr);
|
|
if (error) {
|
|
ovs_fatal(error, "failed to look up IP address for \"%s\"", s_);
|
|
}
|
|
} else {
|
|
addr->s_addr = htonl(INADDR_ANY);
|
|
}
|
|
|
|
*min = *max = 0;
|
|
if (colon && colon[1] != '\0') {
|
|
const char *ports = colon + 1;
|
|
if (sscanf(ports, "%hu-%hu", min, max) == 2) {
|
|
if (*min > *max) {
|
|
ovs_fatal(0, "%s: minimum is greater than maximum", s_);
|
|
}
|
|
} else if (sscanf(ports, "%hu", min) == 1) {
|
|
*max = *min;
|
|
} else {
|
|
ovs_fatal(0, "%s: number or range expected", s_);
|
|
}
|
|
}
|
|
|
|
free(s);
|
|
}
|
|
|
|
static void
|
|
parse_options(int argc, char *argv[])
|
|
{
|
|
static const struct option long_options[] = {
|
|
{"local", required_argument, NULL, 'l'},
|
|
{"remote", required_argument, NULL, 'r'},
|
|
{"batches", required_argument, NULL, 'b'},
|
|
{"sockets", required_argument, NULL, 's'},
|
|
{"max-rate", required_argument, NULL, 'c'},
|
|
{"timeout", required_argument, NULL, 'T'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{NULL, 0, NULL, 0},
|
|
};
|
|
char *short_options = long_options_to_short_options(long_options);
|
|
|
|
local_addr.s_addr = htonl(INADDR_ANY);
|
|
local_min_port = local_max_port = 0;
|
|
|
|
remote_addr.s_addr = htonl(0);
|
|
remote_min_port = remote_max_port = 0;
|
|
|
|
for (;;) {
|
|
int c;
|
|
|
|
c = getopt_long(argc, argv, short_options, long_options, NULL);
|
|
if (c == -1) {
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case 'l':
|
|
parse_target(optarg,
|
|
&local_addr, &local_min_port, &local_max_port);
|
|
break;
|
|
|
|
case 'r':
|
|
parse_target(optarg,
|
|
&remote_addr, &remote_min_port, &remote_max_port);
|
|
if (remote_addr.s_addr == htonl(INADDR_ANY)) {
|
|
ovs_fatal(0, "remote IP address is required");
|
|
}
|
|
break;
|
|
|
|
case 'b':
|
|
n_batches = atoi(optarg);
|
|
if (n_batches < 0) {
|
|
ovs_fatal(0, "--batches or -b argument must be at least 1");
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
n_sockets = atoi(optarg);
|
|
if (n_sockets < 1 || n_sockets > MAX_SOCKETS) {
|
|
ovs_fatal(0, "--sockets or -s argument must be between 1 "
|
|
"and %d (inclusive)", MAX_SOCKETS);
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
max_rate = atof(optarg);
|
|
if (max_rate <= 0.0) {
|
|
ovs_fatal(0, "--max-rate or -c argument must be positive");
|
|
}
|
|
break;
|
|
|
|
case 'T':
|
|
timeout = atoi(optarg);
|
|
if (!timeout) {
|
|
ovs_fatal(0, "-T or --timeout argument must be positive");
|
|
}
|
|
break;
|
|
|
|
case 'h':
|
|
usage();
|
|
|
|
case 'V':
|
|
ovs_print_version(0, 0);
|
|
exit(EXIT_SUCCESS);
|
|
|
|
case '?':
|
|
exit(EXIT_FAILURE);
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
free(short_options);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
printf("\
|
|
%s: Open vSwitch flow setup benchmark utility\n\
|
|
usage: %s [OPTIONS] COMMAND [ARG...]\n\
|
|
latency connect many times all at once\n\
|
|
rate measure sustained flow setup rate\n\
|
|
listen accept TCP connections\n\
|
|
help display this help message\n\
|
|
\n\
|
|
Command options:\n\
|
|
-l, --local [IP][:PORTS] use local IP and range of PORTS\n\
|
|
-r, --remote IP[:PORTS] connect to remote IP and PORTS\n\
|
|
-s, --sockets N number of sockets for \"rate\" or \"latency\"\n\
|
|
-b, --batches N number of connection batches for \"latency\"\n\
|
|
-c, --max-rate NPERSEC connection rate limit for \"rate\"\n\
|
|
-T, --timeout MAXSECS max number of seconds to run for \"rate\"\n\
|
|
\n\
|
|
Other options:\n\
|
|
-h, --help display this help message\n\
|
|
-V, --version display version information\n",
|
|
program_name, program_name);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
cmd_listen(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
{
|
|
struct pollfd *fds;
|
|
int n_fds;
|
|
int port;
|
|
int i;
|
|
|
|
if (!local_min_port && !local_max_port) {
|
|
local_min_port = local_max_port = DEFAULT_PORT;
|
|
}
|
|
fds = xmalloc((1 + local_max_port - local_min_port) * sizeof *fds);
|
|
n_fds = 0;
|
|
for (port = local_min_port; port <= local_max_port; port++) {
|
|
struct sockaddr_in sin;
|
|
unsigned int yes = 1;
|
|
int error;
|
|
int fd;
|
|
|
|
/* Create socket, set SO_REUSEADDR. */
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
ovs_fatal(errno, "failed to create socket");
|
|
}
|
|
error = set_nonblocking(fd);
|
|
if (error) {
|
|
ovs_fatal(error, "failed to set non-blocking mode");
|
|
}
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
|
|
ovs_fatal(errno, "setsockopt(SO_REUSEADDR) failed");
|
|
}
|
|
|
|
/* Bind. */
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr = remote_addr;
|
|
sin.sin_port = htons(port);
|
|
if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) {
|
|
ovs_fatal(errno, "bind failed");
|
|
}
|
|
|
|
/* Listen. */
|
|
if (listen(fd, 10000) < 0) {
|
|
ovs_fatal(errno, "listen failed");
|
|
}
|
|
|
|
fds[n_fds].fd = fd;
|
|
fds[n_fds].events = POLLIN;
|
|
n_fds++;
|
|
}
|
|
|
|
for (;;) {
|
|
int retval;
|
|
|
|
do {
|
|
retval = poll(fds, n_fds, -1);
|
|
} while (retval < 0 && errno == EINTR);
|
|
if (retval < 0) {
|
|
ovs_fatal(errno, "poll failed");
|
|
}
|
|
|
|
for (i = 0; i < n_fds; i++) {
|
|
if (fds[i].revents & POLLIN) {
|
|
int newfd;
|
|
|
|
do {
|
|
newfd = accept(fds[i].fd, NULL, NULL);
|
|
} while (newfd < 0 && errno == EINTR);
|
|
|
|
if (newfd >= 0) {
|
|
close(newfd);
|
|
} else if (errno != EAGAIN) {
|
|
ovs_fatal(errno, "accept failed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Increments '*value' within the range 'min...max' inclusive. Returns true
|
|
* if '*value' wraps around to 'min', otherwise false. */
|
|
static bool
|
|
increment(unsigned short int *value,
|
|
unsigned short int min, unsigned short int max)
|
|
{
|
|
if (*value < max) {
|
|
++*value;
|
|
return false;
|
|
} else {
|
|
*value = min;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static void
|
|
next_ports(unsigned short int *local_port, unsigned short int *remote_port)
|
|
{
|
|
if (increment(local_port, local_min_port, local_max_port)) {
|
|
increment(remote_port, remote_min_port, remote_max_port);
|
|
}
|
|
}
|
|
|
|
static void
|
|
bind_local_port(int fd, unsigned short int *local_port,
|
|
unsigned short int *remote_port)
|
|
{
|
|
int error;
|
|
|
|
if (!local_min_port && !local_max_port) {
|
|
next_ports(local_port, remote_port);
|
|
return;
|
|
}
|
|
|
|
do {
|
|
struct sockaddr_in local;
|
|
|
|
memset(&local, 0, sizeof local);
|
|
local.sin_family = AF_INET;
|
|
local.sin_addr = local_addr;
|
|
local.sin_port = htons(*local_port);
|
|
error = (bind(fd, (struct sockaddr *) &local, sizeof local) < 0
|
|
? errno : 0);
|
|
next_ports(local_port, remote_port);
|
|
} while (error == EADDRINUSE || error == EINTR);
|
|
if (error) {
|
|
ovs_fatal(error, "bind failed");
|
|
}
|
|
}
|
|
|
|
static void
|
|
cmd_rate(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
{
|
|
unsigned short int local_port;
|
|
unsigned short int remote_port;
|
|
unsigned int completed = 0;
|
|
unsigned int failures = 0;
|
|
long long int start, prev;
|
|
struct pollfd *fds;
|
|
int n_fds;
|
|
|
|
if (!remote_addr.s_addr) {
|
|
ovs_fatal(0, "remote address must be specified with -r or --remote");
|
|
}
|
|
if (!remote_min_port && !remote_max_port) {
|
|
remote_min_port = remote_max_port = DEFAULT_PORT;
|
|
}
|
|
|
|
local_port = local_min_port;
|
|
remote_port = remote_min_port;
|
|
fds = xmalloc(n_sockets * sizeof *fds);
|
|
n_fds = 0;
|
|
start = prev = time_in_msec();
|
|
for (;;) {
|
|
long long int now;
|
|
long long int may_open;
|
|
int delay;
|
|
int error;
|
|
int j;
|
|
|
|
if (max_rate > 0) {
|
|
long long int cur_total = completed + n_fds;
|
|
long long int max_total = (time_in_msec() - start) * (max_rate / 1000.0);
|
|
if (max_total > cur_total) {
|
|
may_open = MIN(n_sockets, max_total - cur_total);
|
|
} else {
|
|
may_open = 0;
|
|
}
|
|
delay = 1000.0 / max_rate;
|
|
} else {
|
|
may_open = n_sockets;
|
|
delay = 1000;
|
|
}
|
|
|
|
while (may_open-- > 0 && n_fds < n_sockets) {
|
|
struct sockaddr_in remote;
|
|
int error;
|
|
int fd;
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
ovs_fatal(errno, "socket failed");
|
|
}
|
|
|
|
error = set_nonblocking(fd);
|
|
if (error) {
|
|
ovs_fatal(error, "set_nonblocking failed");
|
|
}
|
|
|
|
bind_local_port(fd, &local_port, &remote_port);
|
|
|
|
memset(&remote, 0, sizeof remote);
|
|
remote.sin_family = AF_INET;
|
|
remote.sin_addr = remote_addr;
|
|
remote.sin_port = htons(remote_port);
|
|
if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
|
|
if (errno == EINPROGRESS) {
|
|
fds[n_fds].fd = fd;
|
|
fds[n_fds].events = POLLOUT;
|
|
fds[n_fds].revents = 0;
|
|
n_fds++;
|
|
} else if (errno != ECONNREFUSED) {
|
|
ovs_fatal(errno, "connect");
|
|
}
|
|
} else {
|
|
/* Success, I guess. */
|
|
shutdown(fd, 2);
|
|
close(fd);
|
|
completed++;
|
|
}
|
|
}
|
|
|
|
if (n_fds == n_sockets) {
|
|
delay = 1000;
|
|
}
|
|
|
|
do {
|
|
error = poll(fds, n_fds, delay) < 0 ? errno : 0;
|
|
} while (error == EINTR);
|
|
if (error) {
|
|
ovs_fatal(errno, "poll");
|
|
}
|
|
|
|
for (j = 0; j < n_fds; ) {
|
|
if (fds[j].revents) {
|
|
if (fds[j].revents & POLLERR) {
|
|
failures++;
|
|
}
|
|
shutdown(fds[j].fd, 2);
|
|
close(fds[j].fd);
|
|
fds[j] = fds[--n_fds];
|
|
completed++;
|
|
} else {
|
|
j++;
|
|
}
|
|
}
|
|
|
|
now = time_in_msec();
|
|
if (now >= prev + 1000) {
|
|
long long int elapsed = now - start;
|
|
printf("%.3f s elapsed, %u OK, %u failed, avg %.1f/s\n",
|
|
elapsed / 1000.0, completed - failures, failures,
|
|
completed / (elapsed / 1000.0));
|
|
fflush(stdout);
|
|
prev = now;
|
|
|
|
if (timeout && elapsed > timeout * 1000LL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
timer_end(long long int start, bool error,
|
|
int *min, int *max, unsigned long long int *total)
|
|
{
|
|
int elapsed = time_in_msec() - start;
|
|
static int last_elapsed = INT_MIN;
|
|
char c = error ? '!' : '.';
|
|
|
|
if (last_elapsed != elapsed) {
|
|
if (last_elapsed != INT_MIN) {
|
|
putchar('\n');
|
|
}
|
|
printf("%5d %c", elapsed, c);
|
|
fflush(stdout);
|
|
last_elapsed = elapsed;
|
|
} else {
|
|
putchar(c);
|
|
fflush(stdout);
|
|
}
|
|
|
|
if (elapsed < *min) {
|
|
*min = elapsed;
|
|
}
|
|
if (elapsed > *max) {
|
|
*max = elapsed;
|
|
}
|
|
*total += elapsed;
|
|
}
|
|
|
|
static void
|
|
cmd_latency(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
{
|
|
unsigned short int local_port;
|
|
unsigned short int remote_port;
|
|
int min = INT_MAX;
|
|
int max = 0;
|
|
unsigned long long int total = 0;
|
|
int i;
|
|
|
|
if (!remote_addr.s_addr) {
|
|
ovs_fatal(0, "remote address must be specified with -r or --rate");
|
|
}
|
|
if (!remote_min_port && !remote_max_port) {
|
|
remote_min_port = remote_max_port = DEFAULT_PORT;
|
|
}
|
|
|
|
local_port = local_min_port;
|
|
remote_port = remote_min_port;
|
|
for (i = 0; i < n_batches; i++) {
|
|
struct pollfd fds[MAX_SOCKETS];
|
|
long long int start;
|
|
int n_fds;
|
|
int j;
|
|
|
|
start = time_in_msec();
|
|
n_fds = 0;
|
|
for (j = 0; j < n_sockets; j++) {
|
|
struct sockaddr_in remote;
|
|
int error;
|
|
int fd;
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
ovs_fatal(errno, "socket failed");
|
|
}
|
|
|
|
error = set_nonblocking(fd);
|
|
if (error) {
|
|
ovs_fatal(error, "set_nonblocking failed");
|
|
}
|
|
|
|
bind_local_port(fd, &local_port, &remote_port);
|
|
|
|
memset(&remote, 0, sizeof remote);
|
|
remote.sin_family = AF_INET;
|
|
remote.sin_addr = remote_addr;
|
|
remote.sin_port = htons(remote_port);
|
|
if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
|
|
if (errno == EINPROGRESS) {
|
|
fds[n_fds].fd = fd;
|
|
fds[n_fds].events = POLLOUT;
|
|
fds[n_fds].revents = 0;
|
|
n_fds++;
|
|
} else if (errno != ECONNREFUSED) {
|
|
ovs_fatal(errno, "connect");
|
|
}
|
|
} else {
|
|
/* Success, I guess. */
|
|
close(fd);
|
|
timer_end(start, 0, &min, &max, &total);
|
|
}
|
|
}
|
|
|
|
while (n_fds > 0) {
|
|
int error;
|
|
|
|
do {
|
|
error = poll(fds, n_fds, -1) < 0 ? errno : 0;
|
|
} while (error == EINTR);
|
|
if (error) {
|
|
ovs_fatal(errno, "poll");
|
|
}
|
|
|
|
for (j = 0; j < n_fds; ) {
|
|
if (fds[j].revents) {
|
|
timer_end(start,
|
|
fds[j].revents & (POLLERR|POLLHUP) ? 1 : 0,
|
|
&min, &max, &total);
|
|
close(fds[j].fd);
|
|
fds[j] = fds[--n_fds];
|
|
} else {
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
printf("min %d ms, max %d ms, avg %llu ms\n",
|
|
min, max, total / (1ULL * n_sockets * n_batches));
|
|
}
|
|
|
|
static void
|
|
cmd_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
{
|
|
usage();
|
|
}
|
|
|
|
static const struct command all_commands[] = {
|
|
{ "listen", 0, 0, cmd_listen },
|
|
{ "rate", 0, 0, cmd_rate },
|
|
{ "latency", 0, 0, cmd_latency },
|
|
{ "help", 0, 0, cmd_help },
|
|
{ NULL, 0, 0, NULL },
|
|
};
|
|
|
|
static const struct command *get_all_commands(void)
|
|
{
|
|
return all_commands;
|
|
}
|