All of lore.kernel.org
 help / color / mirror / Atom feed
From: thinker.li@gmail.com
To: bpf@vger.kernel.org, ast@kernel.org, martin.lau@linux.dev,
	song@kernel.org, kernel-team@meta.com, andrii@kernel.org,
	drosen@google.com
Cc: sinquersw@gmail.com, kuifeng@meta.com,
	Kui-Feng Lee <thinker.li@gmail.com>
Subject: [PATCH bpf-next v10 13/13] selftests/bpf: test case for register_bpf_struct_ops().
Date: Fri,  3 Nov 2023 16:22:02 -0700	[thread overview]
Message-ID: <20231103232202.3664407-14-thinker.li@gmail.com> (raw)
In-Reply-To: <20231103232202.3664407-1-thinker.li@gmail.com>

From: Kui-Feng Lee <thinker.li@gmail.com>

Create a new struct_ops type called bpf_testmod_ops within the bpf_testmod
module. When a struct_ops object is registered, the bpf_testmod module will
invoke test_2 from the module.

Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
---
 .../selftests/bpf/bpf_testmod/bpf_testmod.c   |  59 +++++++
 .../selftests/bpf/bpf_testmod/bpf_testmod.h   |   5 +
 .../bpf/prog_tests/test_struct_ops_module.c   | 144 ++++++++++++++++++
 .../selftests/bpf/progs/struct_ops_module.c   |  30 ++++
 .../testing/selftests/bpf/progs/testmod_btf.c |  26 ++++
 5 files changed, 264 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c
 create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_module.c
 create mode 100644 tools/testing/selftests/bpf/progs/testmod_btf.c

diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
index a5e246f7b202..418e10311c33 100644
--- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2020 Facebook */
+#include <linux/bpf.h>
 #include <linux/btf.h>
 #include <linux/btf_ids.h>
 #include <linux/error-injection.h>
@@ -522,11 +523,66 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test_static_unused_arg)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test_offset)
 BTF_SET8_END(bpf_testmod_check_kfunc_ids)
 
+#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+
+DEFINE_STRUCT_OPS_VALUE_TYPE(bpf_testmod_ops);
+
+static int bpf_testmod_ops_init(struct btf *btf)
+{
+	return 0;
+}
+
+static bool bpf_testmod_ops_is_valid_access(int off, int size,
+					    enum bpf_access_type type,
+					    const struct bpf_prog *prog,
+					    struct bpf_insn_access_aux *info)
+{
+	return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
+}
+
+static int bpf_testmod_ops_init_member(const struct btf_type *t,
+				       const struct btf_member *member,
+				       void *kdata, const void *udata)
+{
+	return 0;
+}
+
 static const struct btf_kfunc_id_set bpf_testmod_kfunc_set = {
 	.owner = THIS_MODULE,
 	.set   = &bpf_testmod_check_kfunc_ids,
 };
 
+static const struct bpf_verifier_ops bpf_testmod_verifier_ops = {
+	.is_valid_access = bpf_testmod_ops_is_valid_access,
+};
+
+static int bpf_dummy_reg(void *kdata)
+{
+	struct bpf_testmod_ops *ops = kdata;
+	int r;
+
+	BTF_STRUCT_OPS_TYPE_EMIT(bpf_testmod_ops);
+	r = ops->test_2(4, 3);
+
+	return 0;
+}
+
+static void bpf_dummy_unreg(void *kdata)
+{
+}
+
+struct bpf_struct_ops bpf_bpf_testmod_ops = {
+	.verifier_ops = &bpf_testmod_verifier_ops,
+	.init = bpf_testmod_ops_init,
+	.init_member = bpf_testmod_ops_init_member,
+	.reg = bpf_dummy_reg,
+	.unreg = bpf_dummy_unreg,
+	.name = "bpf_testmod_ops",
+	.owner = THIS_MODULE,
+};
+
+#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */
+
 extern int bpf_fentry_test1(int a);
 
 static int bpf_testmod_init(void)
@@ -537,6 +593,9 @@ static int bpf_testmod_init(void)
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_testmod_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_testmod_kfunc_set);
 	ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &bpf_testmod_kfunc_set);
+#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+	ret = ret ?: register_bpf_struct_ops(&bpf_bpf_testmod_ops);
+#endif
 	if (ret < 0)
 		return ret;
 	if (bpf_fentry_test1(0) < 0)
diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h
index f32793efe095..ca5435751c79 100644
--- a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h
+++ b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h
@@ -28,4 +28,9 @@ struct bpf_iter_testmod_seq {
 	int cnt;
 };
 
+struct bpf_testmod_ops {
+	int (*test_1)(void);
+	int (*test_2)(int a, int b);
+};
+
 #endif /* _BPF_TESTMOD_H */
diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c
new file mode 100644
index 000000000000..49f4a4460642
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <time.h>
+
+#include "rcu_tasks_trace_gp.skel.h"
+#include "struct_ops_module.skel.h"
+#include "testmod_btf.skel.h"
+
+static void test_regular_load(void)
+{
+	struct struct_ops_module *skel;
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+	struct bpf_link *link;
+	int err;
+
+	skel = struct_ops_module__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+		return;
+	err = struct_ops_module__load(skel);
+	if (!ASSERT_OK(err, "struct_ops_module_load"))
+		goto cleanup;
+
+	link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+	ASSERT_OK_PTR(link, "attach_test_mod_1");
+
+	/* test_2() will be called from bpf_dummy_reg() in bpf_testmod.c */
+	ASSERT_EQ(skel->bss->test_2_result, 7, "test_2_result");
+
+	bpf_link__destroy(link);
+
+cleanup:
+	struct_ops_module__destroy(skel);
+}
+
+static void test_load_without_module(void)
+{
+	struct struct_ops_module *skel = NULL;
+	struct testmod_btf *skel_btf;
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+	struct bpf_link *link_btf = NULL;;
+	int err, i;
+
+	skel_btf = testmod_btf__open_and_load();
+	if (!ASSERT_OK_PTR(skel_btf, "testmod_btf_open"))
+		return;
+
+	link_btf = bpf_program__attach(skel_btf->progs.kprobe_btf_put);
+	if (!ASSERT_OK_PTR(link_btf, "kprobe_btf_put_attach"))
+		goto cleanup;
+
+	err = unload_bpf_testmod(false);
+	if (!ASSERT_OK(err, "unload_bpf_testmod"))
+		goto cleanup;
+
+	skel = struct_ops_module__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+		goto cleanup;
+	err = struct_ops_module__load(skel);
+	ASSERT_ERR(err, "struct_ops_module_load");
+
+	/* Wait for the struct_ops map to be freed. Struct_ops maps hold a
+	 * refcount to the module btf. And, this function unloads and then
+	 * loads bpf_testmod. Without waiting the map to be freed, the next
+	 * test may fail since libbpf may use the old btf that is still
+	 * alive instead of the new one that is created for the newly
+	 * loaded module.
+	 */
+	for (i = 0; i < 10; i++) {
+		if (skel_btf->bss->bpf_testmod_put)
+			break;
+		usleep(100000);
+	}
+	ASSERT_EQ(skel_btf->bss->bpf_testmod_put, 1, "btf_put");
+
+cleanup:
+	bpf_link__destroy(link_btf);
+	struct_ops_module__destroy(skel);
+	testmod_btf__destroy(skel_btf);
+	/* Without this, the next test may fail */
+	load_bpf_testmod(false);
+}
+
+static void test_attach_without_module(void)
+{
+	struct struct_ops_module *skel = NULL;
+	struct testmod_btf *skel_btf;
+	struct bpf_link *link, *link_btf = NULL;
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+	int err, i;
+
+	skel_btf = testmod_btf__open_and_load();
+	if (!ASSERT_OK_PTR(skel_btf, "testmod_btf_open"))
+		return;
+
+	link_btf = bpf_program__attach(skel_btf->progs.kprobe_btf_put);
+	if (!ASSERT_OK_PTR(link_btf, "kprobe_btf_put_attach"))
+		goto cleanup;
+
+	skel = struct_ops_module__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
+		goto cleanup;
+	err = struct_ops_module__load(skel);
+	if (!ASSERT_OK(err, "struct_ops_module_load"))
+		goto cleanup;
+
+	err = unload_bpf_testmod(false);
+	if (!ASSERT_OK(err, "unload_bpf_testmod"))
+		goto cleanup;
+
+	link = bpf_map__attach_struct_ops(skel->maps.testmod_1);
+	ASSERT_ERR_PTR(link, "attach_test_mod_1");
+
+	struct_ops_module__destroy(skel);
+	skel = NULL;
+
+	/* Wait for the struct_ops map to be freed */
+	for (i = 0; i < 10; i++) {
+		if (skel_btf->bss->bpf_testmod_put)
+			break;
+		usleep(100000);
+	}
+	ASSERT_EQ(skel_btf->bss->bpf_testmod_put, 1, "btf_put");
+
+cleanup:
+	bpf_link__destroy(link_btf);
+	struct_ops_module__destroy(skel);
+	testmod_btf__destroy(skel_btf);
+	/* Without this, the next test may fail */
+	load_bpf_testmod(false);
+}
+
+void serial_test_struct_ops_module(void)
+{
+	if (test__start_subtest("regular_load"))
+		test_regular_load();
+
+	if (test__start_subtest("load_without_module"))
+		test_load_without_module();
+
+	if (test__start_subtest("attach_without_module"))
+		test_attach_without_module();
+}
+
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_module.c b/tools/testing/selftests/bpf/progs/struct_ops_module.c
new file mode 100644
index 000000000000..cb305d04342f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/struct_ops_module.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "../bpf_testmod/bpf_testmod.h"
+
+char _license[] SEC("license") = "GPL";
+
+int test_2_result = 0;
+
+SEC("struct_ops/test_1")
+int BPF_PROG(test_1)
+{
+	return 0xdeadbeef;
+}
+
+SEC("struct_ops/test_2")
+int BPF_PROG(test_2, int a, int b)
+{
+	test_2_result = a + b;
+	return a + b;
+}
+
+SEC(".struct_ops.link")
+struct bpf_testmod_ops testmod_1 = {
+	.test_1 = (void *)test_1,
+	.test_2 = (void *)test_2,
+};
+
diff --git a/tools/testing/selftests/bpf/progs/testmod_btf.c b/tools/testing/selftests/bpf/progs/testmod_btf.c
new file mode 100644
index 000000000000..cfcd269f07fe
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/testmod_btf.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+int bpf_testmod_put = 0;
+
+SEC("kprobe/btf_put")
+int BPF_KPROBE(kprobe_btf_put, struct btf *btf)
+{
+	const char name[] = "bpf_testmod";
+	int i;
+
+	for (i = 0; i < sizeof(name); i++) {
+		if (BPF_CORE_READ(btf, name[i]) != name[i])
+			return 0;
+	}
+
+	if (BPF_CORE_READ(btf, refcnt.refs.counter) == 1)
+		bpf_testmod_put = 1;
+
+	return 0;
+}
-- 
2.34.1


      parent reply	other threads:[~2023-11-03 23:22 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-03 23:21 [PATCH bpf-next v10 00/13] Registrating struct_ops types from modules thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 01/13] bpf: refactory struct_ops type initialization to a function thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 02/13] bpf: get type information with BPF_ID_LIST thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 03/13] bpf, net: introduce bpf_struct_ops_desc thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 04/13] bpf: add struct_ops_tab to btf thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 05/13] bpf: make struct_ops_map support btfs other than btf_vmlinux thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 06/13] bpf: lookup struct_ops types from a given module BTF thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 07/13] bpf: pass attached BTF to the bpf_struct_ops subsystem thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 08/13] bpf: hold module for bpf_struct_ops_map thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 09/13] bpf: validate value_type thinker.li
2023-11-03 23:21 ` [PATCH bpf-next v10 10/13] bpf, net: switch to dynamic registration thinker.li
2023-11-05  5:16   ` kernel test robot
2023-11-03 23:22 ` [PATCH bpf-next v10 11/13] libbpf: Find correct module BTFs for struct_ops maps and progs thinker.li
2023-11-03 23:22 ` [PATCH bpf-next v10 12/13] bpf: export btf_ctx_access to modules thinker.li
2023-11-03 23:22 ` thinker.li [this message]

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=20231103232202.3664407-14-thinker.li@gmail.com \
    --to=thinker.li@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=drosen@google.com \
    --cc=kernel-team@meta.com \
    --cc=kuifeng@meta.com \
    --cc=martin.lau@linux.dev \
    --cc=sinquersw@gmail.com \
    --cc=song@kernel.org \
    /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.