Skip to content

Commit 331bbcf

Browse files
committed
selftests/bpf: Add tests for bucket resume logic in established sockets
Replicate the set of test cases used for UDP socket iterators to test similar scenarios for TCP established sockets. Signed-off-by: Jordan Rife <[email protected]>
1 parent b24b8f5 commit 331bbcf

File tree

1 file changed

+286
-0
lines changed

1 file changed

+286
-0
lines changed

tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,46 @@ static int get_nth_socket(int *fds, int fds_len, struct bpf_link *link, int n)
119119
return nth_sock_idx;
120120
}
121121

122+
static bool close_and_wait(int fd, struct bpf_link *link)
123+
{
124+
static const int us_per_ms = 1000;
125+
__u64 cookie = socket_cookie(fd);
126+
struct iter_out out;
127+
bool exists = true;
128+
int iter_fd, nread;
129+
int waits = 20; /* 2 seconds */
130+
131+
close(fd);
132+
133+
/* Wait for socket to disappear from the ehash table. */
134+
while (waits--) {
135+
exists = false;
136+
137+
iter_fd = bpf_iter_create(bpf_link__fd(link));
138+
if (!ASSERT_OK_FD(iter_fd, "bpf_iter_create"))
139+
break;
140+
141+
/* Is it still there? */
142+
do {
143+
nread = read(iter_fd, &out, sizeof(out));
144+
if (!ASSERT_GE(nread, 0, "nread")) {
145+
close(iter_fd);
146+
return false;
147+
}
148+
exists = nread && cookie == out.cookie;
149+
} while (!exists && nread);
150+
151+
close(iter_fd);
152+
153+
if (!exists)
154+
break;
155+
156+
usleep(100 * us_per_ms);
157+
}
158+
159+
return !exists;
160+
}
161+
122162
static int get_seen_count(int fd, struct sock_count counts[], int n)
123163
{
124164
__u64 cookie = socket_cookie(fd);
@@ -241,6 +281,42 @@ static void remove_seen(int family, int sock_type, const char *addr, __u16 port,
241281
counts_len);
242282
}
243283

284+
static void remove_seen_established(int family, int sock_type, const char *addr,
285+
__u16 port, int *listen_socks,
286+
int listen_socks_len, int *established_socks,
287+
int established_socks_len,
288+
struct sock_count *counts, int counts_len,
289+
struct bpf_link *link, int iter_fd)
290+
{
291+
int close_idx;
292+
293+
/* Iterate through all listening sockets. */
294+
read_n(iter_fd, listen_socks_len, counts, counts_len);
295+
296+
/* Make sure we saw all listening sockets exactly once. */
297+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
298+
counts, counts_len);
299+
300+
/* Leave one established socket. */
301+
read_n(iter_fd, established_socks_len - 1, counts, counts_len);
302+
303+
/* Close a socket we've already seen to remove it from the bucket. */
304+
close_idx = get_seen_socket(established_socks, counts, counts_len);
305+
if (!ASSERT_GE(close_idx, 0, "close_idx"))
306+
return;
307+
close(established_socks[close_idx]);
308+
established_socks[close_idx] = -1;
309+
310+
/* Iterate through the rest of the sockets. */
311+
read_n(iter_fd, -1, counts, counts_len);
312+
313+
/* Make sure the last socket wasn't skipped and that there were no
314+
* repeats.
315+
*/
316+
check_n_were_seen_once(established_socks, established_socks_len,
317+
established_socks_len - 1, counts, counts_len);
318+
}
319+
244320
static void remove_unseen(int family, int sock_type, const char *addr,
245321
__u16 port, int *socks, int socks_len,
246322
int *established_socks, int established_socks_len,
@@ -274,6 +350,50 @@ static void remove_unseen(int family, int sock_type, const char *addr,
274350
counts_len);
275351
}
276352

