1 // SPDX-License-Identifier: GPL-2.0
2 /* Author: Dmitry Safonov <dima@arista.com> */
10 #include "../../../../include/linux/bits.h"
11 #include "../../../../include/linux/kernel.h"
14 #define BENCH_NR_ITERS 100 /* number of times to run gathering statistics */
16 static void gen_test_ips(union tcp_addr
*ips
, size_t ips_nr
, bool use_rand
)
18 union tcp_addr net
= {};
21 if (inet_pton(TEST_FAMILY
, TEST_NETWORK
, &net
) != 1)
22 test_error("Can't convert ip address %s", TEST_NETWORK
);
25 for (i
= 0; i
< ips_nr
; i
++)
26 ips
[i
] = gen_tcp_addr(net
, 2 * i
+ 1);
29 for (i
= 0; i
< ips_nr
; i
++) {
30 size_t r
= (size_t)random() | 0x1;
32 ips
[i
] = gen_tcp_addr(net
, r
);
34 for (j
= i
- 1; j
> 0 && i
> 0; j
--) {
35 if (!memcmp(&ips
[i
], &ips
[j
], sizeof(union tcp_addr
))) {
43 static void test_add_routes(union tcp_addr
*ips
, size_t ips_nr
)
47 for (i
= 0; i
< ips_nr
; i
++) {
48 union tcp_addr
*p
= (union tcp_addr
*)&ips
[i
];
51 err
= ip_route_add(veth_name
, TEST_FAMILY
, this_ip_addr
, *p
);
52 if (err
&& err
!= -EEXIST
)
53 test_error("Failed to add route");
57 static void server_apply_keys(int lsk
, union tcp_addr
*ips
, size_t ips_nr
)
61 for (i
= 0; i
< ips_nr
; i
++) {
62 union tcp_addr
*p
= (union tcp_addr
*)&ips
[i
];
64 if (test_add_key(lsk
, DEFAULT_TEST_PASSWORD
, *p
, -1, 100, 100))
65 test_error("setsockopt(TCP_AO)");
69 static const size_t nr_keys
[] = { 512, 1024, 2048, 4096, 8192 };
70 static union tcp_addr
*test_ips
;
80 static struct bench_tests
{
81 struct bench_stats delete_last_key
;
82 struct bench_stats add_key
;
83 struct bench_stats delete_rand_key
;
84 struct bench_stats connect_last_key
;
85 struct bench_stats connect_rand_key
;
86 struct bench_stats delete_async
;
87 } bench_results
[ARRAY_SIZE(nr_keys
)];
89 #define NSEC_PER_SEC 1000000000ULL
91 static void measure_call(struct bench_stats
*st
,
92 void (*f
)(int, void *), int sk
, void *arg
)
94 struct timespec start
= {}, end
= {};
98 if (clock_gettime(CLOCK_MONOTONIC
, &start
))
99 test_error("clock_gettime()");
103 if (clock_gettime(CLOCK_MONOTONIC
, &end
))
104 test_error("clock_gettime()");
106 nsec
= (end
.tv_sec
- start
.tv_sec
) * NSEC_PER_SEC
;
107 if (end
.tv_nsec
>= start
.tv_nsec
)
108 nsec
+= end
.tv_nsec
- start
.tv_nsec
;
110 nsec
-= start
.tv_nsec
- end
.tv_nsec
;
113 st
->min
= st
->max
= nsec
;
121 /* Welford-Knuth algorithm */
123 delta
= (double)nsec
- st
->mean
;
124 st
->mean
+= delta
/ st
->nr
;
125 st
->s2
+= delta
* ((double)nsec
- st
->mean
);
128 static void delete_mkt(int sk
, void *arg
)
130 struct tcp_ao_del
*ao
= arg
;
132 if (setsockopt(sk
, IPPROTO_TCP
, TCP_AO_DEL_KEY
, ao
, sizeof(*ao
)))
133 test_error("setsockopt(TCP_AO_DEL_KEY)");
136 static void add_back_mkt(int sk
, void *arg
)
138 union tcp_addr
*p
= arg
;
140 if (test_add_key(sk
, DEFAULT_TEST_PASSWORD
, *p
, -1, 100, 100))
141 test_error("setsockopt(TCP_AO)");
144 static void bench_delete(int lsk
, struct bench_stats
*add
,
145 struct bench_stats
*del
,
146 union tcp_addr
*ips
, size_t ips_nr
,
147 bool rand_order
, bool async
)
149 struct tcp_ao_del ao_del
= {};
155 ao_del
.del_async
= !!async
;
156 ao_del
.prefix
= DEFAULT_TEST_PREFIX
;
158 /* Remove the first added */
159 p
= (union tcp_addr
*)&ips
[0];
160 tcp_addr_to_sockaddr_in(&ao_del
.addr
, p
, 0);
162 for (i
= 0; i
< BENCH_NR_ITERS
; i
++) {
163 measure_call(del
, delete_mkt
, lsk
, (void *)&ao_del
);
165 /* Restore it back */
166 measure_call(add
, add_back_mkt
, lsk
, (void *)p
);
169 * Slowest for FILO-linked-list:
170 * on (i) iteration removing ips[i] element. When it gets
171 * added to the list back - it becomes first to fetch, so
172 * on (i + 1) iteration go to ips[i + 1] element.
175 p
= (union tcp_addr
*)&ips
[rand() % ips_nr
];
177 p
= (union tcp_addr
*)&ips
[i
% ips_nr
];
178 tcp_addr_to_sockaddr_in(&ao_del
.addr
, p
, 0);
182 static void bench_connect_srv(int lsk
, union tcp_addr
*ips
, size_t ips_nr
)
186 for (i
= 0; i
< BENCH_NR_ITERS
; i
++) {
189 synchronize_threads();
191 if (test_wait_fd(lsk
, TEST_TIMEOUT_SEC
, 0))
192 test_error("test_wait_fd()");
194 sk
= accept(lsk
, NULL
, NULL
);
196 test_error("accept()");
202 static void test_print_stats(const char *desc
, size_t nr
, struct bench_stats
*bs
)
204 test_ok("%-20s\t%zu keys: min=%" PRIu64
"ms max=%" PRIu64
"ms mean=%gms stddev=%g",
205 desc
, nr
, bs
->min
/ 1000000, bs
->max
/ 1000000,
206 bs
->mean
/ 1000000, sqrt((bs
->mean
/ 1000000) / bs
->nr
));
209 static void *server_fn(void *arg
)
213 for (i
= 0; i
< ARRAY_SIZE(nr_keys
); i
++) {
214 struct bench_tests
*bt
= &bench_results
[i
];
217 test_ips
= malloc(nr_keys
[i
] * sizeof(union tcp_addr
));
219 test_error("malloc()");
221 lsk
= test_listen_socket(this_ip_addr
, test_server_port
+ i
, 1);
223 gen_test_ips(test_ips
, nr_keys
[i
], false);
224 test_add_routes(test_ips
, nr_keys
[i
]);
225 test_set_optmem(KERNEL_TCP_AO_KEY_SZ_ROUND_UP
* nr_keys
[i
]);
226 server_apply_keys(lsk
, test_ips
, nr_keys
[i
]);
228 synchronize_threads();
229 bench_connect_srv(lsk
, test_ips
, nr_keys
[i
]);
230 bench_connect_srv(lsk
, test_ips
, nr_keys
[i
]);
232 /* The worst case for FILO-list */
233 bench_delete(lsk
, &bt
->add_key
, &bt
->delete_last_key
,
234 test_ips
, nr_keys
[i
], false, false);
235 test_print_stats("Add a new key",
236 nr_keys
[i
], &bt
->add_key
);
237 test_print_stats("Delete: worst case",
238 nr_keys
[i
], &bt
->delete_last_key
);
240 bench_delete(lsk
, &bt
->add_key
, &bt
->delete_rand_key
,
241 test_ips
, nr_keys
[i
], true, false);
242 test_print_stats("Delete: random-search",
243 nr_keys
[i
], &bt
->delete_rand_key
);
245 bench_delete(lsk
, &bt
->add_key
, &bt
->delete_async
,
246 test_ips
, nr_keys
[i
], false, true);
247 test_print_stats("Delete: async", nr_keys
[i
], &bt
->delete_async
);
256 static void connect_client(int sk
, void *arg
)
260 if (test_connect_socket(sk
, this_ip_dest
, test_server_port
+ *p
) <= 0)
261 test_error("failed to connect()");
264 static void client_addr_setup(int sk
, union tcp_addr taddr
)
267 struct sockaddr_in6 addr
= {
268 .sin6_family
= AF_INET6
,
270 .sin6_addr
= taddr
.a6
,
273 struct sockaddr_in addr
= {
274 .sin_family
= AF_INET
,
276 .sin_addr
= taddr
.a4
,
281 ret
= ip_addr_add(veth_name
, TEST_FAMILY
, taddr
, TEST_PREFIX
);
282 if (ret
&& ret
!= -EEXIST
)
283 test_error("Failed to add ip address");
284 ret
= ip_route_add(veth_name
, TEST_FAMILY
, taddr
, this_ip_dest
);
285 if (ret
&& ret
!= -EEXIST
)
286 test_error("Failed to add route");
288 if (bind(sk
, &addr
, sizeof(addr
)))
289 test_error("bind()");
292 static void bench_connect_client(size_t port_off
, struct bench_tests
*bt
,
293 union tcp_addr
*ips
, size_t ips_nr
, bool rand_order
)
295 struct bench_stats
*con
;
300 con
= &bt
->connect_rand_key
;
302 con
= &bt
->connect_last_key
;
304 p
= (union tcp_addr
*)&ips
[0];
306 for (i
= 0; i
< BENCH_NR_ITERS
; i
++) {
307 int sk
= socket(test_family
, SOCK_STREAM
, IPPROTO_TCP
);
310 test_error("socket()");
312 client_addr_setup(sk
, *p
);
313 if (test_add_key(sk
, DEFAULT_TEST_PASSWORD
, this_ip_dest
,
315 test_error("setsockopt(TCP_AO_ADD_KEY)");
317 synchronize_threads();
319 measure_call(con
, connect_client
, sk
, (void *)&port_off
);
324 * Slowest for FILO-linked-list:
325 * on (i) iteration removing ips[i] element. When it gets
326 * added to the list back - it becomes first to fetch, so
327 * on (i + 1) iteration go to ips[i + 1] element.
330 p
= (union tcp_addr
*)&ips
[rand() % ips_nr
];
332 p
= (union tcp_addr
*)&ips
[i
% ips_nr
];
336 static void *client_fn(void *arg
)
340 for (i
= 0; i
< ARRAY_SIZE(nr_keys
); i
++) {
341 struct bench_tests
*bt
= &bench_results
[i
];
343 synchronize_threads();
344 bench_connect_client(i
, bt
, test_ips
, nr_keys
[i
], false);
345 test_print_stats("Connect: worst case",
346 nr_keys
[i
], &bt
->connect_last_key
);
348 bench_connect_client(i
, bt
, test_ips
, nr_keys
[i
], false);
349 test_print_stats("Connect: random-search",
350 nr_keys
[i
], &bt
->connect_last_key
);
352 synchronize_threads();
356 int main(int argc
, char *argv
[])
358 test_init(31, server_fn
, client_fn
);