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 err_free_info; 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 err_free_info; 63 } 64 freeaddrinfo(res); 65 return sock_fd; 66 err_free_info: 67 freeaddrinfo(res); 68 cleanup: 69 close(sock_fd); 70 return err; 71 } 72 73 static void *setup(void *arg) 74 { 75 int sock_fd, i; 76 int *array = (int *)arg; 77 78 for (i = 0; i < MAX_CONNECTIONS; i++) { 79 sock_fd = bind_socket(SO_REUSEPORT, setup_addr); 80 if (sock_fd < 0) { 81 ret = sock_fd; 82 pthread_exit(&ret); 83 } 84 array[i] = sock_fd; 85 } 86 87 return NULL; 88 } 89 90 int main(int argc, const char *argv[]) 91 { 92 int listener_fd, sock_fd, i, j; 93 pthread_t tid[MAX_THREADS]; 94 clock_t begin, end; 95 96 if (argc != 4) { 97 printf("Usage: listener <port> <ipv6 | ipv4> <bind-addr>\n"); 98 return -1; 99 } 100 101 port = argv[1]; 102 use_v6 = strcmp(argv[2], "ipv6") == 0; 103 bind_addr = argv[3]; 104 105 setup_addr = use_v6 ? setup_addr_v6 : setup_addr_v4; 106 107 listener_fd = bind_socket(SO_REUSEPORT, setup_addr); 108 if (listen(listener_fd, 100) < 0) { 109 perror("listen failed"); 110 return -1; 111 } 112 113 /* Set up threads to populate the bhash table entry for the port */ 114 for (i = 0; i < MAX_THREADS; i++) 115 pthread_create(&tid[i], NULL, setup, fd_array[i]); 116 117 for (i = 0; i < MAX_THREADS; i++) 118 pthread_join(tid[i], NULL); 119 120 if (ret) 121 goto done; 122 123 begin = clock(); 124 125 /* Bind to the same port on a different address */ 126 sock_fd = bind_socket(0, bind_addr); 127 if (sock_fd < 0) 128 goto done; 129 130 end = clock(); 131 132 printf("time spent = %f\n", (double)(end - begin) / CLOCKS_PER_SEC); 133 134 /* clean up */ 135 close(sock_fd); 136 137 done: 138 close(listener_fd); 139 for (i = 0; i < MAX_THREADS; i++) { 140 for (j = 0; i < MAX_THREADS; i++) 141 close(fd_array[i][j]); 142 } 143 144 return 0; 145 } 146