diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 40ac13c0b782..255e3b3629a0 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -30,6 +30,10 @@ DESCRIPTION Keyword **kernel** can be omitted. + Note that when probed, some eBPF helpers (e.g. + **bpf_trace_printk**\ () or **bpf_probe_write_user**\ ()) may + print warnings to kernel logs. + **bpftool feature help** Print short help message. diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c index cc731475c74b..55c8d215ca44 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -25,6 +25,13 @@ enum probe_component { COMPONENT_KERNEL, }; +#define BPF_HELPER_MAKE_ENTRY(name) [BPF_FUNC_ ## name] = "bpf_" # name +static const char * const helper_name[] = { + __BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY) +}; + +#undef BPF_HELPER_MAKE_ENTRY + /* Miscellaneous utility functions */ static bool check_procfs(void) @@ -458,6 +465,44 @@ static void probe_map_type(enum bpf_map_type map_type) print_bool_feature(feat_name, plain_desc, res); } +static void +probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type) +{ + const char *ptype_name = prog_type_name[prog_type]; + char feat_name[128]; + unsigned int id; + bool res; + + if (json_output) { + sprintf(feat_name, "%s_available_helpers", ptype_name); + jsonw_name(json_wtr, feat_name); + jsonw_start_array(json_wtr); + } else { + printf("eBPF helpers supported for program type %s:", + ptype_name); + } + + for (id = 1; id < ARRAY_SIZE(helper_name); id++) { + if (!supported_type) + res = false; + else + res = bpf_probe_helper(id, prog_type, 0); + + if (json_output) { + if (res) + jsonw_string(json_wtr, helper_name[id]); + } else { + if (res) + printf("\n\t- %s", helper_name[id]); + } + } + + if (json_output) + jsonw_end_array(json_wtr); + else + printf("\n"); +} + static int do_probe(int argc, char **argv) { enum probe_component target = COMPONENT_UNSPEC; @@ -533,6 +578,12 @@ static int do_probe(int argc, char **argv) for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++) probe_map_type(i); + print_end_then_start_section("helpers", + "Scanning eBPF helper functions..."); + + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++) + probe_helpers_for_progtype(i, supported_types[i]); + exit_close_json: if (json_output) { /* End current "section" of probes */ diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 72385f6f9415..62ae6cb93da1 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -366,6 +366,8 @@ bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo, LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex); LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex); +LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id, + enum bpf_prog_type prog_type, __u32 ifindex); #ifdef __cplusplus } /* extern "C" */ diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index bb2dfc3b2d7b..266bc95d0142 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -127,6 +127,7 @@ LIBBPF_0.0.1 { LIBBPF_0.0.2 { global: + bpf_probe_helper; bpf_probe_map_type; bpf_probe_prog_type; } LIBBPF_0.0.1; diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c index f511bd317b87..8c3a1c04dcb2 100644 --- a/tools/lib/bpf/libbpf_probes.c +++ b/tools/lib/bpf/libbpf_probes.c @@ -2,7 +2,11 @@ /* Copyright (c) 2019 Netronome Systems, Inc. */ #include +#include +#include +#include #include +#include #include #include @@ -11,6 +15,37 @@ #include "bpf.h" #include "libbpf.h" +static bool grep(const char *buffer, const char *pattern) +{ + return !!strstr(buffer, pattern); +} + +static int get_vendor_id(int ifindex) +{ + char ifname[IF_NAMESIZE], path[64], buf[8]; + ssize_t len; + int fd; + + if (!if_indextoname(ifindex, ifname)) + return -1; + + snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname); + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len < 0) + return -1; + if (len >= (ssize_t)sizeof(buf)) + return -1; + buf[len] = '\0'; + + return strtol(buf, NULL, 0); +} + static int get_kernel_version(void) { int version, subversion, patchlevel; @@ -177,3 +212,31 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex) return fd >= 0; } + +bool bpf_probe_helper(enum bpf_func_id id, enum bpf_prog_type prog_type, + __u32 ifindex) +{ + struct bpf_insn insns[2] = { + BPF_EMIT_CALL(id), + BPF_EXIT_INSN() + }; + char buf[4096] = {}; + bool res; + + probe_load(prog_type, insns, ARRAY_SIZE(insns), buf, sizeof(buf), + ifindex); + res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); + + if (ifindex) { + switch (get_vendor_id(ifindex)) { + case 0x19ee: /* Netronome specific */ + res = res && !grep(buf, "not supported by FW") && + !grep(buf, "unsupported function id"); + break; + default: + break; + } + } + + return res; +}