Add selftests covering selective kernel module BTF loading through
bpf_object_open_opts.

The tests verify that:
- the existing behavior is preserved when btf_module_names is not
  specified, and loading succeeds when the required module BTF is
  specified;
- a required module BTF is skipped when the module is not specified in
  btf_module_names;
- an empty btf_module_names list skips loading all module BTFs;
- invalid module BTF name lists are rejected.

Signed-off-by: Fuyu Zhao <[email protected]>
---
 .../bpf/prog_tests/btf_module_names.c         | 139 ++++++++++++++++++
 .../selftests/bpf/progs/btf_module_names.c    |  13 ++
 2 files changed, 152 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_module_names.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf_module_names.c

diff --git a/tools/testing/selftests/bpf/prog_tests/btf_module_names.c 
b/tools/testing/selftests/bpf/prog_tests/btf_module_names.c
new file mode 100644
index 000000000000..3dc72bef014c
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_module_names.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "btf_module_names.skel.h"
+
+static void btf_module_names_load(void)
+{
+       struct btf_module_names *skel = NULL;
+       int ret;
+       static const char *mod_names[] = { "bpf_testmod" };
+
+       LIBBPF_OPTS(bpf_object_open_opts, opts,
+               .btf_module_names = mod_names,
+               .nr_btf_module_names = 1,
+       );
+
+       /* Verify backward compatibility. */
+       skel = btf_module_names__open_opts(NULL);
+       if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts default"))
+               goto out;
+
+       ret = btf_module_names__load(skel);
+       ASSERT_OK(ret, "btf_module_names__load default");
+
+       btf_module_names__destroy(skel);
+       skel = NULL;
+
+       skel = btf_module_names__open_opts(&opts);
+       if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts"))
+               goto out;
+
+       ret = btf_module_names__load(skel);
+       ASSERT_OK(ret, "btf_module_names__load");
+out:
+       btf_module_names__destroy(skel);
+}
+
+/*
+ * Verify that an unrequested module BTF is skipped. The BPF program
+ * requires the BTF of bpf_testmod, but bpf_testmod is not specified in
+ * btf_module_names, so its BTF is skipped and the BPF program fails to load.
+ */
+static void btf_module_names_skip(void)
+{
+       struct btf_module_names *skel = NULL;
+       int ret;
+       static const char *mod_names[] = { "module_nonexist" };
+
+       LIBBPF_OPTS(bpf_object_open_opts, opts,
+               .btf_module_names = mod_names,
+               .nr_btf_module_names = 1,
+       );
+
+       skel = btf_module_names__open_opts(&opts);
+       if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts"))
+               goto out;
+
+       ret = btf_module_names__load(skel);
+       ASSERT_EQ(ret, -ESRCH, "btf_module_names__load");
+
+out:
+       btf_module_names__destroy(skel);
+}
+
+/*
+ * Verify that an empty filter skips loading all module BTFs. The BPF
+ * program requires bpf_testmod BTF, so it fails to load.
+ */
+static void btf_module_names_empty(void)
+{
+       struct btf_module_names *skel = NULL;
+       int ret;
+       static const char *mod_names[] = { "" };
+
+       LIBBPF_OPTS(bpf_object_open_opts, opts,
+               .btf_module_names = mod_names,
+       );
+
+       skel = btf_module_names__open_opts(&opts);
+       if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts empty"))
+               goto out;
+
+       ret = btf_module_names__load(skel);
+       ASSERT_EQ(ret, -ESRCH, "btf_module_names__load empty");
+
+out:
+       btf_module_names__destroy(skel);
+}
+
+static void btf_module_names_invalid(void)
+{
+       struct btf_module_names *skel = NULL;
+       const char *names[] = { NULL };
+       const char *empty_names[] = { "" };
+       const char *duplicate_names[] = {
+               "bpf_testmod", "bpf_testmod",
+       };
+       LIBBPF_OPTS(bpf_object_open_opts, opts,
+               .btf_module_names = names,
+               .nr_btf_module_names = 1,
+       );
+
+       skel = btf_module_names__open_opts(&opts);
+       ASSERT_EQ(libbpf_get_error(skel), -EINVAL,
+                 "btf_module_names__open_opts null");
+       btf_module_names__destroy(skel);
+
+       opts.btf_module_names = empty_names;
+       skel = btf_module_names__open_opts(&opts);
+       ASSERT_EQ(libbpf_get_error(skel), -EINVAL,
+                 "btf_module_names__open_opts empty");
+       btf_module_names__destroy(skel);
+
+       opts.btf_module_names = duplicate_names;
+       opts.nr_btf_module_names = 2;
+       skel = btf_module_names__open_opts(&opts);
+       ASSERT_EQ(libbpf_get_error(skel), -EINVAL,
+                 "btf_module_names__open_opts duplicate name");
+       btf_module_names__destroy(skel);
+}
+
+void test_btf_module_names(void)
+{
+       if (!env.has_testmod) {
+               test__skip();
+               return;
+       }
+
+       if (test__start_subtest("btf_module_names_load"))
+               btf_module_names_load();
+
+       if (test__start_subtest("btf_module_names_skip"))
+               btf_module_names_skip();
+
+       if (test__start_subtest("btf_module_names_empty"))
+               btf_module_names_empty();
+
+       if (test__start_subtest("btf_module_names_invalid"))
+               btf_module_names_invalid();
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_module_names.c 
b/tools/testing/selftests/bpf/progs/btf_module_names.c
new file mode 100644
index 000000000000..232adc0f33f6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_module_names.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+SEC("fentry/bpf_testmod_loop_test")
+int BPF_PROG(test_btf_module_names)
+{
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.34.1


Reply via email to