From mboxrd@z Thu Jan 1 00:00:00 1970 From: Stanislav Fomichev Subject: Re: [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions Date: Thu, 20 Dec 2018 09:53:01 -0800 Message-ID: <20181220175301.GO20955@mini-arch.hsd1.ca.comcast.net> References: <20181220122432.27511-1-quentin.monnet@netronome.com> <20181220122432.27511-7-quentin.monnet@netronome.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Cc: Alexei Starovoitov , Daniel Borkmann , netdev@vger.kernel.org, oss-drivers@netronome.com, Arnaldo Carvalho de Melo , Jesper Dangaard Brouer , Stanislav Fomichev To: Quentin Monnet Return-path: Received: from mail-pf1-f195.google.com ([209.85.210.195]:46708 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729481AbeLTRxE (ORCPT ); Thu, 20 Dec 2018 12:53:04 -0500 Received: by mail-pf1-f195.google.com with SMTP id c73so1255616pfe.13 for ; Thu, 20 Dec 2018 09:53:03 -0800 (PST) Content-Disposition: inline In-Reply-To: <20181220122432.27511-7-quentin.monnet@netronome.com> Sender: netdev-owner@vger.kernel.org List-ID: On 12/20, Quentin Monnet 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. > > Each known helper is tested with all program types supported by the > system, in order to establish a compatibility matrix. Output is provided > as a list of compatible program types, for each helper. > > Sample output: > > # bpftool feature probe kernel > ... > Scanning eBPF helper functions... > ... > eBPF helper bpf_skb_change_head supported for program types: \ > lwt_xmit sk_skb > eBPF helper bpf_xdp_adjust_head supported for program types: xdp > eBPF helper bpf_probe_read_str supported for program types: \ > kprobe tracepoint perf_event raw_tracepoint > ... > > # bpftool --json --pretty feature probe kernel > { > ... > "helpers": { > ... > "bpf_skb_change_head_compat_list": ["lwt_xmit","sk_skb" > ], > "bpf_xdp_adjust_head_compat_list": ["xdp" > ], > "bpf_probe_read_str_compat_list": ["kprobe","tracepoint", \ > "perf_event","raw_tracepoint" > ], > ... > } > } > > 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 > --- > .../bpftool/Documentation/bpftool-feature.rst | 4 ++ > tools/bpf/bpftool/feature.c | 51 +++++++++++++++ > tools/lib/bpf/libbpf.h | 2 + > tools/lib/bpf/libbpf.map | 1 + > tools/lib/bpf/libbpf_probes.c | 63 +++++++++++++++++++ > 5 files changed, 121 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 3c44953ded5a..bc483823014b 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) > @@ -418,6 +423,45 @@ static void probe_map_type(enum bpf_map_type map_type) > print_bool_feature(feat_name, plain_desc, res); > } > > +static void > +probe_helper(__u32 id, const char *name, int kernel_version, > + bool *supported_types) > +{ > + char feat_name[128], plain_desc[128]; > + unsigned int i; > + > + sprintf(feat_name, "%s_compat_list", name); > + sprintf(plain_desc, > + "eBPF helper %s supported for program types:", > + name); > + > + if (json_output) { > + jsonw_name(json_wtr, feat_name); > + jsonw_start_array(json_wtr); > + } else { > + printf("%s", plain_desc); > + } > + > + for (i = BPF_PROG_TYPE_UNSPEC + 1; > + i < ARRAY_SIZE(prog_type_name); i++) { > + if (!supported_types[i]) > + continue; > + > + if (!bpf_probe_helper(id, i, kernel_version, 0)) > + continue; > + > + if (json_output) > + jsonw_string(json_wtr, prog_type_name[i]); > + else > + printf(" %s", prog_type_name[i]); > + } > + > + 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; > @@ -494,6 +538,13 @@ 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 = 1; i < ARRAY_SIZE(helper_name); i++) > + probe_helper(i, helper_name[i], kernel_version, > + supported_types); > + > 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 202c1ee5c579..ab5b40af5fae 100644 > --- a/tools/lib/bpf/libbpf.h > +++ b/tools/lib/bpf/libbpf.h > @@ -361,6 +361,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, > int kernel_version, __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, > + int kernel_version, __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 796fe1e66169..19aa062f33b5 100644 > --- a/tools/lib/bpf/libbpf_probes.c > +++ b/tools/lib/bpf/libbpf_probes.c > @@ -3,7 +3,11 @@ > /* Copyright (c) 2018 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 void > prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, > size_t insns_cnt, int kernel_version, char *buf, size_t buf_len, > @@ -112,3 +147,31 @@ 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, > + int kernel_version, __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), kernel_version, > + buf, sizeof(buf), ifindex); > + res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); Why not look at errno directly? > + > + 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 >