Verify that specifying a module object id 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". Currently the tests take the messy approach of determining object and type ids for the relevant module/function; __builtin_btf_type_id() supports object ids by returning a 64-bit value, but need to find a good way to determine if that support is present. Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- .../selftests/bpf/prog_tests/snprintf_btf_mod.c | 124 +++++++++++++++++++++ tools/testing/selftests/bpf/progs/bpf_iter.h | 2 +- tools/testing/selftests/bpf/progs/btf_ptr.h | 2 +- tools/testing/selftests/bpf/progs/veth_stats_rx.c | 72 ++++++++++++ 4 files changed, 198 insertions(+), 2 deletions(-) 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..89805d7 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/snprintf_btf_mod.c @@ -0,0 +1,124 @@ +// 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; + + /* This could all be replaced by __builtin_btf_type_id(); but need + * a way to determine if it supports object and type id. In the + * meantime, look up type id for veth_stats and object id for veth. + */ + 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; + + bss->veth_obj_id = 0; + + for (id = 1; bpf_btf_get_next_id(id, &id) == 0; ) { + struct bpf_btf_info info; + __u32 len = sizeof(info); + char name[64]; + int fd; + + fd = bpf_btf_get_fd_by_id(id); + if (fd < 0) + continue; + + memset(&info, 0, sizeof(info)); + info.name_len = sizeof(name); + info.name = (__u64)name; + if (bpf_obj_get_info_by_fd(fd, &info, &len) || + strcmp((char *)info.name, "veth") != 0) + continue; + bss->veth_obj_id = info.id; + } + + if (CHECK(bss->veth_obj_id == 0, "get obj id for veth module", + "could not get veth module 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/bpf_iter.h b/tools/testing/selftests/bpf/progs/bpf_iter.h index 6a12554..d6d9838 100644 --- a/tools/testing/selftests/bpf/progs/bpf_iter.h +++ b/tools/testing/selftests/bpf/progs/bpf_iter.h @@ -119,7 +119,7 @@ struct bpf_iter__sockmap { struct btf_ptr { void *ptr; __u32 type_id; - __u32 flags; + __u32 obj_id; }; enum { diff --git a/tools/testing/selftests/bpf/progs/btf_ptr.h b/tools/testing/selftests/bpf/progs/btf_ptr.h index c3c9797..4f84034 100644 --- a/tools/testing/selftests/bpf/progs/btf_ptr.h +++ b/tools/testing/selftests/bpf/progs/btf_ptr.h @@ -16,7 +16,7 @@ struct btf_ptr { void *ptr; __u32 type_id; - __u32 flags; + __u32 obj_id; }; 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..f04fb55 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/veth_stats_rx.c @@ -0,0 +1,72 @@ +// 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; +s32 veth_obj_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 struct btf_ptr p = { }; + __u32 btf_ids[] = { 0, 0 }; + __u32 obj_ids[] = { 0, 0 }; + void *ptrs[] = { 0, 0 }; + __u32 key = 0; + int i, j; + char *str; + + btf_ids[0] = veth_stats_btf_id; + obj_ids[0] = veth_obj_id; + ptrs[0] = (void *)PT_REGS_PARM1_CORE(ctx); + + btf_ids[1] = bpf_core_type_id_kernel(struct net_device); + ptrs[1] = (void *)PT_REGS_PARM2_CORE(ctx); + + str = bpf_map_lookup_elem(&strdata, &key); + if (!str) + return 0; + + for (i = 0; i < ARRAY_SIZE(btf_ids); i++) { + p.type_id = btf_ids[i]; + p.obj_id = obj_ids[i]; + p.ptr = ptrs[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