From 5e00790eef53d9eeb1eb696b894405ab0ea3d66b Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 27 Jul 2011 16:23:06 -0700 Subject: [PATCH] ovs-benchmark: New utility. --- debian/openvswitch-common.install | 1 + debian/openvswitch-common.manpages | 1 + rhel/openvswitch.spec.in | 2 + utilities/automake.mk | 7 + utilities/ovs-benchmark.1.in | 202 ++++++++++ utilities/ovs-benchmark.c | 619 +++++++++++++++++++++++++++++ xenserver/openvswitch-xen.spec | 2 + 7 files changed, 834 insertions(+) create mode 100644 utilities/ovs-benchmark.1.in create mode 100644 utilities/ovs-benchmark.c diff --git a/debian/openvswitch-common.install b/debian/openvswitch-common.install index 758c59c2..df261d07 100644 --- a/debian/openvswitch-common.install +++ b/debian/openvswitch-common.install @@ -1,5 +1,6 @@ etc/openvswitch/bugtool-plugins usr/bin/ovs-appctl +usr/bin/ovs-benchmark usr/bin/ovs-ofctl usr/bin/ovs-parse-leaks usr/bin/ovs-pki diff --git a/debian/openvswitch-common.manpages b/debian/openvswitch-common.manpages index e81295b4..1e99479c 100644 --- a/debian/openvswitch-common.manpages +++ b/debian/openvswitch-common.manpages @@ -1,6 +1,7 @@ _debian/ovsdb/ovsdb-client.1 _debian/ovsdb/ovsdb-tool.1 _debian/utilities/ovs-appctl.8 +_debian/utilities/ovs-benchmark.1 _debian/utilities/ovs-ofctl.8 _debian/utilities/ovs-pki.8 utilities/bugtool/ovs-bugtool.8 diff --git a/rhel/openvswitch.spec.in b/rhel/openvswitch.spec.in index 47b51ffe..13059900 100644 --- a/rhel/openvswitch.spec.in +++ b/rhel/openvswitch.spec.in @@ -103,6 +103,7 @@ exit 0 /etc/logrotate.d/openvswitch /etc/openvswitch/bugtool-plugins/* /usr/bin/ovs-appctl +/usr/bin/ovs-benchmark /usr/bin/ovs-dpctl /usr/bin/ovs-ofctl /usr/bin/ovs-parse-leaks @@ -116,6 +117,7 @@ exit 0 /usr/sbin/ovs-bugtool /usr/sbin/ovs-vswitchd /usr/sbin/ovsdb-server +/usr/share/man/man1/ovs-benchmark.1.gz /usr/share/man/man1/ovs-pcap.1.gz /usr/share/man/man1/ovs-tcpundump.1.gz /usr/share/man/man1/ovsdb-client.1.gz diff --git a/utilities/automake.mk b/utilities/automake.mk index aab85a22..dc731bfb 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -16,6 +16,7 @@ scripts_SCRIPTS += utilities/ovs-ctl utilities/ovs-lib.sh utilities/ovs-save EXTRA_DIST += \ utilities/ovs-appctl.8.in \ + utilities/ovs-benchmark.1.in \ utilities/ovs-controller.8.in \ utilities/ovs-ctl.in \ utilities/ovs-dpctl.8.in \ @@ -39,6 +40,7 @@ EXTRA_DIST += \ DISTCLEANFILES += \ utilities/ovs-appctl.8 \ utilities/ovs-ctl \ + utilities/ovs-benchmark.1 \ utilities/ovs-controller.8 \ utilities/ovs-dpctl.8 \ utilities/ovs-lib.sh \ @@ -58,6 +60,7 @@ DISTCLEANFILES += \ man_MANS += \ utilities/ovs-appctl.8 \ + utilities/ovs-benchmark.1 \ utilities/ovs-controller.8 \ utilities/ovs-dpctl.8 \ utilities/ovs-ofctl.8 \ @@ -98,4 +101,8 @@ utilities_nlmon_SOURCES = utilities/nlmon.c utilities_nlmon_LDADD = lib/libopenvswitch.a endif +bin_PROGRAMS += utilities/ovs-benchmark +utilities_ovs_benchmark_SOURCES = utilities/ovs-benchmark.c +utilities_ovs_benchmark_LDADD = lib/libopenvswitch.a + include utilities/bugtool/automake.mk diff --git a/utilities/ovs-benchmark.1.in b/utilities/ovs-benchmark.1.in new file mode 100644 index 00000000..f0ec3703 --- /dev/null +++ b/utilities/ovs-benchmark.1.in @@ -0,0 +1,202 @@ +.\" -*- nroff -*- +.de IQ +. br +. ns +. IP "\\$1" +.. +.TH ovs\-benchmark 1 "July 2011" "Open vSwitch" "Open vSwitch Manual" +. +.SH NAME +ovs\-benchmark \- flow setup benchmark utility for Open vSwitch +. +.SH SYNOPSIS +. +.SY ovs\-benchmark\ latency +\fB\-\-remote \fIip\fR[\fB:\fIports\fR] +.OP \-\-sockets nsocks +.OP \-\-batches nbatches +.OP \-\-local \fR[\fIip\fR][\fB:\fIports\fR] +.YS +. +.SY ovs\-benchmark\ rate +\fB\-\-remote \fIip\fR[\fB:\fIports\fR] +.OP \-\-max\-rate rate +.OP \-\-timeout maxsecs +.OP \-\-sockets nsocks +.OP \-\-batches nbatches +.OP \-\-local \fR[\fIip\fR][\fB:\fIports\fR] +.YS +. +.SY ovs\-benchmark\ listen +.OP \-\-local \fR[\fIip\fR]\fB:\fIports +.YS +. +.SY ovs\-benchmark\ help +.YS +. +.SH DESCRIPTION +\fBovs\-benchmark\fR tests the performance of Open vSwitch flow setup +by setting up a number of TCP connections and measuring the time +required. It can also be used with the Linux bridge or without any +bridging software, which allows one to measure the bandwidth and +latency cost of bridging. +.PP +Each \fBovs\-benchmark\fR command is described separately below. +. +.SH "The ``latency'' command" +. +.PP +This command initiates \fInsocks\fR TCP connections (by default, 100) +as quickly as possible, waits for each one to complete with success or +failure, and prints a bar chart of completion times on standard +output, followed by a summary line. Each line in the bar chart lists +a time to connection completion in milliseconds followed by a number +of \fB.\fR or \fB!\fR symbols, one for each TCP connection that +completed in that many milliseconds. A successful connection prints a +\fB.\fR, and an unsuccessful connection (e.g. to a port on which no +process is listening) prints a \fB!\fR. +. +.PP +If \fInbatches\fR is given, the entire procedure is repeated the +specified number of times. Only a single summary line is printed at +the end. +. +.PP +Results vary widely based on the number of sockets and whether the +remote host is listening for connections on the specified ports. With +a small number of sockets, all connection times typically remain +within a handful of milliseconds. As the number of sockets increases, +the distribution of connection times clusters around the sending TCP +stack's SYN retransmission interval. (This pattern occurs with or +without Open vSwitch on the network path.) +. +.SH "The ``rate'' command" +. +.PP +This command initiates \fInsocks\fR TCP connections (by default, 100) +as quickly as possible (limited by \fImaxrate\fR, if +\fB\-\-max\-rate\fR is specified). Each time a connection completes +with success or failure, it closes that connection and initiates a new +one. It continues to do so either forever or, if \fB\-\-timeout\fR is +specified, until \fImaxsecs\fR seconds have elapsed. During the test, +it prints statistics about time elapsed, successful and unsuccessful +connections, and the average number of completed (succeeded or failed) +connections per second over the run. +. +.PP +Without \fB\-\-max\-rate\fR, the \fBrate\fR command measures the +maximum sustained flow setup rate for an Open vSwitch instance. This +naturally tends to drive \fBovs\-vswitchd\fR CPU usage to 100% on the +host receiving the traffic. +. +.PP +When \fB\-\-max\-rate\fR is specified with a value below the maximum +rate that an Open vSwitch instance can handle, then \fBrate\fR can +also be used to measure the kernel and userspace CPU cost of flow +setups at specific flow rates. +. +.PP +Results tend to fluctuate greatly for the first few seconds of a run, +then settle down. The displayed average is calculated over the entire +run and so tends to converge asymptotically on the ``correct'' value. +To converge more quickly, try running for 5 to 10 seconds, then +killing and restarting the run. +. +.SH "The ``listen'' command" +. +.PP +This command listens on one or more TCP ports for incoming +connections. It accepts connections and immediately closes them. It +can be paired with the \fBrate\fR or \fBlatency\fR commands for +observing effects of successful vs. unsuccessful TCP connections. +. +.PP +It is easier to reproduce and interpret \fBovs\-benchmark\fR results +when there is no listener (see \fBNOTES\fR below). +. +.SH "The ``help'' command" +. +.PP +Prints a usage message and exits successfully. +. +.SH OPTIONS +. +.IP "\fB\-r \fIip\fR[\fB:\fIports\fR]" +.IQ "\fB\-\-remote \fIip\fR[\fB:\fIports\fR]" +This option, required on \fBlatency\fR and \fBrate\fR commands, +minimally specifies the remote host to connect to (as an IP address or +DNS name) as \fIip\fR. +. +.IP +A TCP port or range of ports (separated by \fB\-\fR) may also be +specified. If a range is specified then each port in the range is +used in round-robin order. The default port is 6630 if none is +specified. +. +.IP "\fB\-l \fR[\fIip\fR][\fB:\fIports\fR]" +.IQ "\fB\-\-local \fR[\fIip\fR][\fB:\fIports\fR]" +On the \fBlatency\fR and \fBrate\fR, without this option, outgoing +connections will not bind a specific TCP port. The local TCP stack +will pick a local TCP port to bind. When this option is specified, +the specified port or range of ports will be used in turn. (If a port +range is specified on both \fB\-\-local\fR and \fB\-\-remote\fR, then +each local port in its range will be used before the remote port is +incremented to the next port in its range.) +. +.IP +On the \fBlisten\fR command, this option specifies the local port or +ports and IP addresses on which to listen. If it is omitted, port +6630 on any IP address is used. +. +.IP "\fB\-s \fInsocks\fR" +.IQ "\fB\-\-sockets \fInsocks\fR" +For \fBlatency\fR, sets the number of connections to initiate per +batch. For \fBrate\fR, sets the number of outstanding connections +attempts to maintain at any given time. The default is 100. +. +.IP "\fB\-b \fInbatches\fR" +.IQ "\fB\-\-batches \fInbatches\fR" +For \fBlatency\fR, sets the number of times to initiate and wait for +all of the connections to complete. The default is 1. +. +.IP "\fB\-c \fImaxrate\fR" +.IQ "\fB\-\-max\-rate \fImaxrate\fR" +For \fBrate\fR, caps the maximum rate at which connections will be +attempted to \fImaxrate\fR connections per second. By default there +is no limit. +. +.IP "\fB\-T \fImaxsecs\fR" +.IQ "\fB\-\-timeout \fImaxsecs\fR" +For \fBrate\fR, stops the benchmark after \fImaxsecs\fR seconds have +elapsed. By default, the benchmark continues until interrupted by a +signal. +. +.SH NOTES +.PP +\fBovs\-benchmark\fR uses standard POSIX socket calls for network +access, so it shares the strengths and limitations of TCP/IP and its +implementations in the local and remote TCP/IP stacks. Particularly, +TCP and its implementations limit the number of successfully completed +and then closed TCP connections. This means that \fBovs\-benchmark\fR +tests tend to slow down if run for long intervals or with large +numbers of sockets or batches, if the remote system is listening on +the port or ports being contacted. The problem does not occur when +the remote system is not listening. \fBovs\-benchmark\fR results are +therefore much more reliable and repeatable when the remote system is +not listening on the port or ports being contacted. Even a single +listening socket (e.g. range of ports 8000 to 9000 with one listener +on port 8080) can cause anomalies in results. +. +.PP +Be sure that the remote TCP/IP stack's firewall allows the benchmark's +traffic to be processed. For Open vSwitch benchmarking purposes, you +might want to disable the firewall with, e.g., \fBiptables \-F\fR. +. +.PP +\fBovs\-benchmark\fR is single-threaded. A multithreaded process +might be able to initiate connections more quickly. +. +.PP +A TCP connection consists of two flows (one in each direction), so +multiply the TCP connection statistics that \fBovs\-benchmark\fR +reports by 2 to get flow statistics. diff --git a/utilities/ovs-benchmark.c b/utilities/ovs-benchmark.c new file mode 100644 index 00000000..e4e92257 --- /dev/null +++ b/utilities/ovs-benchmark.c @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2010, 2011 Nicira Networks. + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 all_commands[]; + +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, 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 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 + 10) { + long long int elapsed = now - start; + printf("%.3f s elapsed, %u OK, %u failed, avg %.1f/s \r", + 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 }, +}; diff --git a/xenserver/openvswitch-xen.spec b/xenserver/openvswitch-xen.spec index 16ea2a1d..32c99fc7 100644 --- a/xenserver/openvswitch-xen.spec +++ b/xenserver/openvswitch-xen.spec @@ -111,9 +111,11 @@ mv $RPM_BUILD_ROOT/etc/openvswitch/bugtool-plugins $RPM_BUILD_ROOT/etc/xensource # Get rid of stuff we don't want to make RPM happy. rm \ + $RPM_BUILD_ROOT/usr/bin/ovs-benchmark \ $RPM_BUILD_ROOT/usr/sbin/ovs-bugtool \ $RPM_BUILD_ROOT/usr/bin/ovs-controller \ $RPM_BUILD_ROOT/usr/bin/ovs-pki \ + $RPM_BUILD_ROOT/usr/share/man/man8/ovs-benchmark.1 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-bugtool.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 -- 2.30.2