From: Siddharth Nayyar <sidnayyar@google.com>
To: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
John Fastabend <john.fastabend@gmail.com>,
Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Martin KaFai Lau <martin.lau@linux.dev>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
Quentin Monnet <qmo@kernel.org>, Shuah Khan <shuah@kernel.org>
Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org,
"Giuliano Procida" <gprocida@google.com>,
"Neill Kapron" <nkapron@google.com>,
"Thiébaud Weksteen" <tweek@google.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Siddharth Nayyar" <sidnayyar@google.com>
Subject: [PATCH RFC bpf-next 7/7] selftests/bpf: Add light skeleton struct_ops tests
Date: Fri, 06 Nov 2026 20:19:54 +0000 [thread overview]
Message-ID: <20261106-lskel-struct-ops-v1-7-a4486ffc79cb@google.com> (raw)
In-Reply-To: <20261106-lskel-struct-ops-v1-0-a4486ffc79cb@google.com>
Add selftests exercising BPF_MAP_TYPE_STRUCT_OPS support in light
skeletons across both vmlinux and kernel module BTF targets:
- struct_ops_vmlinux.c defines a SEC(".struct_ops.link")
tcp_congestion_ops map in vmlinux BTF, testing open/load, link
attachment, callback invocation over a loopback TCP connection,
and unregistration upon detach.
- struct_ops_mod.c defines two SEC(".struct_ops.link") maps targeting
bpf_testmod_ops in module BTF (including a reordered subset struct
bpf_testmod_ops___v2 sharing a callback), testing module BTF FD
resolution, runtime member offset/index lookup via
bpf_btf_get_struct_member_info(), scalar member copying, detach,
and per-map re-attachment.
Signed-off-by: Siddharth Nayyar <sidnayyar@google.com>
---
tools/testing/selftests/bpf/Makefile.skel | 3 +-
.../selftests/bpf/prog_tests/struct_ops_lskel.c | 122 +++++++++++++++++++++
tools/testing/selftests/bpf/progs/struct_ops_mod.c | 51 +++++++++
.../selftests/bpf/progs/struct_ops_vmlinux.c | 42 +++++++
4 files changed, 217 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/Makefile.skel b/tools/testing/selftests/bpf/Makefile.skel
index b8eaecc7f429..2726f9081e35 100644
--- a/tools/testing/selftests/bpf/Makefile.skel
+++ b/tools/testing/selftests/bpf/Makefile.skel
@@ -34,7 +34,8 @@ LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \
LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \
core_kern.c core_kern_overflow.c test_ringbuf.c \
test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c \
- test_ringbuf_overwrite.c callx_rodata.c
+ test_ringbuf_overwrite.c callx_rodata.c struct_ops_vmlinux.c \
+ struct_ops_mod.c
LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c
diff --git a/tools/testing/selftests/bpf/prog_tests/struct_ops_lskel.c b/tools/testing/selftests/bpf/prog_tests/struct_ops_lskel.c
new file mode 100644
index 000000000000..4e9bac460953
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/struct_ops_lskel.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "network_helpers.h"
+#include "struct_ops_vmlinux.lskel.h"
+#include "struct_ops_mod.lskel.h"
+
+static int set_tcp_ca_cb(int fd, void *opts)
+{
+ const char *tcp_ca = opts;
+
+ if (!ASSERT_OK(setsockopt(fd, IPPROTO_TCP, TCP_CONGESTION,
+ tcp_ca, strlen(tcp_ca)), "setsockopt"))
+ return -1;
+ return 0;
+}
+
+static void test_vmlinux(void)
+{
+ char tcp_ca[] = "bpf_lskel_ca";
+ struct network_helper_opts opts = {
+ .post_socket_cb = set_tcp_ca_cb,
+ .cb_opts = tcp_ca,
+ };
+ struct struct_ops_vmlinux_lskel *skel;
+ int srv_fd = -1, cli_fd = -1, sock_fd = -1;
+ int err;
+
+ skel = struct_ops_vmlinux_lskel__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ err = struct_ops_vmlinux_lskel__attach(skel);
+ if (!ASSERT_OK(err, "attach"))
+ goto cleanup;
+
+ srv_fd = start_server_str(AF_INET6, SOCK_STREAM, NULL, 0, &opts);
+ if (!ASSERT_NEQ(srv_fd, -1, "start_server_str"))
+ goto cleanup;
+
+ cli_fd = connect_to_fd_opts(srv_fd, &opts);
+ if (!ASSERT_NEQ(cli_fd, -1, "connect_to_fd_opts"))
+ goto cleanup;
+
+ ASSERT_GT(skel->bss->ca_init_cnt, 0, "ca_init_cnt");
+
+ close(cli_fd);
+ cli_fd = -1;
+ close(srv_fd);
+ srv_fd = -1;
+
+ struct_ops_vmlinux_lskel__detach(skel);
+ skel->links.lskel_ca_fd = 0;
+
+ /* Verify congestion control was unregistered on detach */
+ sock_fd = socket(AF_INET6, SOCK_STREAM, 0);
+ if (!ASSERT_GE(sock_fd, 0, "socket"))
+ goto cleanup;
+
+ err = setsockopt(sock_fd, IPPROTO_TCP, TCP_CONGESTION,
+ tcp_ca, strlen(tcp_ca));
+ ASSERT_EQ(err, -1, "setsockopt_after_detach");
+ ASSERT_EQ(errno, ENOENT, "errno_after_detach");
+
+cleanup:
+ if (sock_fd != -1)
+ close(sock_fd);
+ if (cli_fd != -1)
+ close(cli_fd);
+ if (srv_fd != -1)
+ close(srv_fd);
+ struct_ops_vmlinux_lskel__destroy(skel);
+}
+
+static void test_module(void)
+{
+ struct struct_ops_mod_lskel *skel;
+ int err, link_fd;
+
+ if (!env.has_testmod) {
+ test__skip();
+ return;
+ }
+
+ skel = struct_ops_mod_lskel__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ err = struct_ops_mod_lskel__attach(skel);
+ if (!ASSERT_OK(err, "attach"))
+ goto cleanup;
+
+ ASSERT_GT(skel->links.testmod_1_fd, 0, "testmod_1_fd");
+ ASSERT_GT(skel->links.testmod_2_fd, 0, "testmod_2_fd");
+ ASSERT_EQ(skel->bss->test_1_cnt, 2, "test_1_cnt");
+ ASSERT_EQ(skel->bss->testmod_1_result, 17, "testmod_1_result");
+ ASSERT_EQ(skel->bss->testmod_2_result, 28, "testmod_2_result");
+
+ struct_ops_mod_lskel__detach(skel);
+ skel->links.testmod_1_fd = 0;
+ skel->links.testmod_2_fd = 0;
+ memset(skel->bss, 0, sizeof(*skel->bss));
+
+ link_fd = struct_ops_mod_lskel__testmod_1__attach(skel);
+ if (!ASSERT_GT(link_fd, 0, "testmod_1_attach"))
+ goto cleanup;
+
+ ASSERT_EQ(skel->bss->test_1_cnt, 1, "reattach_test_1_cnt");
+ ASSERT_EQ(skel->bss->testmod_1_result, 17, "reattach_testmod_1_result");
+ ASSERT_EQ(skel->bss->testmod_2_result, 0, "reattach_testmod_2_result");
+
+cleanup:
+ struct_ops_mod_lskel__destroy(skel);
+}
+
+void test_struct_ops_lskel(void)
+{
+ if (test__start_subtest("vmlinux"))
+ test_vmlinux();
+ if (test__start_subtest("module"))
+ test_module();
+}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_mod.c b/tools/testing/selftests/bpf/progs/struct_ops_mod.c
new file mode 100644
index 000000000000..b1bd7429149d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/struct_ops_mod.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "../test_kmods/bpf_testmod.h"
+
+char _license[] SEC("license") = "GPL";
+
+int test_1_cnt = 0;
+int testmod_1_result = 0;
+int testmod_2_result = 0;
+
+SEC("struct_ops/test_1")
+int BPF_PROG(test_1)
+{
+ test_1_cnt++;
+ return 0;
+}
+
+SEC("struct_ops/test_2")
+void BPF_PROG(test_2, int a, int b)
+{
+ testmod_1_result = a + b;
+}
+
+SEC("struct_ops/test_2_v2")
+void BPF_PROG(test_2_v2, int a, int b)
+{
+ testmod_2_result = a * b;
+}
+
+SEC(".struct_ops.link")
+struct bpf_testmod_ops testmod_1 = {
+ .test_1 = (void *)test_1,
+ .test_2 = (void *)test_2,
+ .data = 13,
+};
+
+struct bpf_testmod_ops___v2 {
+ int data;
+ void (*test_2)(int a, int b);
+ int (*test_1)(void);
+};
+
+SEC(".struct_ops.link")
+struct bpf_testmod_ops___v2 testmod_2 = {
+ .data = 7,
+ .test_2 = (void *)test_2_v2,
+ .test_1 = (void *)test_1,
+};
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_vmlinux.c b/tools/testing/selftests/bpf/progs/struct_ops_vmlinux.c
new file mode 100644
index 000000000000..1d8b573ad799
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/struct_ops_vmlinux.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "bpf_tracing_net.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+int ca_init_cnt = 0;
+
+SEC("struct_ops/ca_lskel_init")
+void BPF_PROG(ca_lskel_init, struct sock *sk)
+{
+ ca_init_cnt++;
+}
+
+SEC("struct_ops/ca_lskel_cong_control")
+void BPF_PROG(ca_lskel_cong_control, struct sock *sk, __u32 ack, int flag,
+ const struct rate_sample *rs)
+{
+}
+
+SEC("struct_ops/ca_lskel_ssthresh")
+__u32 BPF_PROG(ca_lskel_ssthresh, struct sock *sk)
+{
+ return tcp_sk(sk)->snd_ssthresh;
+}
+
+SEC("struct_ops/ca_lskel_undo_cwnd")
+__u32 BPF_PROG(ca_lskel_undo_cwnd, struct sock *sk)
+{
+ return tcp_sk(sk)->snd_cwnd;
+}
+
+SEC(".struct_ops.link")
+struct tcp_congestion_ops lskel_ca = {
+ .init = (void *)ca_lskel_init,
+ .cong_control = (void *)ca_lskel_cong_control,
+ .ssthresh = (void *)ca_lskel_ssthresh,
+ .undo_cwnd = (void *)ca_lskel_undo_cwnd,
+ .name = "bpf_lskel_ca",
+};
--
2.56.0.455.g6c5a503b38-goog
next prev parent reply other threads:[~2026-11-06 20:20 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 20:19 [PATCH RFC bpf-next 0/7] bpf: Support struct_ops in light skeletons Siddharth Nayyar
2026-11-06 20:19 ` [PATCH RFC bpf-next 1/7] bpf: Add bpf_btf_get_struct_member_info() helper Siddharth Nayyar
2026-11-06 20:37 ` sashiko-bot
2026-11-06 20:56 ` bot+bpf-ci
2026-11-06 20:19 ` [PATCH RFC bpf-next 2/7] libbpf: Implement bpf_gen__init_kern_struct_ops() for light skeletons Siddharth Nayyar
2026-11-06 20:53 ` sashiko-bot
2026-11-06 20:56 ` bot+bpf-ci
2026-11-06 20:19 ` [PATCH RFC bpf-next 3/7] libbpf: Support BPF_MAP_TYPE_STRUCT_OPS in bpf_gen__map_create() Siddharth Nayyar
2026-11-06 20:33 ` sashiko-bot
2026-11-06 20:56 ` bot+bpf-ci
2026-11-06 20:19 ` [PATCH RFC bpf-next 4/7] libbpf: Support BPF_PROG_TYPE_STRUCT_OPS in bpf_gen__prog_load() Siddharth Nayyar
2026-11-06 20:56 ` bot+bpf-ci
2026-11-06 20:19 ` [PATCH RFC bpf-next 5/7] libbpf: Implement bpf_gen__struct_ops_update_elem() for light skeletons Siddharth Nayyar
2026-11-06 20:39 ` sashiko-bot
2026-11-06 20:56 ` bot+bpf-ci
2026-11-06 20:19 ` [PATCH RFC bpf-next 6/7] bpftool: Generate attach and detach for struct_ops in " Siddharth Nayyar
2026-11-06 20:19 ` Siddharth Nayyar [this message]
2026-11-09 19:46 ` [PATCH RFC bpf-next 0/7] bpf: Support " Ihor Solodrai
2026-11-09 21:39 ` Alexei Starovoitov
2026-11-10 13:08 ` Anton Protopopov
2026-11-10 13:12 ` Anton Protopopov
2026-11-11 7:35 ` Sid Nayyar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106-lskel-struct-ops-v1-7-a4486ffc79cb@google.com \
--to=sidnayyar@google.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=gprocida@google.com \
--cc=ihor.solodrai@linux.dev \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=mripard@kernel.org \
--cc=nkapron@google.com \
--cc=qmo@kernel.org \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=tweek@google.com \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.