On Thu, Jan 3, 2019 at 6:03 AM Quentin Monnet <quentin.mon...@netronome.com> wrote: > > Similarly to what was done for program types and map types, add a set of > probes to test the availability of the different eBPF helper functions > on the current system. > > For each known program type, all known helpers are tested, in order to > establish a compatibility matrix. Output is provided as a set of lists > of available helpers, one per program type. > > Sample output: > > # bpftool feature probe kernel > ... > Scanning eBPF helper functions... > eBPF helpers supported for program type socket_filter: > - bpf_map_lookup_elem > - bpf_map_update_elem > - bpf_map_delete_elem > ... > eBPF helpers supported for program type kprobe: > - bpf_map_lookup_elem > - bpf_map_update_elem > - bpf_map_delete_elem > ... > > # bpftool --json --pretty feature probe kernel > { > ... > "helpers": { > "socket_filter_available_helpers": ["bpf_map_lookup_elem", \ > "bpf_map_update_elem","bpf_map_delete_elem", ... > ], > "kprobe_available_helpers": ["bpf_map_lookup_elem", \ > "bpf_map_update_elem","bpf_map_delete_elem", ... > ], > ... > } > } > > v3: > - Do not pass kernel version from bpftool to libbpf probes (kernel > version for testing program with kprobes is retrieved directly from > libbpf). > - Dump one list of available helpers per program type (instead of one > list of compatible program types per helper). > > v2: > - Move probes from bpftool to libbpf. > - Test all program types for each helper, print a list of working prog > types for each helper. > - Fall back on include/uapi/linux/bpf.h for names and ids of helpers. > - Remove C-style macros output from this patch. > > Signed-off-by: Quentin Monnet <quentin.mon...@netronome.com> > --- > .../bpftool/Documentation/bpftool-feature.rst | 4 ++ > tools/bpf/bpftool/feature.c | 49 +++++++++++++++ > tools/lib/bpf/libbpf.h | 2 + > tools/lib/bpf/libbpf.map | 1 + > tools/lib/bpf/libbpf_probes.c | 62 +++++++++++++++++++ > 5 files changed, 118 insertions(+) > > 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 6a4ff402854c..133cdfda00d4 100644 > --- a/tools/bpf/bpftool/feature.c > +++ b/tools/bpf/bpftool/feature.c > @@ -25,6 +25,11 @@ 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) > +}; > + > /* Miscellaneous utility functions */ > > static bool check_procfs(void) > @@ -400,6 +405,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; > @@ -474,6 +517,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..6ab275933d2e 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(__u32 id, enum bpf_prog_type prog_type, Any reason not to use enum bpf_func_id as id type (instead of __u32)?
> + __u32 ifindex); > > #ifdef __cplusplus > } /* extern "C" */ > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map > index c08f4c726e8e..67e51b2becec 100644 > --- a/tools/lib/bpf/libbpf.map > +++ b/tools/lib/bpf/libbpf.map > @@ -56,6 +56,7 @@ LIBBPF_0.0.1 { > bpf_object__unpin_maps; > bpf_object__unpin_programs; > bpf_perf_event_read_simple; > + bpf_probe_helper; > bpf_probe_map_type; > bpf_probe_prog_type; > bpf_prog_attach; > diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c > index e3d57632a975..1b325142bca4 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 <errno.h> > +#include <fcntl.h> > +#include <string.h> > +#include <stdlib.h> > #include <unistd.h> > +#include <net/if.h> > #include <sys/utsname.h> > > #include <linux/filter.h> > @@ -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,30 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, > __u32 ifindex) > > return fd >= 0; > } > + > +bool bpf_probe_helper(__u32 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; > + > + prog_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; > +} > -- > 2.17.1 >