| // 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_REUSEADDR | 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_REUSEADDR | 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; |
| } |