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