Verify that specifying a module name in "struct btf_ptr *" along with a type id of a module-specific type will succeed. veth_stats_rx() is chosen because its function signature consists of a module-specific type "struct veth_stats" and a kernel-specific one "struct net_device". Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- .../selftests/bpf/prog_tests/snprintf_btf_mod.c | 96 ++++++++++++++++++++++ tools/testing/selftests/bpf/progs/btf_ptr.h | 1 + tools/testing/selftests/bpf/progs/veth_stats_rx.c | 73 ++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/snprintf_btf_mod.c create mode 100644 tools/testing/selftests/bpf/progs/veth_stats_rx.c diff --git a/tools/testing/selftests/bpf/prog_tests/snprintf_btf_mod.c b/tools/testing/selftests/bpf/prog_tests/snprintf_btf_mod.c new file mode 100644 index 0000000..f1b12df --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/snprintf_btf_mod.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <linux/btf.h> +#include <bpf/btf.h> +#include "veth_stats_rx.skel.h" + +#define VETH_NAME "bpfveth0" + +/* Demonstrate that bpf_snprintf_btf succeeds for both module-specific + * and kernel-defined data structures; veth_stats_rx() is used as + * it has both module-specific and kernel-defined data as arguments. + * This test assumes that veth is built as a module and will skip if not. + */ +void test_snprintf_btf_mod(void) +{ + struct btf *vmlinux_btf = NULL, *veth_btf = NULL; + struct veth_stats_rx *skel = NULL; + struct veth_stats_rx__bss *bss; + int err, duration = 0; + __u32 id; + + err = system("ip link add name " VETH_NAME " type veth"); + if (CHECK(err, "system", "ip link add veth failed: %d\n", err)) + return; + + vmlinux_btf = btf__parse_raw("/sys/kernel/btf/vmlinux"); + err = libbpf_get_error(vmlinux_btf); + if (CHECK(err, "parse vmlinux BTF", "failed parsing vmlinux BTF: %d\n", + err)) + goto cleanup; + veth_btf = btf__parse_raw_split("/sys/kernel/btf/veth", vmlinux_btf); + err = libbpf_get_error(veth_btf); + if (err == -ENOENT) { + printf("%s:SKIP:no BTF info for veth\n", __func__); + test__skip(); + goto cleanup; + } + + if (CHECK(err, "parse veth BTF", "failed parsing veth BTF: %d\n", err)) + goto cleanup; + + skel = veth_stats_rx__open(); + if (CHECK(!skel, "skel_open", "failed to open skeleton\n")) + goto cleanup; + + err = veth_stats_rx__load(skel); + if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err)) + goto cleanup; + + bss = skel->bss; + + bss->veth_stats_btf_id = btf__find_by_name(veth_btf, "veth_stats"); + + if (CHECK(bss->veth_stats_btf_id <= 0, "find 'struct veth_stats'", + "could not find 'struct veth_stats' in veth BTF: %d", + bss->veth_stats_btf_id)) + goto cleanup; + + err = veth_stats_rx__attach(skel); + if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err)) + goto cleanup; + + /* generate stats event, then delete; this ensures the program + * triggers prior to reading status. + */ + err = system("ethtool -S " VETH_NAME " > /dev/null"); + if (CHECK(err, "system", "ethtool -S failed: %d\n", err)) + goto cleanup; + + system("ip link delete " VETH_NAME); + + /* + * Make sure veth_stats_rx program was triggered and it set + * expected return values from bpf_trace_printk()s and all + * tests ran. + */ + if (CHECK(bss->ret <= 0, + "bpf_snprintf_btf: got return value", + "ret <= 0 %ld test %d\n", bss->ret, bss->ran_subtests)) + goto cleanup; + + if (CHECK(bss->ran_subtests == 0, "check if subtests ran", + "no subtests ran, did BPF program run?")) + goto cleanup; + + if (CHECK(bss->num_subtests != bss->ran_subtests, + "check all subtests ran", + "only ran %d of %d tests\n", bss->num_subtests, + bss->ran_subtests)) + goto cleanup; + +cleanup: + system("ip link delete " VETH_NAME ">/dev/null 2>&1"); + if (skel) + veth_stats_rx__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/btf_ptr.h b/tools/testing/selftests/bpf/progs/btf_ptr.h index c3c9797..afef9b3 100644 --- a/tools/testing/selftests/bpf/progs/btf_ptr.h +++ b/tools/testing/selftests/bpf/progs/btf_ptr.h @@ -17,6 +17,7 @@ struct btf_ptr { void *ptr; __u32 type_id; __u32 flags; + const char *module; }; enum { diff --git a/tools/testing/selftests/bpf/progs/veth_stats_rx.c b/tools/testing/selftests/bpf/progs/veth_stats_rx.c new file mode 100644 index 0000000..6ec7ce2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/veth_stats_rx.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020, Oracle and/or its affiliates. */ + +#include "btf_ptr.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_core_read.h> + +#include <errno.h> + +long ret = 0; +int num_subtests = 0; +int ran_subtests = 0; +s32 veth_stats_btf_id = 0; + +#define STRSIZE 2048 + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, char[STRSIZE]); +} strdata SEC(".maps"); + +SEC("kprobe/veth_stats_rx") +int veth_stats_rx(struct pt_regs *ctx) +{ + static __u64 flags[] = { 0, BTF_F_COMPACT, BTF_F_ZERO, BTF_F_PTR_RAW, + BTF_F_NONAME, BTF_F_COMPACT | BTF_F_ZERO | + BTF_F_PTR_RAW | BTF_F_NONAME }; + static const char mod[] = "veth"; + const char *mods[] = { mod, 0 }; + static struct btf_ptr p = { }; + __u32 btf_ids[] = { 0, 0 }; + void *ptrs[] = { 0, 0 }; + __u32 key = 0; + int i, j; + char *str; + + btf_ids[0] = veth_stats_btf_id; + ptrs[0] = (void *)PT_REGS_PARM1_CORE(ctx); +#if __has_builtin(__builtin_btf_type_id) + btf_ids[1] = bpf_core_type_id_kernel(struct net_device); + ptrs[1] = (void *)PT_REGS_PARM2_CORE(ctx); +#endif + str = bpf_map_lookup_elem(&strdata, &key); + if (!str) + return 0; + + for (i = 0; i < ARRAY_SIZE(btf_ids); i++) { + if (btf_ids[i] == 0) + continue; + p.type_id = btf_ids[i]; + p.ptr = ptrs[i]; + p.module = mods[i]; + for (j = 0; j < ARRAY_SIZE(flags); j++) { + ++num_subtests; + ret = bpf_snprintf_btf(str, STRSIZE, &p, sizeof(p), 0); + if (ret < 0) + bpf_printk("returned %d when writing id %d", + ret, p.type_id); + ++ran_subtests; + } + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; -- 1.8.3.1