| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * This times how long it takes to bind to a port when the port already | |
| * has multiple sockets in its bhash table. | |
| * | |
| * In the setup(), we populate the port's bhash table with | |
| * MAX_THREADS * MAX_CONNECTIONS number of entries. | |
| */ | |
| #include <unistd.h> | |
| #include <stdio.h> | |
| #include <netdb.h> | |
| #include <pthread.h> | |
| #include <string.h> | |
| #include <stdbool.h> | |
| #define MAX_THREADS 600 | |
| #define MAX_CONNECTIONS 40 | |
| static const char *setup_addr_v6 = "::1"; | |
| static const char *setup_addr_v4 = "127.0.0.1"; | |
| static const char *setup_addr; | |
| static const char *bind_addr; | |
| static const char *port; | |
| bool use_v6; | |
| int ret; | |
| static int fd_array[MAX_THREADS][MAX_CONNECTIONS]; | |
| static int bind_socket(int opt, const char *addr) | |
| { | |
| struct addrinfo *res, hint = {}; | |
| int sock_fd, reuse = 1, err; | |
| int domain = use_v6 ? AF_INET6 : AF_INET; | |
| sock_fd = socket(domain, SOCK_STREAM, 0); | |
| if (sock_fd < 0) { | |
| perror("socket fd err"); | |
| return sock_fd; | |
| } | |
| hint.ai_family = domain; | |
| hint.ai_socktype = SOCK_STREAM; | |
| err = getaddrinfo(addr, port, &hint, &res); | |
| if (err) { | |
| perror("getaddrinfo failed"); | |
| goto cleanup; | |
| } | |
| if (opt) { | |
| err = setsockopt(sock_fd, SOL_SOCKET, opt, &reuse, sizeof(reuse)); | |
| if (err) { | |
| perror("setsockopt failed"); | |
| goto cleanup; | |
| } | |
| } | |
| err = bind(sock_fd, res->ai_addr, res->ai_addrlen); | |
| if (err) { | |
| perror("failed to bind to port"); | |
| goto cleanup; | |
| } | |
| return sock_fd; | |
| cleanup: | |
| close(sock_fd); | |
| return err; | |
| } | |
| static void *setup(void *arg) | |
| { | |
| int sock_fd, i; | |
| int *array = (int *)arg; | |
| for (i = 0; i < MAX_CONNECTIONS; i++) { | |
| sock_fd = bind_socket(SO_REUSEPORT, setup_addr); | |
| if (sock_fd < 0) { | |
| ret = sock_fd; | |
| pthread_exit(&ret); | |
| } | |
| array[i] = sock_fd; | |
| } | |
| return NULL; | |
| } | |
| int main(int argc, const char *argv[]) | |
| { | |
| int listener_fd, sock_fd, i, j; | |
| pthread_t tid[MAX_THREADS]; | |
| clock_t begin, end; | |
| if (argc != 4) { | |
| printf("Usage: listener <port> <ipv6 | ipv4> <bind-addr>\n"); | |
| return -1; | |
| } | |
| port = argv[1]; | |
| use_v6 = strcmp(argv[2], "ipv6") == 0; | |
| bind_addr = argv[3]; | |
| setup_addr = use_v6 ? setup_addr_v6 : setup_addr_v4; | |
| listener_fd = bind_socket(SO_REUSEPORT, setup_addr); | |
| if (listen(listener_fd, 100) < 0) { | |
| perror("listen failed"); | |
| return -1; | |
| } | |
| /* Set up threads to populate the bhash table entry for the port */ | |
| for (i = 0; i < MAX_THREADS; i++) | |
| pthread_create(&tid[i], NULL, setup, fd_array[i]); | |
| for (i = 0; i < MAX_THREADS; i++) | |
| pthread_join(tid[i], NULL); | |
| if (ret) | |
| goto done; | |
| begin = clock(); | |
| /* Bind to the same port on a different address */ | |
| sock_fd = bind_socket(0, bind_addr); | |
| if (sock_fd < 0) | |
| goto done; | |
| end = clock(); | |
| printf("time spent = %f\n", (double)(end - begin) / CLOCKS_PER_SEC); | |
| /* clean up */ | |
| close(sock_fd); | |
| done: | |
| close(listener_fd); | |
| for (i = 0; i < MAX_THREADS; i++) { | |
| for (j = 0; i < MAX_THREADS; i++) | |
| close(fd_array[i][j]); | |
| } | |
| return 0; | |
| } |