Skip to content

Commit be6123a

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 9f3d4fa commit be6123a

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,44 @@ 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+
iter_fd = bpf_iter_create(bpf_link__fd(link));
136+
if (!ASSERT_OK_FD(iter_fd, "bpf_iter_create"))
137+
return false;
138+
139+
/* Is it still there? */
140+
do {
141+
nread = read(iter_fd, &out, sizeof(out));
142+
if (!ASSERT_GE(nread, 0, "nread")) {
143+
close(iter_fd);
144+
return false;
145+
}
146+
exists = nread && cookie == out.cookie;
147+
} while (!exists && nread);
148+
149+
close(iter_fd);
150+
151+
if (!exists)
152+
break;
153+
154+
usleep(100 * us_per_ms);
155+
}
156+
157+
return !exists;
158+
}
159+
122160
static int get_seen_count(int fd, struct sock_count counts[], int n)
123161
{
124162
__u64 cookie = socket_cookie(fd);
@@ -241,6 +279,43 @@ static void remove_seen(int family, int sock_type, const char *addr, __u16 port,
241279
counts_len);
242280
}
243281

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

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

427+
static void remove_all_established(int family, int sock_type, const char *addr,
428+
__u16 port, int *listen_socks,
429+
int listen_socks_len, int *established_socks,
430+
int established_socks_len,
431+
struct sock_count *counts, int counts_len,
432+
struct bpf_link *link, int iter_fd)
433+
{
434+
int close_idx, i;
435+
436+
/* Iterate through all listening sockets. */
437+
read_n(iter_fd, listen_socks_len, counts, counts_len);
438+
439+
/* Make sure we saw all listening sockets exactly once. */
440+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
441+
counts, counts_len);
442+
443+
/* Iterate through the first established socket. */
444+
read_n(iter_fd, 1, counts, counts_len);
445+
446+
/* Make sure we saw one established socks. */
447+
check_n_were_seen_once(established_socks, established_socks_len, 1,
448+
counts, counts_len);
449+
450+
/* Close all remaining sockets to exhaust the list of saved cookies and
451+
* exit without putting any sockets into the batch on the next read.
452+
*/
453+
for (i = 0; i < established_socks_len - 1; i++) {
454+
close_idx = get_nth_socket(established_socks,
455+
established_socks_len, link,
456+
listen_socks_len + 1);
457+
if (!ASSERT_GE(close_idx, 0, "close_idx"))
458+
return;
459+
ASSERT_TRUE(close_and_wait(established_socks[close_idx], link),
460+
"close_and_wait");
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)