353+
static void remove_unseen_established(int family, int sock_type,
354+
const char *addr, __u16 port,
355+
int *listen_socks, int listen_socks_len,
356+
int *established_socks,
357+
int established_socks_len,
358+
struct sock_count *counts, int counts_len,
359+
struct bpf_link *link, int iter_fd)
360+
{
361+
int close_idx;
362+
363+
/* Iterate through all listening sockets. */
364+
read_n(iter_fd, listen_socks_len, counts, counts_len);
365+
366+
/* Make sure we saw all listening sockets exactly once. */
367+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
368+
counts, counts_len);
369+
370+
/* Iterate through the first established socket. */
371+
read_n(iter_fd, 1, counts, counts_len);
372+
373+
/* Make sure we saw one established socks. */
374+
check_n_were_seen_once(established_socks, established_socks_len, 1,
375+
counts, counts_len);
376+
377+
/* Close what would be the next socket in the bucket to exercise the
378+
* condition where we need to skip past the first cookie we remembered.
379+
*/
380+
close_idx = get_nth_socket(established_socks, established_socks_len,
381+
link, listen_socks_len + 1);
382+
if (!ASSERT_GE(close_idx, 0, "close_idx"))
383+
return;
384+
close(established_socks[close_idx]);
385+
established_socks[close_idx] = -1;
386+
387+
/* Iterate through the rest of the sockets. */
388+
read_n(iter_fd, -1, counts, counts_len);
389+
390+
/* Make sure the remaining sockets were seen exactly once and that we
391+
* didn't repeat the socket that was already seen.
392+
*/
393+
check_n_were_seen_once(established_socks, established_socks_len,
394+
established_socks_len - 1, counts, counts_len);
395+
}
396+
277397
static void remove_all(int family, int sock_type, const char *addr,
278398
__u16 port, int *socks, int socks_len,
279399
int *established_socks, int established_socks_len,
@@ -303,6 +423,48 @@ static void remove_all(int family, int sock_type, const char *addr,
303423
ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n");
304424
}
305425

426+
static void remove_all_established(int family, int sock_type, const char *addr,
427+
__u16 port, int *listen_socks,
428+
int listen_socks_len, int *established_socks,
429+
int established_socks_len,
430+
struct sock_count *counts, int counts_len,
431+
struct bpf_link *link, int iter_fd)
432+
{
433+
int close_idx, i;
434+
435+
/* Iterate through all listening sockets. */
436+
read_n(iter_fd, listen_socks_len, counts, counts_len);
437+
438+
/* Make sure we saw all listening sockets exactly once. */
439+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
440+
counts, counts_len);
441+
442+
/* Iterate through the first established socket. */
443+
read_n(iter_fd, 1, counts, counts_len);
444+
445+
/* Make sure we saw one established socks. */
446+
check_n_were_seen_once(established_socks, established_socks_len, 1,
447+
counts, counts_len);
448+
449+
/* Close all remaining sockets to exhaust the list of saved cookies and
450+
* exit without putting any sockets into the batch on the next read.
451+
*/
452+
for (i = 0; i < established_socks_len - 1; i++) {
453+
close_idx = get_nth_socket(established_socks,
454+
established_socks_len, link,
455+
listen_socks_len + 1);
456+
if (!ASSERT_GE(close_idx, 0, "close_idx"))
457+
return;
458+
if (!ASSERT_TRUE(close_and_wait(established_socks[close_idx],
459+
link), "close_and_wait"))
460+
return;
461+
established_socks[close_idx] = -1;
462+
}
463+
464+
/* Make sure there are no more sockets returned */
465+
ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n");
466+
}
467+
306468
static void add_some(int family, int sock_type, const char *addr, __u16 port,
307469
int *socks, int socks_len, int *established_socks,
308470
int established_socks_len, struct sock_count *counts,
@@ -333,6 +495,49 @@ static void add_some(int family, int sock_type, const char *addr, __u16 port,
333495
free_fds(new_socks, socks_len);
334496
}
335497

498+
static void add_some_established(int family, int sock_type, const char *addr,
499+
__u16 port, int *listen_socks,
500+
int listen_socks_len, int *established_socks,
501+
int established_socks_len,
502+
struct sock_count *counts,
503+
int counts_len, struct bpf_link *link,
504+
int iter_fd)
505+
{
506+
int *new_socks = NULL;
507+
508+
/* Iterate through all listening sockets. */
509+
read_n(iter_fd, listen_socks_len, counts, counts_len);
510+
511+
/* Make sure we saw all listening sockets exactly once. */
512+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
513+
counts, counts_len);
514+
515+
/* Iterate through the first established_socks_len - 1 sockets. */
516+
read_n(iter_fd, established_socks_len - 1, counts, counts_len);
517+
518+
/* Make sure we saw established_socks_len - 1 sockets exactly once. */
519+
check_n_were_seen_once(established_socks, established_socks_len,
520+
established_socks_len - 1, counts, counts_len);
521+
522+
/* Double the number of established sockets in the bucket. */
523+
new_socks = connect_to_server(family, sock_type, addr, port,
524+
established_socks_len / 2, listen_socks,
525+
listen_socks_len);
526+
if (!ASSERT_OK_PTR(new_socks, "connect_to_server"))
527+
goto done;
528+
529+
/* Iterate through the rest of the sockets. */
530+
read_n(iter_fd, -1, counts, counts_len);
531+
532+
/* Make sure each of the original sockets was seen exactly once. */
533+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
534+
counts, counts_len);
535+
check_n_were_seen_once(established_socks, established_socks_len,
536+
established_socks_len, counts, counts_len);
537+
done:
538+
free_fds(new_socks, established_socks_len);
539+
}
540+
336541
static void force_realloc(int family, int sock_type, const char *addr,
337542
__u16 port, int *socks, int socks_len,
338543
int *established_socks, int established_socks_len,
@@ -362,6 +567,24 @@ static void force_realloc(int family, int sock_type, const char *addr,
362567
free_fds(new_socks, socks_len);
363568
}
364569

570+
static void force_realloc_established(int family, int sock_type,
571+
const char *addr, __u16 port,
572+
int *listen_socks, int listen_socks_len,
573+
int *established_socks,
574+
int established_socks_len,
575+
struct sock_count *counts, int counts_len,
576+
struct bpf_link *link, int iter_fd)
577+
{
578+
/* Iterate through all sockets to trigger a realloc. */
579+
read_n(iter_fd, -1, counts, counts_len);
580+
581+
/* Make sure each socket was seen exactly once. */
582+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
583+
counts, counts_len);
584+
check_n_were_seen_once(established_socks, established_socks_len,
585+
established_socks_len, counts, counts_len);
586+
}
587+
365588
struct test_case {
366589
void (*test)(int family, int sock_type, const char *addr, __u16 port,
367590
int *socks, int socks_len, int *established_socks,
@@ -471,6 +694,69 @@ static struct test_case resume_tests[] = {
471694
.family = AF_INET6,
472695
.test = force_realloc,
473696
},
697+
{
698+
.description = "tcp: resume after removing a seen socket (established)",
699+
/* Force all established sockets into one bucket */
700+
.ehash_buckets = 1,
701+
.connections = nr_soreuse,
702+
.init_socks = nr_soreuse,
703+
/* Room for connect()ed and accept()ed sockets */
704+
.max_socks = nr_soreuse * 3,
705+
.sock_type = SOCK_STREAM,
706+
.family = AF_INET6,
707+
.test = remove_seen_established,
708+
},
709+
{
710+
.description = "tcp: resume after removing one unseen socket (established)",
711+
/* Force all established sockets into one bucket */
712+
.ehash_buckets = 1,
713+
.connections = nr_soreuse,
714+
.init_socks = nr_soreuse,
715+
/* Room for connect()ed and accept()ed sockets */
716+
.max_socks = nr_soreuse * 3,
717+
.sock_type = SOCK_STREAM,
718+
.family = AF_INET6,
719+
.test = remove_unseen_established,
720+
},
721+
{
722+
.description = "tcp: resume after removing all unseen sockets (established)",
723+
/* Force all established sockets into one bucket */
724+
.ehash_buckets = 1,
725+
.connections = nr_soreuse,
726+
.init_socks = nr_soreuse,
727+
/* Room for connect()ed and accept()ed sockets */
728+
.max_socks = nr_soreuse * 3,
729+
.sock_type = SOCK_STREAM,
730+
.family = AF_INET6,
731+
.test = remove_all_established,
732+
},
733+
{
734+
.description = "tcp: resume after adding a few sockets (established)",
735+
/* Force all established sockets into one bucket */
736+
.ehash_buckets = 1,
737+
.connections = nr_soreuse,
738+
.init_socks = nr_soreuse,
739+
/* Room for connect()ed and accept()ed sockets */
740+
.max_socks = nr_soreuse * 3,
741+
.sock_type = SOCK_STREAM,
742+
.family = AF_INET6,
743+
.test = add_some_established,
744+
},
745+
{
746+
.description = "tcp: force a realloc to occur (established)",
747+
/* Force all established sockets into one bucket */
748+
.ehash_buckets = 1,
749+
/* Bucket size will need to double when going from listening to
750+
* established sockets.
751+
*/
752+
.connections = init_batch_size,
753+
.init_socks = nr_soreuse,
754+
/* Room for connect()ed and accept()ed sockets */
755+
.max_socks = nr_soreuse + (init_batch_size * 2),
756+
.sock_type = SOCK_STREAM,
757+
.family = AF_INET6,
758+
.test = force_realloc_established,
759+
},
474760
};
475761

476762
static void do_resume_test(struct test_case *tc)

0 commit comments

Comments
 (0)