diff mbox series

[RFC,bpf-next,v3,6/9] tools: bpftool: add probes for eBPF helper functions

Message ID 20190103140239.23548-7-quentin.monnet@netronome.com
State RFC
Delegated to: BPF Maintainers
Headers show
Series tools: bpftool: add probes for system and device | expand

Commit Message

Quentin Monnet Jan. 3, 2019, 2:02 p.m. UTC
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.monnet@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(+)

Comments

Stanislav Fomichev Jan. 3, 2019, 5:15 p.m. UTC | #1
On Thu, Jan 3, 2019 at 6:03 AM Quentin Monnet
<quentin.monnet@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.monnet@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
>
Quentin Monnet Jan. 3, 2019, 5:26 p.m. UTC | #2
2019-01-03 09:15 UTC-0800 ~ Stanislav Fomichev <sdf@google.com>
> On Thu, Jan 3, 2019 at 6:03 AM Quentin Monnet
> <quentin.monnet@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.monnet@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/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)?

No reason (other than me forgetting about that enum when writing the
function), I'll fix it, thanks!
diff mbox series

Patch

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,
+				 __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;
+}