diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2023-12-06 14:41:53 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2023-12-06 14:41:53 +0000 |
commit | 0209a3fdcd80dccf3d1264a70003412c63776eee (patch) | |
tree | 9bbab8f9c50773ce6c7be433a3fa9a3bb9920a04 | |
parent | d4cd916a714965c895affd7a0c32f4b68ecf568f (diff) |
Stress test bind(2) and connect(2) system calls in OpenBSD regress.
-rw-r--r-- | regress/sys/netinet/Makefile | 4 | ||||
-rw-r--r-- | regress/sys/netinet/bindconnect/Makefile | 28 | ||||
-rw-r--r-- | regress/sys/netinet/bindconnect/README | 18 | ||||
-rw-r--r-- | regress/sys/netinet/bindconnect/bindconnect.c | 279 |
4 files changed, 327 insertions, 2 deletions
diff --git a/regress/sys/netinet/Makefile b/regress/sys/netinet/Makefile index cff4e22daf3..6f4da849596 100644 --- a/regress/sys/netinet/Makefile +++ b/regress/sys/netinet/Makefile @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile,v 1.8 2020/12/17 14:16:10 bluhm Exp $ +# $OpenBSD: Makefile,v 1.9 2023/12/06 14:41:52 bluhm Exp $ SUBDIR += arp autoport -SUBDIR += broadcast_bind +SUBDIR += bindconnect broadcast_bind SUBDIR += carp SUBDIR += frag SUBDIR += in_pcbbind ipsec diff --git a/regress/sys/netinet/bindconnect/Makefile b/regress/sys/netinet/bindconnect/Makefile new file mode 100644 index 00000000000..2884042f645 --- /dev/null +++ b/regress/sys/netinet/bindconnect/Makefile @@ -0,0 +1,28 @@ +# $OpenBSD: Makefile,v 1.1 2023/12/06 14:41:52 bluhm Exp $ + +PROG= bindconnect +LDADD= -lpthread +DPADD= ${LIBPTHREAD} +WARNINGS= yes + +CLEANFILES= ktrace.out + +${REGRESS_TARGETS}: ${PROG} + +REGRESS_TARGETS += run-default +run-default: + ${SUDO} time ${KTRACE} ./${PROG} + +REGRESS_TARGETS += run-bind +run-bind: + ${SUDO} time ${KTRACE} ./${PROG} -n 10 -s 2 -o 1 -b 5 -c 0 + +REGRESS_TARGETS += run-connect +run-connect: + ${SUDO} time ${KTRACE} ./${PROG} -n 10 -s 2 -o 1 -b 0 -c 5 + +REGRESS_TARGETS += run-bind-connect +run-bind-connect: + ${SUDO} time ${KTRACE} ./${PROG} -n 10 -s 2 -o 1 -b 3 -c 3 + +.include <bsd.regress.mk> diff --git a/regress/sys/netinet/bindconnect/README b/regress/sys/netinet/bindconnect/README new file mode 100644 index 00000000000..b8a00ad2f7d --- /dev/null +++ b/regress/sys/netinet/bindconnect/README @@ -0,0 +1,18 @@ +Stress test bind(2) and connect(2) system calls in OpenBSD regress. + +bindconnect [-b bind] [-c connect] [-n num] [-o close] [-s socket] [-t time] + -b bind threads binding sockets, default 1 + -c connect threads connecting sockets, default 1 + -n num number of file descriptors, default 100 + -o close threads closing sockets, default 1 + -s socket threads creating sockets, default 1 + -t time run time in seconds, default 10 + +Separate threads are started to run socket(2), close(2), bind(2), +and connect(2) system calls concurrently. The number of sockets +is controlled by the process limit of open file descriptors. All +system calls operate on random file descriptors. By setting the +number of threads for each system call and the number of available +file descriptors, the focus for the stress test can be changed. + +Currently only IPv4 UDP sockets with 127.0.0.1 are supported. diff --git a/regress/sys/netinet/bindconnect/bindconnect.c b/regress/sys/netinet/bindconnect/bindconnect.c new file mode 100644 index 00000000000..e94f80987ff --- /dev/null +++ b/regress/sys/netinet/bindconnect/bindconnect.c @@ -0,0 +1,279 @@ +/* $OpenBSD: bindconnect.c,v 1.1 2023/12/06 14:41:52 bluhm Exp $ */ + +/* + * Copyright (c) 2023 Alexander Bluhm <bluhm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/resource.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <err.h> +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +int fd_base; +unsigned int fd_num = 100; +unsigned int run_time = 10; +unsigned int socket_num = 1, close_num = 1, bind_num = 1, connect_num = 1; + +static void __dead +usage(void) +{ + fprintf(stderr, + "bindconnect [-b bind] [-c connect] [-n num] [-o close]\n" + "[-s socket] [-t time]\n" + " -b bind threads binding sockets, default %u\n" + " -c connect threads connecting sockets, default %u\n" + " -n num number of file descriptors, default %u\n" + " -o close threads closing sockets, default %u\n" + " -s socket threads creating sockets, default %u\n" + " -t time run time in seconds, default %u\n", + bind_num, connect_num, fd_num, close_num, socket_num, run_time); + exit(2); +} + +static inline struct sockaddr * +sintosa(struct sockaddr_in *sin) +{ + return ((struct sockaddr *)(sin)); +} + +static void * +thread_socket(void *arg) +{ + volatile int *run = arg; + unsigned long count; + + for (count = 0; *run; count++) { + socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + } + + return (void *)count; +} + +static void * +thread_close(void *arg) +{ + volatile int *run = arg; + unsigned long count; + int fd; + + for (count = 0; *run; count++) { + fd = fd_base + arc4random_uniform(fd_num); + close(fd); + } + + return (void *)count; +} + +static void * +thread_bind(void *arg) +{ + volatile int *run = arg; + unsigned long count; + int fd; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + for (count = 0; *run; count++) { + fd = fd_base + arc4random_uniform(fd_num); + bind(fd, sintosa(&sin), sizeof(sin)); + } + + return (void *)count; +} + +static void * +thread_connect(void *arg) +{ + volatile int *run = arg; + unsigned long count; + int fd; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = arc4random(); + + for (count = 0; *run; count++) { + fd = fd_base + arc4random_uniform(fd_num); + connect(fd, sintosa(&sin), sizeof(sin)); + } + + return (void *)count; +} + +int +main(int argc, char *argv[]) +{ + struct rlimit rlim; + pthread_t *tsocket, *tclose, *tbind, *tconnect; + const char *errstr; + int ch, run; + unsigned int n; + unsigned long socket_count, close_count, bind_count, connect_count; + + fd_base = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd_base < 0) + err(1, "socket fd_base"); + + while ((ch = getopt(argc, argv, "b:c:n:o:s:t:")) != -1) { + switch (ch) { + case 'b': + bind_num = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bind is %s: %s", errstr, optarg); + break; + case 'c': + connect_num = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) + errx(1, "connect is %s: %s", errstr, optarg); + break; + case 'n': + fd_num = strtonum(optarg, 1, INT_MAX - fd_base, + &errstr); + if (errstr != NULL) + errx(1, "num is %s: %s", errstr, optarg); + break; + case 'o': + close_num = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) + errx(1, "close is %s: %s", errstr, optarg); + break; + case 's': + socket_num = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) + errx(1, "socket is %s: %s", errstr, optarg); + break; + case 't': + run_time = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) + errx(1, "time is %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc > 0) + usage(); + + if (closefrom(fd_base) < 0) + err(1, "closefrom %d", fd_base); + + if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) + err(1, "getrlimit"); + rlim.rlim_max = MAX(rlim.rlim_max, fd_base + fd_num); + rlim.rlim_cur = fd_base + fd_num; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + err(1, "setrlimit %llu", rlim.rlim_cur); + + run = 1; + tsocket = calloc(socket_num, sizeof(pthread_t)); + if (tsocket == NULL) + err(1, "tsocket"); + for (n = 0; n < socket_num; n++) { + errno = pthread_create(&tsocket[n], NULL, thread_socket, &run); + if (errno) + err(1, "pthread_create socket %u", n); + } + tclose = calloc(close_num, sizeof(pthread_t)); + if (tclose == NULL) + err(1, "tclose"); + for (n = 0; n < close_num; n++) { + errno = pthread_create(&tclose[n], NULL, thread_close, &run); + if (errno) + err(1, "pthread_create close %u", n); + } + tbind = calloc(bind_num, sizeof(pthread_t)); + if (tbind == NULL) + err(1, "tbind"); + for (n = 0; n < bind_num; n++) { + errno = pthread_create(&tbind[n], NULL, thread_bind, &run); + if (errno) + err(1, "pthread_create bind %u", n); + } + tconnect = calloc(connect_num, sizeof(pthread_t)); + if (tconnect == NULL) + err(1, "tconnect"); + for (n = 0; n < connect_num; n++) { + errno = pthread_create(&tconnect[n], NULL, thread_connect, + &run); + if (errno) + err(1, "pthread_create connect %u", n); + } + + if (run_time > 0) { + if (sleep(run_time) < 0) + err(1, "sleep %u", run_time); + } + + run = 0; + socket_count = 0; + for (n = 0; n < socket_num; n++) { + unsigned long count; + + errno = pthread_join(tsocket[n], (void **)&count); + if (errno) + err(1, "pthread_join socket %u", n); + socket_count += count; + } + close_count = 0; + for (n = 0; n < close_num; n++) { + unsigned long count; + + errno = pthread_join(tclose[n], (void **)&count); + if (errno) + err(1, "pthread_join close %u", n); + close_count += count; + } + bind_count = 0; + for (n = 0; n < bind_num; n++) { + unsigned long count; + + errno = pthread_join(tbind[n], (void **)&count); + if (errno) + err(1, "pthread_join bind %u", n); + bind_count += count; + } + connect_count = 0; + for (n = 0; n < connect_num; n++) { + unsigned long count; + + errno = pthread_join(tconnect[n], (void **)&count); + if (errno) + err(1, "pthread_join connect %u", n); + connect_count += count; + } + printf("count: socket %lu, close %lu, bind %lu, connect %lu\n", + socket_count, close_count, bind_count, connect_count); + + return 0; +} |