Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
selftests/bpf: Test switching TCP Congestion Control algorithms.
Browse files Browse the repository at this point in the history
Create a pair of sockets that utilize the congestion control algorithm
under a particular name. Then switch up this congestion control
algorithm to another implementation and check whether newly created
connections using the same cc name now run the new implementation.

Also, try to update a link with a struct_ops that is without
BPF_F_LINK or with a wrong or different name.  These cases should fail
due to the violation of assumptions.  To update a bpf_link of a
struct_ops, it must be replaced with another struct_ops that is
identical in type and name and has the BPF_F_LINK flag.

The other test case is to create links from the same struct_ops more
than once.  It makes sure a struct_ops can be used repeatly.

Signed-off-by: Kui-Feng Lee <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Martin KaFai Lau <[email protected]>
  • Loading branch information
Kui-Feng Lee authored and Martin KaFai Lau committed Mar 23, 2023
1 parent 809a69d commit 06da9f3
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 0 deletions.
160 changes: 160 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "bpf_dctcp.skel.h"
#include "bpf_cubic.skel.h"
#include "bpf_tcp_nogpl.skel.h"
#include "tcp_ca_update.skel.h"
#include "bpf_dctcp_release.skel.h"
#include "tcp_ca_write_sk_pacing.skel.h"
#include "tcp_ca_incompl_cong_ops.skel.h"
Expand Down Expand Up @@ -381,6 +382,155 @@ static void test_unsupp_cong_op(void)
libbpf_set_print(old_print_fn);
}

static void test_update_ca(void)
{
struct tcp_ca_update *skel;
struct bpf_link *link;
int saved_ca1_cnt;
int err;

skel = tcp_ca_update__open_and_load();
if (!ASSERT_OK_PTR(skel, "open"))
return;

link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);
ASSERT_OK_PTR(link, "attach_struct_ops");

do_test("tcp_ca_update", NULL);
saved_ca1_cnt = skel->bss->ca1_cnt;
ASSERT_GT(saved_ca1_cnt, 0, "ca1_ca1_cnt");

err = bpf_link__update_map(link, skel->maps.ca_update_2);
ASSERT_OK(err, "update_map");

do_test("tcp_ca_update", NULL);
ASSERT_EQ(skel->bss->ca1_cnt, saved_ca1_cnt, "ca2_ca1_cnt");
ASSERT_GT(skel->bss->ca2_cnt, 0, "ca2_ca2_cnt");

bpf_link__destroy(link);
tcp_ca_update__destroy(skel);
}

static void test_update_wrong(void)
{
struct tcp_ca_update *skel;
struct bpf_link *link;
int saved_ca1_cnt;
int err;

skel = tcp_ca_update__open_and_load();
if (!ASSERT_OK_PTR(skel, "open"))
return;

link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);
ASSERT_OK_PTR(link, "attach_struct_ops");

do_test("tcp_ca_update", NULL);
saved_ca1_cnt = skel->bss->ca1_cnt;
ASSERT_GT(saved_ca1_cnt, 0, "ca1_ca1_cnt");

err = bpf_link__update_map(link, skel->maps.ca_wrong);
ASSERT_ERR(err, "update_map");

do_test("tcp_ca_update", NULL);
ASSERT_GT(skel->bss->ca1_cnt, saved_ca1_cnt, "ca2_ca1_cnt");

bpf_link__destroy(link);
tcp_ca_update__destroy(skel);
}

static void test_mixed_links(void)
{
struct tcp_ca_update *skel;
struct bpf_link *link, *link_nl;
int err;

skel = tcp_ca_update__open_and_load();
if (!ASSERT_OK_PTR(skel, "open"))
return;

link_nl = bpf_map__attach_struct_ops(skel->maps.ca_no_link);
ASSERT_OK_PTR(link_nl, "attach_struct_ops_nl");

link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);
ASSERT_OK_PTR(link, "attach_struct_ops");

do_test("tcp_ca_update", NULL);
ASSERT_GT(skel->bss->ca1_cnt, 0, "ca1_ca1_cnt");

err = bpf_link__update_map(link, skel->maps.ca_no_link);
ASSERT_ERR(err, "update_map");

bpf_link__destroy(link);
bpf_link__destroy(link_nl);
tcp_ca_update__destroy(skel);
}

static void test_multi_links(void)
{
struct tcp_ca_update *skel;
struct bpf_link *link;

skel = tcp_ca_update__open_and_load();
if (!ASSERT_OK_PTR(skel, "open"))
return;

link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);
ASSERT_OK_PTR(link, "attach_struct_ops_1st");
bpf_link__destroy(link);

/* A map should be able to be used to create links multiple
* times.
*/
link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);
ASSERT_OK_PTR(link, "attach_struct_ops_2nd");
bpf_link__destroy(link);

