Skip to content

Commit d1066c9

Browse files
0x7f454c46davem330
authored andcommitted
selftests/net: Add test/benchmark for removing MKTs
Sample output: > 1..36 > # 1106[lib/setup.c:207] rand seed 1660754406 > TAP version 13 > ok 1 Worst case connect 512 keys: min=0ms max=1ms mean=0.583329ms stddev=0.076376 > ok 2 Connect random-search 512 keys: min=0ms max=1ms mean=0.53412ms stddev=0.0516779 > ok 3 Worst case delete 512 keys: min=2ms max=11ms mean=6.04139ms stddev=0.245792 > ok 4 Add a new key 512 keys: min=0ms max=13ms mean=0.673415ms stddev=0.0820618 > ok 5 Remove random-search 512 keys: min=5ms max=9ms mean=6.65969ms stddev=0.258064 > ok 6 Remove async 512 keys: min=0ms max=0ms mean=0.041825ms stddev=0.0204512 > ok 7 Worst case connect 1024 keys: min=0ms max=2ms mean=0.520357ms stddev=0.0721358 > ok 8 Connect random-search 1024 keys: min=0ms max=2ms mean=0.535312ms stddev=0.0517355 > ok 9 Worst case delete 1024 keys: min=5ms max=9ms mean=8.27219ms stddev=0.287614 > ok 10 Add a new key 1024 keys: min=0ms max=1ms mean=0.688121ms stddev=0.0829531 > ok 11 Remove random-search 1024 keys: min=5ms max=9ms mean=8.37649ms stddev=0.289422 > ok 12 Remove async 1024 keys: min=0ms max=0ms mean=0.0457096ms stddev=0.0213798 > ok 13 Worst case connect 2048 keys: min=0ms max=2ms mean=0.748804ms stddev=0.0865335 > ok 14 Connect random-search 2048 keys: min=0ms max=2ms mean=0.782993ms stddev=0.0625697 > ok 15 Worst case delete 2048 keys: min=5ms max=10ms mean=8.23106ms stddev=0.286898 > ok 16 Add a new key 2048 keys: min=0ms max=1ms mean=0.812988ms stddev=0.0901658 > ok 17 Remove random-search 2048 keys: min=8ms max=9ms mean=8.84949ms stddev=0.297481 > ok 18 Remove async 2048 keys: min=0ms max=0ms mean=0.0297223ms stddev=0.0172402 > ok 19 Worst case connect 4096 keys: min=1ms max=5ms mean=1.53352ms stddev=0.123836 > ok 20 Connect random-search 4096 keys: min=1ms max=5ms mean=1.52226ms stddev=0.0872429 > ok 21 Worst case delete 4096 keys: min=5ms max=9ms mean=8.25874ms stddev=0.28738 > ok 22 Add a new key 4096 keys: min=0ms max=3ms mean=1.67382ms stddev=0.129376 > ok 23 Remove random-search 4096 keys: min=5ms max=10ms mean=8.26178ms stddev=0.287433 > ok 24 Remove async 4096 keys: min=0ms max=0ms mean=0.0340009ms stddev=0.0184393 > ok 25 Worst case connect 8192 keys: min=2ms max=4ms mean=2.86208ms stddev=0.169177 > ok 26 Connect random-search 8192 keys: min=2ms max=4ms mean=2.87592ms stddev=0.119915 > ok 27 Worst case delete 8192 keys: min=6ms max=11ms mean=7.55291ms stddev=0.274826 > ok 28 Add a new key 8192 keys: min=1ms max=5ms mean=2.56797ms stddev=0.160249 > ok 29 Remove random-search 8192 keys: min=5ms max=10ms mean=7.14002ms stddev=0.267208 > ok 30 Remove async 8192 keys: min=0ms max=0ms mean=0.0320066ms stddev=0.0178904 > ok 31 Worst case connect 16384 keys: min=5ms max=6ms mean=5.55334ms stddev=0.235655 > ok 32 Connect random-search 16384 keys: min=5ms max=6ms mean=5.52614ms stddev=0.166225 > ok 33 Worst case delete 16384 keys: min=5ms max=11ms mean=7.39109ms stddev=0.271866 > ok 34 Add a new key 16384 keys: min=2ms max=4ms mean=3.35799ms stddev=0.183248 > ok 35 Remove random-search 16384 keys: min=5ms max=8ms mean=6.86078ms stddev=0.261931 > ok 36 Remove async 16384 keys: min=0ms max=0ms mean=0.0302384ms stddev=0.0173892 > # Totals: pass:36 fail:0 xfail:0 xpass:0 skip:0 error:0 >From the output it's visible that the current simplified approach with linked-list of MKTs scales quite fine even for thousands of keys. And that also means that the majority of the time for delete is eaten by synchronize_rcu() [which I can confirm separately by tracing]. Signed-off-by: Dmitry Safonov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6f0c472 commit d1066c9

