]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-benchmark.c
util: Introduce get_program_version function.
[mirror_ovs.git] / utilities / ovs-benchmark.c
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
39 static int n_batches = 1;
40 static int n_sockets = 100;
41
42 static struct in_addr local_addr;
43 static unsigned short int local_min_port, local_max_port;
44
45 static struct in_addr remote_addr;
46 static unsigned short int remote_min_port, remote_max_port;
47
48 static double max_rate;
49
50 static double timeout;
51
52 static const struct command all_commands[];
53
54 static void parse_options(int argc, char *argv[]);
55 static void usage(void);
56
57 static long long int
58 time_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
69 int
70 main(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
79 static void
80 parse_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
118 static void
119 parse_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':
195 ovs_print_version(0, 0);
196 exit(EXIT_SUCCESS);
197
198 case '?':
199 exit(EXIT_FAILURE);
200
201 default:
202 abort();
203 }
204 }
205 free(short_options);
206 }
207
208 static void
209 usage(void)
210 {
211 printf("\
212 %s: Open vSwitch flow setup benchmark utility\n\
213 usage: %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\
219 Command 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\
227 Other 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
234 static void
235 cmd_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. */
314 static bool
315 increment(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
327 static void
328 next_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
335 static void
336 bind_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
362 static void
363 cmd_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
485 static void
486 timer_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
514 static void
515 cmd_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
607 static void
608 cmd_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
609 {
610 usage();
611 }
612
613 static 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 };