summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2023-12-06 14:41:53 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2023-12-06 14:41:53 +0000
commit0209a3fdcd80dccf3d1264a70003412c63776eee (patch)
tree9bbab8f9c50773ce6c7be433a3fa9a3bb9920a04
parentd4cd916a714965c895affd7a0c32f4b68ecf568f (diff)
Stress test bind(2) and connect(2) system calls in OpenBSD regress.
-rw-r--r--regress/sys/netinet/Makefile4
-rw-r--r--regress/sys/netinet/bindconnect/Makefile28
-rw-r--r--regress/sys/netinet/bindconnect/README18
-rw-r--r--regress/sys/netinet/bindconnect/bindconnect.c279
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;
+}