File tree

2 files changed

+362
-1
lines changed

2 files changed

+362
-1
lines changed

tools/testing/selftests/net/tcp_ao/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: GPL-2.0
2-
TEST_BOTH_AF := connect
2+
TEST_BOTH_AF := bench-lookups
3+
TEST_BOTH_AF += connect
34
TEST_BOTH_AF += connect-deny
45
TEST_BOTH_AF += icmps-accept icmps-discard
56
TEST_BOTH_AF += setsockopt-closed
@@ -49,3 +50,5 @@ $(OUTPUT)/%_ipv6: %.c
4950

5051
$(OUTPUT)/icmps-accept_ipv4: CFLAGS+= -DTEST_ICMPS_ACCEPT
5152
$(OUTPUT)/icmps-accept_ipv6: CFLAGS+= -DTEST_ICMPS_ACCEPT
53+
$(OUTPUT)/bench-lookups_ipv4: LDFLAGS+= -lm
54+
$(OUTPUT)/bench-lookups_ipv6: LDFLAGS+= -lm
Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Author: Dmitry Safonov <[email protected]> */
3+
#include <arpa/inet.h>
4+
#include <inttypes.h>
5+
#include <math.h>
6+
#include <stdlib.h>
7+
#include <stdio.h>
8+
#include <time.h>
9+
10+
#include "../../../../include/linux/bits.h"
11+
#include "../../../../include/linux/kernel.h"
12+
#include "aolib.h"
13+
14+
#define BENCH_NR_ITERS 100 /* number of times to run gathering statistics */
15+
16+
static void gen_test_ips(union tcp_addr *ips, size_t ips_nr, bool use_rand)
17+
{
18+
union tcp_addr net = {};
19+
size_t i, j;
20+
21+
if (inet_pton(TEST_FAMILY, TEST_NETWORK, &net) != 1)
22+
test_error("Can't convert ip address %s", TEST_NETWORK);
23+
24+
if (!use_rand) {
25+
for (i = 0; i < ips_nr; i++)
26+
ips[i] = gen_tcp_addr(net, 2 * i + 1);
27+
return;
28+
}
29+
for (i = 0; i < ips_nr; i++) {
30+
size_t r = (size_t)random() | 0x1;
31+
32+
ips[i] = gen_tcp_addr(net, r);
33+
34+
for (j = i - 1; j > 0 && i > 0; j--) {
35+
if (!memcmp(&ips[i], &ips[j], sizeof(union tcp_addr))) {
36+
i--; /* collision */
37+
break;
38+
}
39+
}
40+
}
41+
}
42+
43+
static void test_add_routes(union tcp_addr *ips, size_t ips_nr)
44+
{
45+
size_t i;
46+
47+
for (i = 0; i < ips_nr; i++) {
48+
union tcp_addr *p = (union tcp_addr *)&ips[i];
49+
50+
if (ip_route_add(veth_name, TEST_FAMILY, this_ip_addr, *p))
51+
test_error("Failed to add route");
52+
}
53+
}
54+
55+
static void server_apply_keys(int lsk, union tcp_addr *ips, size_t ips_nr)
56+
{
57+
size_t i;
58+
59+
for (i = 0; i < ips_nr; i++) {
60+
union tcp_addr *p = (union tcp_addr *)&ips[i];
61+
62+
if (test_add_key(lsk, DEFAULT_TEST_PASSWORD, *p, -1, 100, 100))
63+
test_error("setsockopt(TCP_AO)");
64+
}
65+
}
66+
67+
static const size_t nr_keys[] = { 512, 1024, 2048, 4096, 8192 };
68+
static union tcp_addr *test_ips;
69+
70+
struct bench_stats {
71+
uint64_t min;
72+
uint64_t max;
73+
uint64_t nr;
74+
double mean;
75+
double s2;
76+
};
77+
78+
static struct bench_tests {
79+
struct bench_stats delete_last_key;
80+
struct bench_stats add_key;
81+
struct bench_stats delete_rand_key;
82+
struct bench_stats connect_last_key;
83+
struct bench_stats connect_rand_key;
84+
struct bench_stats delete_async;
85+
} bench_results[ARRAY_SIZE(nr_keys)];
86+
87+
#define NSEC_PER_SEC 1000000000ULL
88+
89+
static void measure_call(struct bench_stats *st,
90+
void (*f)(int, void *), int sk, void *arg)
91+
{
92+
struct timespec start = {}, end = {};
93+
double delta;
94+
uint64_t nsec;
95+
96+
if (clock_gettime(CLOCK_MONOTONIC, &start))
97+
test_error("clock_gettime()");
98+
99+
f(sk, arg);
100+
101+
if (clock_gettime(CLOCK_MONOTONIC, &end))
102+
test_error("clock_gettime()");
103+
104+
nsec = (end.tv_sec - start.tv_sec) * NSEC_PER_SEC;
105+
if (end.tv_nsec >= start.tv_nsec)
106+
nsec += end.tv_nsec - start.tv_nsec;
107+
else
108+
nsec -= start.tv_nsec - end.tv_nsec;
109+
110+
if (st->nr == 0) {
111+
st->min = st->max = nsec;
112+
} else {
113+
if (st->min > nsec)
114+
st->min = nsec;
115+
if (st->max < nsec)
116+
st->max = nsec;
117+
}
118+
119+
/* Welford-Knuth algorithm */
120+
st->nr++;
121+
delta = (double)nsec - st->mean;
122+
st->mean += delta / st->nr;
123+
st->s2 += delta * ((double)nsec - st->mean);
124+
}
125+
126+
static void delete_mkt(int sk, void *arg)
127+
{
128+
struct tcp_ao_del *ao = arg;
129+
130+
if (setsockopt(sk, IPPROTO_TCP, TCP_AO_DEL_KEY, ao, sizeof(*ao)))
131+
test_error("setsockopt(TCP_AO_DEL_KEY)");
132+
}
133+
134+
static void add_back_mkt(int sk, void *arg)
135+
{
136+
union tcp_addr *p = arg;
137+
138+
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, *p, -1, 100, 100))
139+
test_error("setsockopt(TCP_AO)");
140+
}
141+
142+
static void bench_delete(int lsk, struct bench_stats *add,
143+
struct bench_stats *del,
144+
union tcp_addr *ips, size_t ips_nr,
145+
bool rand_order, bool async)
146+
{
147+
struct tcp_ao_del ao_del = {};
148+
union tcp_addr *p;
149+
size_t i;
150+
151+
ao_del.sndid = 100;
152+
ao_del.rcvid = 100;
153+
ao_del.del_async = !!async;
154+
ao_del.prefix = DEFAULT_TEST_PREFIX;
155+
156+
/* Remove the first added */
157+
p = (union tcp_addr *)&ips[0];
158+
tcp_addr_to_sockaddr_in(&ao_del.addr, p, 0);
159+
160+
for (i = 0; i < BENCH_NR_ITERS; i++) {
161+
measure_call(del, delete_mkt, lsk, (void *)&ao_del);
162+
163+
/* Restore it back */
164+
measure_call(add, add_back_mkt, lsk, (void *)p);
165+
166+
/*
167+
* Slowest for FILO-linked-list:
168+
* on (i) iteration removing ips[i] element. When it gets
169+
* added to the list back - it becomes first to fetch, so
170+
* on (i + 1) iteration go to ips[i + 1] element.
171+
*/
172+
if (rand_order)
173+
p = (union tcp_addr *)&ips[rand() % ips_nr];
174+
else
175+
p = (union tcp_addr *)&ips[i % ips_nr];
176+
tcp_addr_to_sockaddr_in(&ao_del.addr, p, 0);
177+
}
178+
}
179+
180+
static void bench_connect_srv(int lsk, union tcp_addr *ips, size_t ips_nr)
181+
{
182+
size_t i;
183+
184+
for (i = 0; i < BENCH_NR_ITERS; i++) {
185+
int sk;
186+
187+
synchronize_threads();
188+
189+
if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0))
190+
test_error("test_wait_fd()");
191+
192+
sk = accept(lsk, NULL, NULL);
193+
if (sk < 0)
194+
test_error("accept()");
195+
196+
close(sk);
197+
}
198+
}
199+
200+
static void test_print_stats(const char *desc, size_t nr, struct bench_stats *bs)
201+
{
202+
test_ok("%-20s\t%zu keys: min=%" PRIu64 "ms max=%" PRIu64 "ms mean=%gms stddev=%g",
203+
desc, nr, bs->min / 1000000, bs->max / 1000000,
204+
bs->mean / 1000000, sqrt((bs->mean / 1000000) / bs->nr));
205+
}
206+
207+
static void *server_fn(void *arg)
208+
{
209+
size_t i;
210+
211+
for (i = 0; i < ARRAY_SIZE(nr_keys); i++) {
212+
struct bench_tests *bt = &bench_results[i];
213+
int lsk;
214+
215+
test_ips = malloc(nr_keys[i] * sizeof(union tcp_addr));
216+
if (!test_ips)
217+
test_error("malloc()");
218+
219+
lsk = test_listen_socket(this_ip_addr, test_server_port + i, 1);
220+
221+
gen_test_ips(test_ips, nr_keys[i], false);
222+
test_add_routes(test_ips, nr_keys[i]);
223+
test_set_optmem(KERNEL_TCP_AO_KEY_SZ_ROUND_UP * nr_keys[i]);
224+
server_apply_keys(lsk, test_ips, nr_keys[i]);
225+
226+
synchronize_threads();
227+
bench_connect_srv(lsk, test_ips, nr_keys[i]);
228+
bench_connect_srv(lsk, test_ips, nr_keys[i]);
229+
230+
/* The worst case for FILO-list */
231+
bench_delete(lsk, &bt->add_key, &bt->delete_last_key,
232+
test_ips, nr_keys[i], false, false);
233+
test_print_stats("Add a new key",
234+
nr_keys[i], &bt->add_key);
235+
test_print_stats("Delete: worst case",
236+
nr_keys[i], &bt->delete_last_key);
237+
238+
bench_delete(lsk, &bt->add_key, &bt->delete_rand_key,
239+
test_ips, nr_keys[i], true, false);
240+
test_print_stats("Delete: random-search",
241+
nr_keys[i], &bt->delete_rand_key);
242+
243+
bench_delete(lsk, &bt->add_key, &bt->delete_async,
244+
test_ips, nr_keys[i], false, true);
245+
test_print_stats("Delete: async", nr_keys[i], &bt->delete_async);
246+
247+
free(test_ips);
248+
close(lsk);
249+
}
250+
251+
return NULL;
252+
}
253+
254+
static void connect_client(int sk, void *arg)
255+
{
256+
size_t *p = arg;
257+
258+
if (test_connect_socket(sk, this_ip_dest, test_server_port + *p) <= 0)
259+
test_error("failed to connect()");
260+
}
261+
262+
static void client_addr_setup(int sk, union tcp_addr taddr)
263+
{
264+
#ifdef IPV6_TEST
265+
struct sockaddr_in6 addr = {
266+
.sin6_family = AF_INET6,
267+
.sin6_port = 0,
268+
.sin6_addr = taddr.a6,
269+
};
270+
#else
271+
struct sockaddr_in addr = {
272+
.sin_family = AF_INET,
273+
.sin_port = 0,
274+
.sin_addr = taddr.a4,
275+
};
276+
#endif
277+
int ret;
278+
279+
ret = ip_addr_add(veth_name, TEST_FAMILY, taddr, TEST_PREFIX);
280+
if (ret && ret != -EEXIST)
281+
test_error("Failed to add ip address");
282+
ret = ip_route_add(veth_name, TEST_FAMILY, taddr, this_ip_dest);
283+
if (ret && ret != -EEXIST)
284+
test_error("Failed to add route");
285+
286+
if (bind(sk, &addr, sizeof(addr)))
287+
test_error("bind()");
288+
}
289+
290+
static void bench_connect_client(size_t port_off, struct bench_tests *bt,
291+
union tcp_addr *ips, size_t ips_nr, bool rand_order)
292+
{
293+
struct bench_stats *con;
294+
union tcp_addr *p;
295+
size_t i;
296+
297+
if (rand_order)
298+
con = &bt->connect_rand_key;
299+
else
300+
con = &bt->connect_last_key;
301+
302+
p = (union tcp_addr *)&ips[0];
303+
304+
for (i = 0; i < BENCH_NR_ITERS; i++) {
305+
int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
306+
307+
if (sk < 0)
308+
test_error("socket()");
309+
310+
client_addr_setup(sk, *p);
311+
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest,
312+
-1, 100, 100))
313+
test_error("setsockopt(TCP_AO_ADD_KEY)");
314+
315+
synchronize_threads();
316+
317+
measure_call(con, connect_client, sk, (void *)&port_off);
318+
319+
close(sk);
320+
321+
/*
322+
* Slowest for FILO-linked-list:
323+
* on (i) iteration removing ips[i] element. When it gets
324+
* added to the list back - it becomes first to fetch, so
325+
* on (i + 1) iteration go to ips[i + 1] element.
326+
*/
327+
if (rand_order)
328+
p = (union tcp_addr *)&ips[rand() % ips_nr];
329+
else
330+
p = (union tcp_addr *)&ips[i % ips_nr];
331+
}
332+
}
333+
334+
static void *client_fn(void *arg)
335+
{
336+
size_t i;
337+
338+
for (i = 0; i < ARRAY_SIZE(nr_keys); i++) {
339+
struct bench_tests *bt = &bench_results[i];
340+
341+
synchronize_threads();
342+
bench_connect_client(i, bt, test_ips, nr_keys[i], false);
343+
test_print_stats("Connect: worst case",
344+
nr_keys[i], &bt->connect_last_key);
345+
346+
bench_connect_client(i, bt, test_ips, nr_keys[i], false);
347+
test_print_stats("Connect: random-search",
348+
nr_keys[i], &bt->connect_last_key);
349+
}
350+
synchronize_threads();
351+
return NULL;
352+
}
353+
354+
int main(int argc, char *argv[])
355+
{
356+
test_init(30, server_fn, client_fn);
357+
return 0;
358+
}

0 commit comments

Comments
 (0)