tcp_ca_update__destroy(skel);
}

static void test_link_replace(void)
{
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, opts);
struct tcp_ca_update *skel;
struct bpf_link *link;
int err;

skel = tcp_ca_update__open_and_load();
if (!ASSERT_OK_PTR(skel, "open"))
return;

link = bpf_map__attach_struct_ops(skel->maps.ca_update_1);
ASSERT_OK_PTR(link, "attach_struct_ops_1st");
bpf_link__destroy(link);

link = bpf_map__attach_struct_ops(skel->maps.ca_update_2);
ASSERT_OK_PTR(link, "attach_struct_ops_2nd");

/* BPF_F_REPLACE with a wrong old map Fd. It should fail!
*
* With BPF_F_REPLACE, the link should be updated only if the
* old map fd given here matches the map backing the link.
*/
opts.old_map_fd = bpf_map__fd(skel->maps.ca_update_1);
opts.flags = BPF_F_REPLACE;
err = bpf_link_update(bpf_link__fd(link),
bpf_map__fd(skel->maps.ca_update_1),
&opts);
ASSERT_ERR(err, "bpf_link_update_fail");

/* BPF_F_REPLACE with a correct old map Fd. It should success! */
opts.old_map_fd = bpf_map__fd(skel->maps.ca_update_2);
err = bpf_link_update(bpf_link__fd(link),
bpf_map__fd(skel->maps.ca_update_1),
&opts);
ASSERT_OK(err, "bpf_link_update_success");

bpf_link__destroy(link);

tcp_ca_update__destroy(skel);
}

void test_bpf_tcp_ca(void)
{
if (test__start_subtest("dctcp"))
Expand All @@ -399,4 +549,14 @@ void test_bpf_tcp_ca(void)
test_incompl_cong_ops();
if (test__start_subtest("unsupp_cong_op"))
test_unsupp_cong_op();
if (test__start_subtest("update_ca"))
test_update_ca();
if (test__start_subtest("update_wrong"))
test_update_wrong();
if (test__start_subtest("mixed_links"))
test_mixed_links();
if (test__start_subtest("multi_links"))
test_multi_links();
if (test__start_subtest("link_replace"))
test_link_replace();
}
80 changes: 80 additions & 0 deletions tools/testing/selftests/bpf/progs/tcp_ca_update.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0

#include "vmlinux.h"

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char _license[] SEC("license") = "GPL";

int ca1_cnt = 0;
int ca2_cnt = 0;

static inline struct tcp_sock *tcp_sk(const struct sock *sk)
{
return (struct tcp_sock *)sk;
}

SEC("struct_ops/ca_update_1_init")
void BPF_PROG(ca_update_1_init, struct sock *sk)
{
ca1_cnt++;
}

SEC("struct_ops/ca_update_2_init")
void BPF_PROG(ca_update_2_init, struct sock *sk)
{
ca2_cnt++;
}

SEC("struct_ops/ca_update_cong_control")
void BPF_PROG(ca_update_cong_control, struct sock *sk,
const struct rate_sample *rs)
{
}

SEC("struct_ops/ca_update_ssthresh")
__u32 BPF_PROG(ca_update_ssthresh, struct sock *sk)
{
return tcp_sk(sk)->snd_ssthresh;
}

SEC("struct_ops/ca_update_undo_cwnd")
__u32 BPF_PROG(ca_update_undo_cwnd, struct sock *sk)
{
return tcp_sk(sk)->snd_cwnd;
}

SEC(".struct_ops.link")
struct tcp_congestion_ops ca_update_1 = {
.init = (void *)ca_update_1_init,
.cong_control = (void *)ca_update_cong_control,
.ssthresh = (void *)ca_update_ssthresh,
.undo_cwnd = (void *)ca_update_undo_cwnd,
.name = "tcp_ca_update",
};

SEC(".struct_ops.link")
struct tcp_congestion_ops ca_update_2 = {
.init = (void *)ca_update_2_init,
.cong_control = (void *)ca_update_cong_control,
.ssthresh = (void *)ca_update_ssthresh,
.undo_cwnd = (void *)ca_update_undo_cwnd,
.name = "tcp_ca_update",
};

SEC(".struct_ops.link")
struct tcp_congestion_ops ca_wrong = {
.cong_control = (void *)ca_update_cong_control,
.ssthresh = (void *)ca_update_ssthresh,
.undo_cwnd = (void *)ca_update_undo_cwnd,
.name = "tcp_ca_wrong",
};

SEC(".struct_ops")
struct tcp_congestion_ops ca_no_link = {
.cong_control = (void *)ca_update_cong_control,
.ssthresh = (void *)ca_update_ssthresh,
.undo_cwnd = (void *)ca_update_undo_cwnd,
.name = "tcp_ca_no_link",
};

0 comments on commit 06da9f3

Please sign in to comment.