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(opt: SO_REUSEADDR | SO_REUSEPORT, addr: 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(opt: SO_REUSEADDR | SO_REUSEPORT, addr: 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(opt: 0, addr: 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 | |