Extend the suite of tests that exercise edge cases around iteration over multiple sockets in the same bucket to cover socket hashes using key prefix filtering. Signed-off-by: Jordan Rife --- .../bpf/prog_tests/sock_iter_batch.c | 119 +++++++++++++++++- .../selftests/bpf/progs/sock_iter_batch.c | 31 +++++ 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c index e6fc4fd994f9..2034ddfdf134 100644 --- a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c +++ b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c @@ -10,6 +10,7 @@ #define TEST_CHILD_NS "sock_iter_batch_child_netns" static const int init_batch_size = 16; +static const __u32 key_prefix = 1; static const int nr_soreuse = 4; struct iter_out { @@ -255,6 +256,31 @@ static int *connect_to_server(int family, int sock_type, const char *addr, return NULL; } +static int insert_sockets_hash(struct bpf_map *sock_map, __u32 first_id, + int *sock_fds, int sock_fds_len) +{ + int map_fd = bpf_map__fd(sock_map); + struct { + __u32 bucket_key; + __u32 id; + } key = { + .bucket_key = key_prefix, + }; + __s64 sfd; + int ret; + __u32 i; + + for (i = 0; i < sock_fds_len; i++) { + sfd = sock_fds[i]; + key.id = first_id + i; + ret = bpf_map_update_elem(map_fd, &key, &sfd, BPF_NOEXIST); + if (!ASSERT_OK(ret, "map_update")) + return -1; + } + + return 0; +} + static void remove_seen(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, int *established_socks, int established_socks_len, struct sock_count *counts, @@ -609,6 +635,7 @@ struct test_case { int init_socks; int max_socks; int sock_type; + bool fill_map; int family; }; @@ -660,6 +687,33 @@ static struct test_case resume_tests[] = { .family = AF_INET6, .test = force_realloc, }, + { + .description = "sockhash: udp: resume after removing a seen socket", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_DGRAM, + .family = AF_INET6, + .test = remove_seen, + .fill_map = true, + }, + { + .description = "sockhash: udp: resume after removing one unseen socket", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_DGRAM, + .family = AF_INET6, + .test = remove_unseen, + .fill_map = true, + }, + { + .description = "sockhash: udp: resume after removing all unseen sockets", + .init_socks = nr_soreuse, + .max_socks = nr_soreuse, + .sock_type = SOCK_DGRAM, + .family = AF_INET6, + .test = remove_all, + .fill_map = true, + }, { .description = "tcp: resume after removing a seen socket (listening)", .init_socks = nr_soreuse, @@ -770,13 +824,49 @@ static struct test_case resume_tests[] = { .family = AF_INET6, .test = force_realloc_established, }, + { + .description = "sockhash: tcp: resume after removing a seen socket", + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_seen_established, + .fill_map = true, + }, + { + .description = "sockhash: tcp: resume after removing one unseen socket", + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_unseen_established, + .fill_map = true, + }, + { + .description = "sockhash: tcp: resume after removing all unseen sockets", + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_all_established, + .fill_map = true, + }, }; static void do_resume_test(struct test_case *tc) { + DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); + union bpf_iter_link_info linfo = {}; struct sock_iter_batch *skel = NULL; struct sock_count *counts = NULL; static const __u16 port = 10001; + struct bpf_program *prog = NULL; struct nstoken *nstoken = NULL; struct bpf_link *link = NULL; int *established_fds = NULL; @@ -825,10 +915,33 @@ static void do_resume_test(struct test_case *tc) if (!ASSERT_OK(err, "sock_iter_batch__load")) goto done; - link = bpf_program__attach_iter(tc->sock_type == SOCK_STREAM ? + if (tc->fill_map) { + /* Established sockets must be inserted first so that all + * listening sockets will be seen first during iteration. + */ + if (!ASSERT_OK(insert_sockets_hash(skel->maps.sockets, 0, + established_fds, + tc->connections*2), + "insert_sockets_hash")) + goto done; + if (!ASSERT_OK(insert_sockets_hash(skel->maps.sockets, + tc->connections*2, fds, + tc->init_socks), + "insert_sockets_hash")) + goto done; + linfo.map.map_fd = bpf_map__fd(skel->maps.sockets); + linfo.map.sock_hash.key_prefix = (__u64)(void *)&key_prefix; + linfo.map.sock_hash.key_prefix_len = sizeof(key_prefix); + opts.link_info = &linfo; + opts.link_info_len = sizeof(linfo); + prog = skel->progs.iter_sockmap; + } else { + prog = tc->sock_type == SOCK_STREAM ? skel->progs.iter_tcp_soreuse : - skel->progs.iter_udp_soreuse, - NULL); + skel->progs.iter_udp_soreuse; + } + + link = bpf_program__attach_iter(prog, &opts); if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter")) goto done; diff --git a/tools/testing/selftests/bpf/progs/sock_iter_batch.c b/tools/testing/selftests/bpf/progs/sock_iter_batch.c index 77966ded5467..a19581f19eda 100644 --- a/tools/testing/selftests/bpf/progs/sock_iter_batch.c +++ b/tools/testing/selftests/bpf/progs/sock_iter_batch.c @@ -130,4 +130,35 @@ int iter_udp_soreuse(struct bpf_iter__udp *ctx) return 0; } +struct sock_hash_key { + __u32 bucket_key; + __u32 id; +}; + +struct { + __uint(type, BPF_MAP_TYPE_SOCKHASH); + __uint(max_entries, 16); + __ulong(map_extra, offsetof(struct sock_hash_key, id)); + __type(key, sizeof(struct sock_hash_key)); + __type(value, __u64); +} sockets SEC(".maps"); + +SEC("iter/sockmap") +int iter_sockmap(struct bpf_iter__sockmap *ctx) +{ + struct sock *sk = ctx->sk; + __u32 *key = ctx->key; + __u64 sock_cookie; + int idx = 0; + + if (!key || !sk) + return 0; + + sock_cookie = bpf_get_socket_cookie(sk); + bpf_seq_write(ctx->meta->seq, &idx, sizeof(idx)); + bpf_seq_write(ctx->meta->seq, &sock_cookie, sizeof(sock_cookie)); + + return 0; +} + char _license[] SEC("license") = "GPL"; -- 2.43.0