1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * This times how long it takes to bind to a port when the port already 4 * has multiple sockets in its bhash table. 5 * 6 * In the setup(), we populate the port's bhash table with 7 * MAX_THREADS * MAX_CONNECTIONS number of entries. 8 */ 9 10 #include <unistd.h> 11 #include <stdio.h> 12 #include <netdb.h> 13 #include <pthread.h> 14 #include <string.h> 15 #include <stdbool.h> 16 17 #define MAX_THREADS 600 18 #define MAX_CONNECTIONS 40 19 20 static const char *setup_addr_v6 = "::1"; 21 static const char *setup_addr_v4 = "127.0.0.1"; 22 static const char *setup_addr; 23 static const char *bind_addr; 24 static const char *port; 25 bool use_v6; 26 int ret; 27 28 static int fd_array[MAX_THREADS][MAX_CONNECTIONS]; 29 30 static int bind_socket(int opt, const char *addr) 31 { 32 struct addrinfo *res, hint = {}; 33 int sock_fd, reuse = 1, err; 34 int domain = use_v6 ? AF_INET6 : AF_INET; 35 36 sock_fd = socket(domain, SOCK_STREAM, 0); 37 if (sock_fd < 0) { 38 perror("socket fd err"); 39 return sock_fd; 40 } 41 42 hint.ai_family = domain; 43 hint.ai_socktype = SOCK_STREAM; 44 45 err = getaddrinfo(addr, port, &hint, &res); 46 if (err) { 47 perror("getaddrinfo failed"); 48 goto cleanup; 49 } 50 51 if (opt) { 52 err = setsockopt(sock_fd, SOL_SOCKET, opt, &reuse, sizeof(reuse)); 53 if (err) { 54 perror("setsockopt failed"); 55 goto cleanup; 56 } 57 } 58 59 err = bind(sock_fd, res->ai_addr, res->ai_addrlen); 60 if (err) { 61 perror("failed to bind to port"); 62 goto cleanup; 63 } 64 65 return sock_fd; 66 67 cleanup: 68 close(sock_fd); 69 return err; 70 } 71 72 static void *setup(void *arg) 73 { 74 int sock_fd, i; 75 int *array = (int *)arg; 76 77 for (i = 0; i < MAX_CONNECTIONS; i++) { 78 sock_fd = bind_socket(SO_REUSEADDR | SO_REUSEPORT, setup_addr); 79 if (sock_fd < 0) { 80 ret = sock_fd; 81 pthread_exit(&ret); 82 } 83 array[i] = sock_fd; 84 } 85 86 return NULL; 87 } 88 89 int main(int argc, const char *argv[]) 90 { 91 int listener_fd, sock_fd, i, j; 92 pthread_t tid[MAX_THREADS]; 93 clock_t begin, end; 94 95 if (argc != 4) { 96 printf("Usage: listener <port> <ipv6 | ipv4> <bind-addr>\n"); 97 return -1; 98 } 99 100 port = argv[1]; 101 use_v6 = strcmp(argv[2], "ipv6") == 0; 102 bind_addr = argv[3]; 103 104 setup_addr = use_v6 ? setup_addr_v6 : setup_addr_v4; 105 106 listener_fd = bind_socket(SO_REUSEADDR | SO_REUSEPORT, setup_addr); 107 if (listen(listener_fd, 100) < 0) { 108 perror("listen failed"); 109 return -1; 110 } 111 112 /* Set up threads to populate the bhash table entry for the port */ 113 for (i = 0; i < MAX_THREADS; i++) 114 pthread_create(&tid[i], NULL, setup, fd_array[i]); 115 116 for (i = 0; i < MAX_THREADS; i++) 117 pthread_join(tid[i], NULL); 118 119 if (ret) 120 goto done; 121 122 begin = clock(); 123 124 /* Bind to the same port on a different address */ 125 sock_fd = bind_socket(0, bind_addr); 126 if (sock_fd < 0) 127 goto done; 128 129 end = clock(); 130 131 printf("time spent = %f\n", (double)(end - begin) / CLOCKS_PER_SEC); 132 133 /* clean up */ 134 close(sock_fd); 135 136 done: 137 close(listener_fd); 138 for (i = 0; i < MAX_THREADS; i++) { 139 for (j = 0; i < MAX_THREADS; i++) 140 close(fd_array[i][j]); 141 } 142 143 return 0; 144 } 145