Stress test bind(2) and connect(2) system calls in OpenBSD regress.
authorbluhm <bluhm@openbsd.org>
Wed, 6 Dec 2023 14:41:52 +0000 (14:41 +0000)
committerbluhm <bluhm@openbsd.org>
Wed, 6 Dec 2023 14:41:52 +0000 (14:41 +0000)
regress/sys/netinet/Makefile
regress/sys/netinet/bindconnect/Makefile [new file with mode: 0644]
regress/sys/netinet/bindconnect/README [new file with mode: 0644]
regress/sys/netinet/bindconnect/bindconnect.c [new file with mode: 0644]

index cff4e22..6f4da84 100644 (file)
@@ -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 (file)
index 0000000..2884042
--- /dev/null
@@ -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 (file)
index 0000000..b8a00ad
--- /dev/null
@@ -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 (file)
index 0000000..e94f809
--- /dev/null
@@ -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;
+}