* [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device
@ 2018-12-20 12:24 Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 1/9] tools: bpftool: add basic probe capability, probe syscall and kversion Quentin Monnet
` (8 more replies)
0 siblings, 9 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Hi,
This set adds a new command to bpftool in order to dump a list of
eBPF-related parameters for the system (or for a specific network
device) to the console. Once again, this is based on a suggestion from
Daniel.
At this time, output includes:
- Availability of bpf() system call
- Availability of bpf() system call for unprivileged users
- JIT status (enabled or not, with or without debugging traces)
- JIT hardening status
- JIT kallsyms exports status
- Status of kernel compilation options related to BPF features
- Release number of the running kernel
- Availability of known eBPF program types
- Availability of known eBPF map types
- Availability of known eBPF helper functions
There are three different ways to dump this information at this time:
- Plain output dumps probe results in plain text. It is the most
flexible options for providing descriptive output to the user, but
should not be relied upon for parsing the output.
- JSON output is supported.
- A third mode, available through the "macros" keyword appended to the
command line, dumps some of those parameters (not all) as a series of
"#define" directives, that can be included into a C header file for
example.
Probes for supported program and map types, and supported helpers, are
directly added to libbpf, so that other applications (or selftests) can
reuse them as necessary.
If the user does not have root privileges (or more precisely, the
CAP_SYS_ADMIN capability) detection will be erroneous for most
parameters. Therefore, forbid non-root users to run the command.
v2 (please also refer to individual patches' history):
- Move probes for prog/map types, helpers, from bpftool to libbpf.
- Move C-style output as a separate patch, and restrict it to a subset of
collected information (bpf() availability, prog/map types, helpers).
- Now probe helpers with all supported program types, and display a list of
compatible program types (as supported on the system) for each helper.
- NOT addressed: grouping compilation options for kernel into subsections
(patch 3) (I don't see an easy way of grouping them at the moment, please
see also the discussion on v1 thread).
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Jesper Dangaard Brouer <brouer@redhat.com>
Cc: Stanislav Fomichev <sdf@google.com>
---
I'm sending this v2 as a RFC for feedback, mostly about the relocation of
the probes into libbpf and for the changes in helper probing. As mentioned
in v2 history, the grouping of kernel config options (suggested by Daniel)
is not addressed in this series.
I still expect some discussions on this set, so I do not mind at all being
delayed after the merge window.
Quentin Monnet (9):
tools: bpftool: add basic probe capability, probe syscall and kversion
tools: bpftool: add probes for /proc/ eBPF parameters
tools: bpftool: add probes for kernel configuration options
tools: bpftool: add probes for eBPF program types
tools: bpftool: add probes for eBPF map types
tools: bpftool: add probes for eBPF helper functions
tools: bpftool: add C-style "#define" output for probes
tools: bpftool: add probes for a network device
tools: bpftool: add bash completion for bpftool probes
.../bpftool/Documentation/bpftool-cgroup.rst | 1 +
.../bpftool/Documentation/bpftool-feature.rst | 85 +++
.../bpf/bpftool/Documentation/bpftool-map.rst | 1 +
.../bpf/bpftool/Documentation/bpftool-net.rst | 1 +
.../bpftool/Documentation/bpftool-perf.rst | 1 +
.../bpftool/Documentation/bpftool-prog.rst | 1 +
tools/bpf/bpftool/Documentation/bpftool.rst | 1 +
tools/bpf/bpftool/bash-completion/bpftool | 19 +
tools/bpf/bpftool/common.c | 2 +-
tools/bpf/bpftool/feature.c | 702 ++++++++++++++++++
tools/bpf/bpftool/main.c | 3 +-
tools/bpf/bpftool/main.h | 5 +
tools/bpf/bpftool/map.c | 4 +-
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 9 +
tools/lib/bpf/libbpf.map | 3 +
tools/lib/bpf/libbpf_probes.c | 178 +++++
17 files changed, 1014 insertions(+), 4 deletions(-)
create mode 100644 tools/bpf/bpftool/Documentation/bpftool-feature.rst
create mode 100644 tools/bpf/bpftool/feature.c
create mode 100644 tools/lib/bpf/libbpf_probes.c
--
2.17.1
^ permalink raw reply [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 1/9] tools: bpftool: add basic probe capability, probe syscall and kversion
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 2/9] tools: bpftool: add probes for /proc/ eBPF parameters Quentin Monnet
` (7 subsequent siblings)
8 siblings, 0 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Add a new component and command for bpftool, in order to probe the
system to dump a set of eBPF-related parameters so that users can know
what features are available on the system.
Parameters are dumped in plain or JSON output (with -j/-p options).
The current patch introduces probing of two simple parameters:
availability of the bpf() system call, and kernel version. Later commits
will add other probes. Kernel version will be used in some of those
later commits to check e.g. kprobes availability.
Sample output:
# bpftool feature probe kernel
Scanning system call and kernel version...
Kernel release is 4.19.0
bpf() syscall is available
# bpftool --json --pretty feature probe kernel
{
"syscall_config": {
"kernel_version_code": 267008,
"have_bpf_syscall": true
}
}
The optional "kernel" keyword enforces probing of the current system,
which is the only possible behaviour at this stage. It can be safely
omitted.
The feature comes with the relevant man page, but bash completion will
come in a dedicated commit.
v2:
- Remove C-style macros output from this patch.
- Even though kernel version is no longer needed for testing kprobes
availability, note that we still collect it in this patch so that
bpftool gets able to probe (in next patches) older kernels as well.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
.../bpftool/Documentation/bpftool-cgroup.rst | 1 +
.../bpftool/Documentation/bpftool-feature.rst | 60 +++++++
.../bpf/bpftool/Documentation/bpftool-map.rst | 1 +
.../bpf/bpftool/Documentation/bpftool-net.rst | 1 +
.../bpftool/Documentation/bpftool-perf.rst | 1 +
.../bpftool/Documentation/bpftool-prog.rst | 1 +
tools/bpf/bpftool/Documentation/bpftool.rst | 1 +
tools/bpf/bpftool/feature.c | 153 ++++++++++++++++++
tools/bpf/bpftool/main.c | 3 +-
tools/bpf/bpftool/main.h | 1 +
10 files changed, 222 insertions(+), 1 deletion(-)
create mode 100644 tools/bpf/bpftool/Documentation/bpftool-feature.rst
create mode 100644 tools/bpf/bpftool/feature.c
diff --git a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
index d07ccf8a23f7..d43fce568ef7 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
@@ -142,5 +142,6 @@ SEE ALSO
**bpftool**\ (8),
**bpftool-prog**\ (8),
**bpftool-map**\ (8),
+ **bpftool-feature**\ (8),
**bpftool-net**\ (8),
**bpftool-perf**\ (8)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
new file mode 100644
index 000000000000..40ac13c0b782
--- /dev/null
+++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
@@ -0,0 +1,60 @@
+===============
+bpftool-feature
+===============
+-------------------------------------------------------------------------------
+tool for inspection of eBPF-related parameters for Linux kernel or net device
+-------------------------------------------------------------------------------
+
+:Manual section: 8
+
+SYNOPSIS
+========
+
+ **bpftool** [*OPTIONS*] **feature** *COMMAND*
+
+ *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
+
+ *COMMANDS* := { **probe** | **help** }
+
+MAP COMMANDS
+=============
+
+| **bpftool** **feature probe** [**kernel**]
+| **bpftool** **feature help**
+
+DESCRIPTION
+===========
+ **bpftool feature probe** [**kernel**]
+ Probe the running kernel and dump a number of eBPF-related
+ parameters, such as availability of the **bpf()** system call.
+
+ Keyword **kernel** can be omitted.
+
+ **bpftool feature help**
+ Print short help message.
+
+OPTIONS
+=======
+ -h, --help
+ Print short generic help message (similar to **bpftool help**).
+
+ -v, --version
+ Print version number (similar to **bpftool version**).
+
+ -j, --json
+ Generate JSON output. For commands that cannot produce JSON, this
+ option has no effect.
+
+ -p, --pretty
+ Generate human-readable JSON output. Implies **-j**.
+
+SEE ALSO
+========
+ **bpf**\ (2),
+ **bpf-helpers**\ (7),
+ **bpftool**\ (8),
+ **bpftool-prog**\ (8),
+ **bpftool-map**\ (8),
+ **bpftool-cgroup**\ (8),
+ **bpftool-net**\ (8),
+ **bpftool-perf**\ (8)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index 64b001b4f777..eafdc9f76694 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -236,5 +236,6 @@ SEE ALSO
**bpftool**\ (8),
**bpftool-prog**\ (8),
**bpftool-cgroup**\ (8),
+ **bpftool-feature**\ (8),
**bpftool-net**\ (8),
**bpftool-perf**\ (8)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-net.rst b/tools/bpf/bpftool/Documentation/bpftool-net.rst
index ed87c9b619ad..779dab3650ee 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-net.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-net.rst
@@ -142,4 +142,5 @@ SEE ALSO
**bpftool-prog**\ (8),
**bpftool-map**\ (8),
**bpftool-cgroup**\ (8),
+ **bpftool-feature**\ (8),
**bpftool-perf**\ (8)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-perf.rst b/tools/bpf/bpftool/Documentation/bpftool-perf.rst
index f4c5e5538bb8..bca5590a80d0 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-perf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-perf.rst
@@ -84,4 +84,5 @@ SEE ALSO
**bpftool-prog**\ (8),
**bpftool-map**\ (8),
**bpftool-cgroup**\ (8),
+ **bpftool-feature**\ (8),
**bpftool-net**\ (8)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index 58c8369b77dd..13b56102f528 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -258,5 +258,6 @@ SEE ALSO
**bpftool**\ (8),
**bpftool-map**\ (8),
**bpftool-cgroup**\ (8),
+ **bpftool-feature**\ (8),
**bpftool-net**\ (8),
**bpftool-perf**\ (8)
diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst
index e1677e81ed59..27153bb816ac 100644
--- a/tools/bpf/bpftool/Documentation/bpftool.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool.rst
@@ -72,5 +72,6 @@ SEE ALSO
**bpftool-prog**\ (8),
**bpftool-map**\ (8),
**bpftool-cgroup**\ (8),
+ **bpftool-feature**\ (8),
**bpftool-net**\ (8),
**bpftool-perf**\ (8)
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
new file mode 100644
index 000000000000..9bf8e786572e
--- /dev/null
+++ b/tools/bpf/bpftool/feature.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (c) 2018 Netronome Systems, Inc. */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include <linux/filter.h>
+#include <linux/limits.h>
+
+#include <bpf.h>
+
+#include "main.h"
+
+enum probe_component {
+ COMPONENT_UNSPEC,
+ COMPONENT_KERNEL,
+};
+
+/* Printing utility functions */
+
+static void
+print_bool_feature(const char *feat_name, const char *plain_name, bool res)
+{
+ if (json_output)
+ jsonw_bool_field(json_wtr, feat_name, res);
+ else
+ printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
+}
+
+static void
+print_start_section(const char *json_title, const char *plain_title)
+{
+ if (json_output) {
+ jsonw_name(json_wtr, json_title);
+ jsonw_start_object(json_wtr);
+ } else {
+ printf("%s\n", plain_title);
+ }
+}
+
+/* Probing functions */
+
+static int probe_kernel_version(void)
+{
+ int version, subversion, patchlevel, code = 0;
+ struct utsname utsn;
+
+ if (!uname(&utsn))
+ if (sscanf(utsn.release, "%d.%d.%d",
+ &version, &subversion, &patchlevel) == 3)
+ code = (version << 16) + (subversion << 8) + patchlevel;
+
+ if (json_output)
+ jsonw_uint_field(json_wtr, "kernel_version_code", code);
+ else if (code)
+ printf("Kernel release is %d.%d.%d\n",
+ version, subversion, patchlevel);
+ else
+ printf("Unable to parse kernel release number\n");
+
+ return code;
+}
+
+static bool probe_bpf_syscall(void)
+{
+ bool res;
+
+ bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0);
+ res = (errno != ENOSYS);
+
+ print_bool_feature("have_bpf_syscall",
+ "bpf() syscall",
+ res);
+
+ return res;
+}
+
+static int do_probe(int argc, char **argv)
+{
+ enum probe_component target = COMPONENT_UNSPEC;
+
+ /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
+ * Let's approximate, and restrict usage to root user only.
+ */
+ if (geteuid()) {
+ p_err("please run this command as root user");
+ return -1;
+ }
+
+ set_max_rlimit();
+
+ while (argc) {
+ if (is_prefix(*argv, "kernel")) {
+ if (target != COMPONENT_UNSPEC) {
+ p_err("component to probe already specified");
+ return -1;
+ }
+ target = COMPONENT_KERNEL;
+ NEXT_ARG();
+ } else {
+ p_err("expected no more arguments, 'kernel', got: '%s'?",
+ *argv);
+ return -1;
+ }
+ }
+
+ if (json_output)
+ jsonw_start_object(json_wtr);
+
+ print_start_section("syscall_config",
+ "Scanning system call and kernel version...");
+
+ probe_kernel_version();
+ probe_bpf_syscall();
+
+ if (json_output) {
+ /* End current "section" of probes */
+ jsonw_end_object(json_wtr);
+ /* End root object */
+ jsonw_end_object(json_wtr);
+ }
+
+ return 0;
+}
+
+static int do_help(int argc, char **argv)
+{
+ if (json_output) {
+ jsonw_null(json_wtr);
+ return 0;
+ }
+
+ fprintf(stderr,
+ "Usage: %s %s probe [kernel]\n"
+ " %s %s help\n"
+ "",
+ bin_name, argv[-2], bin_name, argv[-2]);
+
+ return 0;
+}
+
+static const struct cmd cmds[] = {
+ { "help", do_help },
+ { "probe", do_probe },
+ { 0 }
+};
+
+int do_feature(int argc, char **argv)
+{
+ return cmd_select(cmds, argc, argv, do_help);
+}
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index f44a1c2c4ea0..a9d5e9e6a732 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -56,7 +56,7 @@ static int do_help(int argc, char **argv)
" %s batch file FILE\n"
" %s version\n"
"\n"
- " OBJECT := { prog | map | cgroup | perf | net }\n"
+ " OBJECT := { prog | map | cgroup | perf | net | feature }\n"
" " HELP_SPEC_OPTIONS "\n"
"",
bin_name, bin_name, bin_name);
@@ -187,6 +187,7 @@ static const struct cmd cmds[] = {
{ "cgroup", do_cgroup },
{ "perf", do_perf },
{ "net", do_net },
+ { "feature", do_feature },
{ "version", do_version },
{ 0 }
};
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 052c91d4dc55..5cfc6601de9b 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -145,6 +145,7 @@ int do_cgroup(int argc, char **arg);
int do_perf(int argc, char **arg);
int do_net(int argc, char **arg);
int do_tracelog(int argc, char **arg);
+int do_feature(int argc, char **argv);
int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
int prog_parse_fd(int *argc, char ***argv);
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 2/9] tools: bpftool: add probes for /proc/ eBPF parameters
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 1/9] tools: bpftool: add basic probe capability, probe syscall and kversion Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options Quentin Monnet
` (6 subsequent siblings)
8 siblings, 0 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Add a set of probes to dump the eBPF-related parameters available from
/proc/: availability of bpf() syscall for unprivileged users,
JIT compiler status and hardening status, kallsyms exports status.
Sample output:
# bpftool feature probe kernel
Scanning system configuration...
bpf() syscall for unprivileged users is enabled
JIT compiler is disabled
JIT compiler hardening is disabled
JIT compiler kallsyms exports are disabled
...
# bpftool --json --pretty feature probe kernel
{
"system_config": {
"unprivileged_bpf_disabled": 0,
"bpf_jit_enable": 0,
"bpf_jit_harden": 0,
"bpf_jit_kallsyms": 0
},
...
}
These probes are skipped if procfs is not mounted.
v2:
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
tools/bpf/bpftool/feature.c | 168 ++++++++++++++++++++++++++++++++++++
1 file changed, 168 insertions(+)
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 9bf8e786572e..410d35857cf4 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -5,6 +5,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/utsname.h>
+#include <sys/vfs.h>
#include <linux/filter.h>
#include <linux/limits.h>
@@ -13,11 +14,29 @@
#include "main.h"
+#ifndef PROC_SUPER_MAGIC
+# define PROC_SUPER_MAGIC 0x9fa0
+#endif
+
enum probe_component {
COMPONENT_UNSPEC,
COMPONENT_KERNEL,
};
+/* Miscellaneous utility functions */
+
+static bool check_procfs(void)
+{
+ struct statfs st_fs;
+
+ if (statfs("/proc", &st_fs) < 0)
+ return false;
+ if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC)
+ return false;
+
+ return true;
+}
+
/* Printing utility functions */
static void
@@ -42,6 +61,135 @@ print_start_section(const char *json_title, const char *plain_title)
/* Probing functions */
+static int read_procfs(const char *path)
+{
+ char *endptr, *line = NULL;
+ size_t len = 0;
+ FILE *fd;
+ int res;
+
+ fd = fopen(path, "r");
+ if (!fd)
+ return -1;
+
+ res = getline(&line, &len, fd);
+ fclose(fd);
+ if (res < 0)
+ return -1;
+
+ errno = 0;
+ res = strtol(line, &endptr, 10);
+ if (errno || *line == '\0' || *endptr != '\n')
+ res = -1;
+ free(line);
+
+ return res;
+}
+
+static void probe_unprivileged_disabled(void)
+{
+ int res;
+
+ res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
+ if (json_output) {
+ jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
+ } else {
+ switch (res) {
+ case 0:
+ printf("bpf() syscall for unprivileged users is enabled\n");
+ break;
+ case 1:
+ printf("bpf() syscall restricted to privileged users\n");
+ break;
+ case -1:
+ printf("Unable to retrieve required privileges for bpf() syscall\n");
+ break;
+ default:
+ printf("bpf() syscall restriction has unknown value %d\n", res);
+ }
+ }
+}
+
+static void probe_jit_enable(void)
+{
+ int res;
+
+ res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
+ if (json_output) {
+ jsonw_int_field(json_wtr, "bpf_jit_enable", res);
+ } else {
+ switch (res) {
+ case 0:
+ printf("JIT compiler is disabled\n");
+ break;
+ case 1:
+ printf("JIT compiler is enabled\n");
+ break;
+ case 2:
+ printf("JIT compiler is enabled with debugging traces in kernel logs\n");
+ break;
+ case -1:
+ printf("Unable to retrieve JIT-compiler status\n");
+ break;
+ default:
+ printf("JIT-compiler status has unknown value %d\n",
+ res);
+ }
+ }
+}
+
+static void probe_jit_harden(void)
+{
+ int res;
+
+ res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
+ if (json_output) {
+ jsonw_int_field(json_wtr, "bpf_jit_harden", res);
+ } else {
+ switch (res) {
+ case 0:
+ printf("JIT compiler hardening is disabled\n");
+ break;
+ case 1:
+ printf("JIT compiler hardening is enabled for unprivileged users\n");
+ break;
+ case 2:
+ printf("JIT compiler hardening is enabled for all users\n");
+ break;
+ case -1:
+ printf("Unable to retrieve JIT hardening status\n");
+ break;
+ default:
+ printf("JIT hardening status has unknown value %d\n",
+ res);
+ }
+ }
+}
+
+static void probe_jit_kallsyms(void)
+{
+ int res;
+
+ res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
+ if (json_output) {
+ jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
+ } else {
+ switch (res) {
+ case 0:
+ printf("JIT compiler kallsyms exports are disabled\n");
+ break;
+ case 1:
+ printf("JIT compiler kallsyms exports are enabled for root\n");
+ break;
+ case -1:
+ printf("Unable to retrieve JIT kallsyms export status\n");
+ break;
+ default:
+ printf("JIT kallsyms exports status has unknown value %d\n", res);
+ }
+ }
+}
+
static int probe_kernel_version(void)
{
int version, subversion, patchlevel, code = 0;
@@ -109,6 +257,26 @@ static int do_probe(int argc, char **argv)
if (json_output)
jsonw_start_object(json_wtr);
+ switch (target) {
+ case COMPONENT_KERNEL:
+ case COMPONENT_UNSPEC:
+ print_start_section("system_config",
+ "Scanning system configuration...");
+ if (check_procfs()) {
+ probe_unprivileged_disabled();
+ probe_jit_enable();
+ probe_jit_harden();
+ probe_jit_kallsyms();
+ } else {
+ p_info("/* procfs not mounted, skipping related probes */");
+ }
+ if (json_output)
+ jsonw_end_object(json_wtr);
+ else
+ printf("\n");
+ break;
+ }
+
print_start_section("syscall_config",
"Scanning system call and kernel version...");
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 1/9] tools: bpftool: add basic probe capability, probe syscall and kversion Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 2/9] tools: bpftool: add probes for /proc/ eBPF parameters Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 17:40 ` Stanislav Fomichev
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types Quentin Monnet
` (5 subsequent siblings)
8 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Add probes to dump a number of options set (or not set) for compiling
the kernel image. These parameters provide information about what BPF
components should be available on the system. A number of them are not
directly related to eBPF, but are in fact used in the kernel as
conditions on which to compile, or not to compile, some of the eBPF
helper functions.
Sample output:
# bpftool feature probe kernel
Scanning system configuration...
...
CONFIG_BPF is set to y
CONFIG_BPF_SYSCALL is set to y
CONFIG_HAVE_EBPF_JIT is set to y
...
# bpftool --pretty --json feature probe kernel
{
"system_config": {
...
"CONFIG_BPF": "y",
"CONFIG_BPF_SYSCALL": "y",
"CONFIG_HAVE_EBPF_JIT": "y",
...
}
}
v2:
- Remove C-style macros output from this patch.
- NOT addressed: grouping of those config options into subsections
(I don't see an easy way of grouping them at the moment, please see
also the discussion on v1 thread).
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 410d35857cf4..238d7b80f426 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res)
printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
}
+static void print_kernel_option(const char *name, const char *value)
+{
+ char *endptr;
+ int res;
+
+ if (json_output) {
+ if (!value) {
+ jsonw_null_field(json_wtr, name);
+ return;
+ }
+ errno = 0;
+ res = strtol(value, &endptr, 0);
+ if (!errno && *endptr == '\n')
+ jsonw_int_field(json_wtr, name, res);
+ else
+ jsonw_string_field(json_wtr, name, value);
+ } else {
+ if (value)
+ printf("%s is set to %s\n", name, value);
+ else
+ printf("%s is not set\n", name);
+ }
+}
+
static void
print_start_section(const char *json_title, const char *plain_title)
{
@@ -190,6 +214,118 @@ static void probe_jit_kallsyms(void)
}
}
+static char *get_kernel_config_option(FILE *fd, const char *option)
+{
+ size_t line_n = 0, optlen = strlen(option);
+ char *res, *strval, *line = NULL;
+ ssize_t n;
+
+ rewind(fd);
+ while ((n = getline(&line, &line_n, fd)) > 0) {
+ if (strncmp(line, option, optlen))
+ continue;
+ /* Check we have at least '=', value, and '\n' */
+ if (strlen(line) < optlen + 3)
+ continue;
+ if (*(line + optlen) != '=')
+ continue;
+
+ /* Trim ending '\n' */
+ line[strlen(line) - 1] = '\0';
+
+ /* Copy and return config option value */
+ strval = line + optlen + 1;
+ res = strdup(strval);
+ free(line);
+ return res;
+ }
+ free(line);
+
+ return NULL;
+}
+
+static void probe_kernel_image_config(void)
+{
+ const char * const options[] = {
+ "CONFIG_BPF",
+ "CONFIG_BPF_SYSCALL",
+ "CONFIG_HAVE_EBPF_JIT",
+ "CONFIG_BPF_JIT",
+ "CONFIG_BPF_JIT_ALWAYS_ON",
+ "CONFIG_NET",
+ "CONFIG_XDP_SOCKETS",
+ "CONFIG_CGROUPS",
+ "CONFIG_CGROUP_BPF",
+ "CONFIG_CGROUP_NET_CLASSID",
+ "CONFIG_BPF_EVENTS",
+ "CONFIG_LWTUNNEL_BPF",
+ "CONFIG_NET_ACT_BPF",
+ "CONFIG_NET_CLS_ACT",
+ "CONFIG_NET_CLS_BPF",
+ "CONFIG_NET_SCH_INGRESS",
+ "CONFIG_XFRM",
+ "CONFIG_SOCK_CGROUP_DATA",
+ "CONFIG_IP_ROUTE_CLASSID",
+ "CONFIG_IPV6_SEG6_BPF",
+ "CONFIG_FUNCTION_ERROR_INJECTION",
+ "CONFIG_BPF_KPROBE_OVERRIDE",
+ "CONFIG_BPF_LIRC_MODE2",
+ "CONFIG_NETFILTER_XT_MATCH_BPF",
+ "CONFIG_TEST_BPF",
+ "CONFIG_BPFILTER",
+ "CONFIG_BPFILTER_UMH",
+ "CONFIG_BPF_STREAM_PARSER",
+ };
+ char *value, *buf = NULL;
+ struct utsname utsn;
+ char path[PATH_MAX];
+ size_t i, n;
+ ssize_t ret;
+ FILE *fd;
+
+ if (uname(&utsn))
+ goto no_config;
+
+ snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
+
+ fd = fopen(path, "r");
+ if (!fd && errno == ENOENT) {
+ /* Sometimes config is at /proc/config */
+ fd = fopen("/proc/config", "r");
+ }
+ if (!fd) {
+ p_err("can't open kernel config file: %s", strerror(errno));
+ goto no_config;
+ }
+ /* Sanity checks */
+ ret = getline(&buf, &n, fd);
+ ret = getline(&buf, &n, fd);
+ if (!buf || !ret) {
+ p_err("can't read from kernel config file: %s",
+ strerror(errno));
+ free(buf);
+ goto no_config;
+ }
+ if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
+ p_err("can't find correct kernel config file");
+ free(buf);
+ goto no_config;
+ }
+ free(buf);
+
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ value = get_kernel_config_option(fd, options[i]);
+ print_kernel_option(options[i], value);
+ free(value);
+ }
+ fclose(fd);
+ return;
+
+no_config:
+ for (i = 0; i < ARRAY_SIZE(options); i++)
+ print_kernel_option(options[i], NULL);
+}
+
static int probe_kernel_version(void)
{
int version, subversion, patchlevel, code = 0;
@@ -270,6 +406,7 @@ static int do_probe(int argc, char **argv)
} else {
p_info("/* procfs not mounted, skipping related probes */");
}
+ probe_kernel_image_config();
if (json_output)
jsonw_end_object(json_wtr);
else
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
` (2 preceding siblings ...)
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 17:45 ` Stanislav Fomichev
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types Quentin Monnet
` (4 subsequent siblings)
8 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Introduce probes for supported BPF program types in libbpf, and call it
from bpftool to test what types are available on the system. The probe
simply consists in loading a very simple program of that type and see if
the verifier complains or not.
Sample output:
# bpftool feature probe kernel
...
Scanning eBPF program types...
eBPF program_type socket_filter is available
eBPF program_type kprobe is available
eBPF program_type sched_cls is available
...
# bpftool --json --pretty feature probe kernel
{
...
"program_types": {
"have_socket_filter_prog_type": true,
"have_kprobe_prog_type": true,
"have_sched_cls_prog_type": true,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
---
tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/libbpf.h | 6 ++++
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
5 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 tools/lib/bpf/libbpf_probes.c
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 238d7b80f426..3ba0a0a5904c 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (c) 2018 Netronome Systems, Inc. */
+#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
@@ -11,6 +12,7 @@
#include <linux/limits.h>
#include <bpf.h>
+#include <libbpf.h>
#include "main.h"
@@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
}
}
+static void
+print_end_then_start_section(const char *json_title, const char *plain_title)
+{
+ if (json_output)
+ jsonw_end_object(json_wtr);
+ else
+ printf("\n");
+
+ print_start_section(json_title, plain_title);
+}
+
/* Probing functions */
static int read_procfs(const char *path)
@@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)
return res;
}
+static void
+probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
+ bool *supported_types)
+{
+ const char *plain_comment = "eBPF program_type ";
+ char feat_name[128], plain_desc[128];
+ size_t maxlen;
+ bool res;
+
+ res = bpf_probe_prog_type(prog_type, kernel_version, 0);
+
+ supported_types[prog_type] |= res;
+
+ maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
+ if (strlen(prog_type_name[prog_type]) > maxlen) {
+ p_info("program type name too long");
+ return;
+ }
+
+ sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
+ sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
+ print_bool_feature(feat_name, plain_desc, res);
+}
+
static int do_probe(int argc, char **argv)
{
enum probe_component target = COMPONENT_UNSPEC;
+ bool supported_types[128] = {};
+ int kernel_version;
+ unsigned int i;
/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
* Let's approximate, and restrict usage to root user only.
@@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)
print_start_section("syscall_config",
"Scanning system call and kernel version...");
- probe_kernel_version();
- probe_bpf_syscall();
+ kernel_version = probe_kernel_version();
+ if (!probe_bpf_syscall())
+ /* bpf() syscall unavailable, don't probe other BPF features */
+ goto exit_close_json;
+
+ print_end_then_start_section("program_types",
+ "Scanning eBPF program types...");
+
+ for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
+ probe_prog_type(i, kernel_version, supported_types);
+exit_close_json:
if (json_output) {
/* End current "section" of probes */
jsonw_end_object(json_wtr);
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
index 197b40f5b5c6..bfd9bfc82c3b 100644
--- a/tools/lib/bpf/Build
+++ b/tools/lib/bpf/Build
@@ -1 +1 @@
-libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
+libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 5f68d7b75215..f4bb2764ca9a 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -355,6 +355,12 @@ LIBBPF_API const struct bpf_line_info *
bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
__u32 insn_off, __u32 nr_skip);
+/*
+ * Probe for supported system features
+ */
+LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
+ int kernel_version, __u32 ifindex);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index cd02cd4e2cc3..6355e4c80a86 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_prog_type;
bpf_prog_attach;
bpf_prog_detach;
bpf_prog_detach2;
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
new file mode 100644
index 000000000000..2c5e0cdc9f2f
--- /dev/null
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/* Copyright (c) 2018 Netronome Systems, Inc. */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <linux/filter.h>
+#include <linux/kernel.h>
+
+#include "bpf.h"
+#include "libbpf.h"
+
+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,
+ __u32 ifindex)
+{
+ struct bpf_load_program_attr xattr = {};
+ int fd;
+
+ /* Some prog type require an expected_attach_type */
+ if (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR)
+ xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
+
+ xattr.prog_type = prog_type;
+ xattr.insns = insns;
+ xattr.insns_cnt = insns_cnt;
+ xattr.license = "GPL";
+ xattr.kern_version = kernel_version;
+ xattr.prog_ifindex = ifindex;
+
+ fd = bpf_load_program_xattr(&xattr, buf, buf_len);
+ if (fd >= 0)
+ close(fd);
+}
+
+bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
+ __u32 ifindex)
+{
+ struct bpf_insn insns[2] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN()
+ };
+
+ if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS)
+ /* nfp returns -EINVAL on exit(0) with TC offload */
+ insns[0].imm = 2;
+
+ errno = 0;
+ prog_load(prog_type, insns, ARRAY_SIZE(insns), kernel_version,
+ NULL, 0, ifindex);
+
+ return errno != EINVAL && errno != EOPNOTSUPP;
+}
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
` (3 preceding siblings ...)
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 17:47 ` Stanislav Fomichev
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions Quentin Monnet
` (3 subsequent siblings)
8 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Add new probes for eBPF map types, to detect what are the ones available
on the system. Try creating one map of each type, and see if the kernel
complains.
Sample output:
# bpftool feature probe kernel
...
Scanning eBPF map types...
eBPF map_type hash is available
eBPF map_type array is available
eBPF map_type prog_array is available
...
# bpftool --json --pretty feature probe kernel
{
...
"map_types": {
"have_hash_map_type": true,
"have_array_map_type": true,
"have_prog_array_map_type": true,
...
}
}
v2:
- Move probes from bpftool to libbpf.
- Remove C-style macros output from this patch.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
---
tools/bpf/bpftool/feature.c | 26 +++++++++++++++
tools/bpf/bpftool/main.h | 3 ++
tools/bpf/bpftool/map.c | 4 ++-
tools/lib/bpf/libbpf.h | 1 +
tools/lib/bpf/libbpf.map | 1 +
tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
6 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 3ba0a0a5904c..3c44953ded5a 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
print_bool_feature(feat_name, plain_desc, res);
}
+static void probe_map_type(enum bpf_map_type map_type)
+{
+ const char *plain_comment = "eBPF map_type ";
+ char feat_name[128], plain_desc[128];
+ size_t maxlen;
+ bool res;
+
+ res = bpf_probe_map_type(map_type, 0);
+
+ maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
+ if (strlen(map_type_name[map_type]) > maxlen) {
+ p_info("map type name too long");
+ return;
+ }
+
+ sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
+ sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
+ print_bool_feature(feat_name, plain_desc, res);
+}
+
static int do_probe(int argc, char **argv)
{
enum probe_component target = COMPONENT_UNSPEC;
@@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
probe_prog_type(i, kernel_version, supported_types);
+ print_end_then_start_section("map_types",
+ "Scanning eBPF map types...");
+
+ for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
+ probe_map_type(i);
+
exit_close_json:
if (json_output) {
/* End current "section" of probes */
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 5cfc6601de9b..d7dd84d3c660 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
[BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
};
+extern const char * const map_type_name[];
+extern const size_t map_type_name_size;
+
enum bpf_obj_type {
BPF_OBJ_UNKNOWN,
BPF_OBJ_PROG,
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index 2037e3dc864b..b73985589929 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -21,7 +21,7 @@
#include "json_writer.h"
#include "main.h"
-static const char * const map_type_name[] = {
+const char * const map_type_name[] = {
[BPF_MAP_TYPE_UNSPEC] = "unspec",
[BPF_MAP_TYPE_HASH] = "hash",
[BPF_MAP_TYPE_ARRAY] = "array",
@@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
[BPF_MAP_TYPE_STACK] = "stack",
};
+const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
+
static bool map_is_per_cpu(__u32 type)
{
return type == BPF_MAP_TYPE_PERCPU_HASH ||
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f4bb2764ca9a..202c1ee5c579 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -360,6 +360,7 @@ 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);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 6355e4c80a86..c08f4c726e8e 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_map_type;
bpf_probe_prog_type;
bpf_prog_attach;
bpf_prog_detach;
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 2c5e0cdc9f2f..796fe1e66169 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
return errno != EINVAL && errno != EOPNOTSUPP;
}
+
+bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
+{
+ int key_size, value_size, max_entries, map_flags;
+ struct bpf_create_map_attr attr = {};
+ int fd = -1, fd_inner;
+
+ key_size = sizeof(__u32);
+ value_size = sizeof(__u32);
+ max_entries = 1;
+ map_flags = 0;
+
+ if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
+ key_size = sizeof(__u64);
+ value_size = sizeof(__u64);
+ map_flags = BPF_F_NO_PREALLOC;
+ } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
+ value_size = sizeof(__u64);
+ } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
+ map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
+ key_size = sizeof(struct bpf_cgroup_storage_key);
+ value_size = sizeof(__u64);
+ max_entries = 0;
+ } else if (map_type == BPF_MAP_TYPE_QUEUE ||
+ map_type == BPF_MAP_TYPE_STACK) {
+ key_size = 0;
+ }
+
+ if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
+ map_type == BPF_MAP_TYPE_HASH_OF_MAPS) {
+ /* TODO: probe for device, once libbpf has a function to create
+ * map-in-map for offload
+ */
+ if (ifindex)
+ return false;
+
+ fd_inner = bpf_create_map(BPF_MAP_TYPE_HASH,
+ sizeof(__u32), sizeof(__u32), 1, 0);
+ if (fd_inner < 0)
+ return false;
+ fd = bpf_create_map_in_map(map_type, NULL, sizeof(__u32),
+ fd_inner, 1, 0);
+ close(fd_inner);
+ } else {
+ /* Note: No other restriction on map type probes for offload */
+ attr.map_type = map_type;
+ attr.key_size = key_size;
+ attr.value_size = value_size;
+ attr.max_entries = max_entries;
+ attr.map_flags = map_flags;
+ attr.map_ifindex = ifindex;
+
+ fd = bpf_create_map_xattr(&attr);
+ }
+ if (fd >= 0)
+ close(fd);
+
+ return fd >= 0;
+}
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
` (4 preceding siblings ...)
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 17:53 ` Stanislav Fomichev
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes Quentin Monnet
` (2 subsequent siblings)
8 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
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 <quentin.monnet@netronome.com>
---
.../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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
#include <unistd.h>
+#include <net/if.h>
#include <linux/filter.h>
#include <linux/kernel.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 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 ");
+
+ 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
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
` (5 preceding siblings ...)
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 17:25 ` Daniel Borkmann
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 8/9] tools: bpftool: add probes for a network device Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 9/9] tools: bpftool: add bash completion for bpftool probes Quentin Monnet
8 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Make bpftool able to dump a subset of the parameters collected by
probing the system as a listing of C-style #define macros, so that
external projects can reuse the result of this probing and build
BPF-based project in accordance with the features available on the
system.
The new "macros" keyword is used to select this output. An additional
"prefix" keyword is added so that users can select a custom prefix for
macro names, in order to avoid any namespace conflict.
Sample output:
# bpftool feature probe kernel macros prefix FOO_
/*** System call availability ***/
#define FOO_HAVE_BPF_SYSCALL
/*** eBPF program types ***/
#define FOO_HAVE_SOCKET_FILTER_PROG_TYPE
#define FOO_HAVE_KPROBE_PROG_TYPE
#define FOO_HAVE_SCHED_CLS_PROG_TYPE
...
/*** eBPF map types ***/
#define FOO_HAVE_HASH_MAP_TYPE
#define FOO_HAVE_ARRAY_MAP_TYPE
#define FOO_HAVE_PROG_ARRAY_MAP_TYPE
...
/*** eBPF helper functions ***/
...
#define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
"lwt_xmit " \
"sk_skb "
#define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
"xdp "
#define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
"kprobe " \
"tracepoint " \
"perf_event " \
"raw_tracepoint "
...
v2:
- #define-based output added as a distinct patch.
- "HAVE_" prefix appended to all macro names.
- Output limited to bpf() syscall availability, BPF prog and map types,
helper functions. In this version kernel config options, procfs
parameter or kernel version are intentionally left aside.
- Following the change on helper probes, format for helper probes in
this output style has changed (now a list of compatible program
types).
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
---
.../bpftool/Documentation/bpftool-feature.rst | 13 +-
tools/bpf/bpftool/feature.c | 138 ++++++++++++++----
2 files changed, 120 insertions(+), 31 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
index 255e3b3629a0..53092995f46b 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
@@ -19,15 +19,24 @@ SYNOPSIS
MAP COMMANDS
=============
-| **bpftool** **feature probe** [**kernel**]
+| **bpftool** **feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
| **bpftool** **feature help**
DESCRIPTION
===========
- **bpftool feature probe** [**kernel**]
+ **bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
Probe the running kernel and dump a number of eBPF-related
parameters, such as availability of the **bpf()** system call.
+ If the **macros** keyword (but not the **-j** option) is
+ passed, a subset of the output is dumped as a list of
+ **#define** macros that are ready to be included in a C
+ header file, for example. If, additionally, **prefix** is
+ used to define a *PREFIX*, the provided string will be used
+ as a prefix to the names of the macros: this can be used to
+ avoid conflicts on macro names when including the output of
+ this command as a header file.
+
Keyword **kernel** can be omitted.
Note that when probed, some eBPF helpers (e.g.
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index bc483823014b..cd84617312dc 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -44,13 +44,25 @@ static bool check_procfs(void)
return true;
}
+static void uppercase(char *str, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len && str[i] != '\0'; i++)
+ str[i] = toupper(str[i]);
+}
+
/* Printing utility functions */
static void
-print_bool_feature(const char *feat_name, const char *plain_name, bool res)
+print_bool_feature(const char *feat_name, const char *plain_name,
+ const char *define_name, bool res, const char *define_prefix)
{
if (json_output)
jsonw_bool_field(json_wtr, feat_name, res);
+ else if (define_prefix)
+ printf("#define %s%sHAVE_%s\n", define_prefix,
+ res ? "" : "NO_", define_name);
else
printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
}
@@ -60,6 +72,8 @@ static void print_kernel_option(const char *name, const char *value)
char *endptr;
int res;
+ /* No support for C-style ouptut */
+
if (json_output) {
if (!value) {
jsonw_null_field(json_wtr, name);
@@ -80,25 +94,31 @@ static void print_kernel_option(const char *name, const char *value)
}
static void
-print_start_section(const char *json_title, const char *plain_title)
+print_start_section(const char *json_title, const char *plain_title,
+ const char *define_comment, const char *define_prefix)
{
if (json_output) {
jsonw_name(json_wtr, json_title);
jsonw_start_object(json_wtr);
+ } else if (define_prefix) {
+ printf("%s\n", define_comment);
} else {
printf("%s\n", plain_title);
}
}
static void
-print_end_then_start_section(const char *json_title, const char *plain_title)
+print_end_then_start_section(const char *json_title, const char *plain_title,
+ const char *define_comment,
+ const char *define_prefix)
{
if (json_output)
jsonw_end_object(json_wtr);
else
printf("\n");
- print_start_section(json_title, plain_title);
+ print_start_section(json_title, plain_title, define_comment,
+ define_prefix);
}
/* Probing functions */
@@ -132,6 +152,8 @@ static void probe_unprivileged_disabled(void)
{
int res;
+ /* No support for C-style ouptut */
+
res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
if (json_output) {
jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
@@ -156,6 +178,8 @@ static void probe_jit_enable(void)
{
int res;
+ /* No support for C-style ouptut */
+
res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_enable", res);
@@ -184,6 +208,8 @@ static void probe_jit_harden(void)
{
int res;
+ /* No support for C-style ouptut */
+
res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_harden", res);
@@ -212,6 +238,8 @@ static void probe_jit_kallsyms(void)
{
int res;
+ /* No support for C-style ouptut */
+
res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
@@ -344,7 +372,7 @@ static void probe_kernel_image_config(void)
print_kernel_option(options[i], NULL);
}
-static int probe_kernel_version(void)
+static int probe_kernel_version(const char *define_prefix)
{
int version, subversion, patchlevel, code = 0;
struct utsname utsn;
@@ -354,6 +382,10 @@ static int probe_kernel_version(void)
&version, &subversion, &patchlevel) == 3)
code = (version << 16) + (subversion << 8) + patchlevel;
+ if (define_prefix)
+ /* Nothing currently displayed for this ouptut */
+ return code;
+
if (json_output)
jsonw_uint_field(json_wtr, "kernel_version_code", code);
else if (code)
@@ -365,7 +397,7 @@ static int probe_kernel_version(void)
return code;
}
-static bool probe_bpf_syscall(void)
+static bool probe_bpf_syscall(const char *define_prefix)
{
bool res;
@@ -374,17 +406,18 @@ static bool probe_bpf_syscall(void)
print_bool_feature("have_bpf_syscall",
"bpf() syscall",
- res);
+ "BPF_SYSCALL",
+ res, define_prefix);
return res;
}
static void
probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
- bool *supported_types)
+ bool *supported_types, const char *define_prefix)
{
+ char feat_name[128], plain_desc[128], define_name[128];
const char *plain_comment = "eBPF program_type ";
- char feat_name[128], plain_desc[128];
size_t maxlen;
bool res;
@@ -399,14 +432,18 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
}
sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
+ sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]);
+ uppercase(define_name, sizeof(define_name));
sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
- print_bool_feature(feat_name, plain_desc, res);
+ print_bool_feature(feat_name, plain_desc, define_name, res,
+ define_prefix);
}
-static void probe_map_type(enum bpf_map_type map_type)
+static void
+probe_map_type(enum bpf_map_type map_type, const char *define_prefix)
{
+ char feat_name[128], plain_desc[128], define_name[128];
const char *plain_comment = "eBPF map_type ";
- char feat_name[128], plain_desc[128];
size_t maxlen;
bool res;
@@ -419,18 +456,23 @@ static void probe_map_type(enum bpf_map_type map_type)
}
sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
+ sprintf(define_name, "%s_map_type", map_type_name[map_type]);
+ uppercase(define_name, sizeof(define_name));
sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
- print_bool_feature(feat_name, plain_desc, res);
+ print_bool_feature(feat_name, plain_desc, define_name, res,
+ define_prefix);
}
static void
probe_helper(__u32 id, const char *name, int kernel_version,
- bool *supported_types)
+ bool *supported_types, const char *define_prefix)
{
- char feat_name[128], plain_desc[128];
+ char feat_name[128], plain_desc[128], define_name[128];
unsigned int i;
sprintf(feat_name, "%s_compat_list", name);
+ sprintf(define_name, "%s_helper_compat_list", name);
+ uppercase(define_name, sizeof(define_name));
sprintf(plain_desc,
"eBPF helper %s supported for program types:",
name);
@@ -438,6 +480,8 @@ probe_helper(__u32 id, const char *name, int kernel_version,
if (json_output) {
jsonw_name(json_wtr, feat_name);
jsonw_start_array(json_wtr);
+ } else if (define_prefix) {
+ printf("#define %s%s\t\"\"", define_prefix, define_name);
} else {
printf("%s", plain_desc);
}
@@ -452,19 +496,22 @@ probe_helper(__u32 id, const char *name, int kernel_version,
if (json_output)
jsonw_string(json_wtr, prog_type_name[i]);
+ else if (define_prefix)
+ printf("\t\\\n\t\"%s \"", prog_type_name[i]);
else
printf(" %s", prog_type_name[i]);
}
if (json_output)
jsonw_end_array(json_wtr);
- else
+ else /* For both C-style and plain output */
printf("\n");
}
static int do_probe(int argc, char **argv)
{
enum probe_component target = COMPONENT_UNSPEC;
+ const char *define_prefix = NULL;
bool supported_types[128] = {};
int kernel_version;
unsigned int i;
@@ -487,21 +534,45 @@ static int do_probe(int argc, char **argv)
}
target = COMPONENT_KERNEL;
NEXT_ARG();
+ } else if (is_prefix(*argv, "macros") && !define_prefix) {
+ define_prefix = "";
+ NEXT_ARG();
+ } else if (is_prefix(*argv, "prefix")) {
+ if (!define_prefix) {
+ p_err("'prefix' argument can only be use after 'macros'");
+ return -1;
+ }
+ if (strcmp(define_prefix, "")) {
+ p_err("'prefix' already defined");
+ return -1;
+ }
+ NEXT_ARG();
+
+ if (!REQ_ARGS(1))
+ return -1;
+ define_prefix = GET_ARG();
} else {
- p_err("expected no more arguments, 'kernel', got: '%s'?",
+ p_err("expected no more arguments, 'kernel', 'macros' or 'prefix', got: '%s'?",
*argv);
return -1;
}
}
- if (json_output)
+ if (json_output) {
+ define_prefix = NULL;
jsonw_start_object(json_wtr);
+ }
switch (target) {
case COMPONENT_KERNEL:
case COMPONENT_UNSPEC:
+ if (define_prefix)
+ break;
+
print_start_section("system_config",
- "Scanning system configuration...");
+ "Scanning system configuration...",
+ NULL, /* define_comment never used here */
+ NULL); /* define_prefix always NULL here */
if (check_procfs()) {
probe_unprivileged_disabled();
probe_jit_enable();
@@ -519,31 +590,40 @@ static int do_probe(int argc, char **argv)
}
print_start_section("syscall_config",
- "Scanning system call and kernel version...");
+ "Scanning system call and kernel version...",
+ "/*** System call availability ***/",
+ define_prefix);
- kernel_version = probe_kernel_version();
- if (!probe_bpf_syscall())
+ kernel_version = probe_kernel_version(define_prefix);
+ if (!probe_bpf_syscall(define_prefix))
/* bpf() syscall unavailable, don't probe other BPF features */
goto exit_close_json;
print_end_then_start_section("program_types",
- "Scanning eBPF program types...");
+ "Scanning eBPF program types...",
+ "/*** eBPF program types ***/",
+ define_prefix);
for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
- probe_prog_type(i, kernel_version, supported_types);
+ probe_prog_type(i, kernel_version, supported_types,
+ define_prefix);
print_end_then_start_section("map_types",
- "Scanning eBPF map types...");
+ "Scanning eBPF map types...",
+ "/*** eBPF map types ***/",
+ define_prefix);
for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
- probe_map_type(i);
+ probe_map_type(i, define_prefix);
print_end_then_start_section("helpers",
- "Scanning eBPF helper functions...");
+ "Scanning eBPF helper functions...",
+ "/*** eBPF helper functions ***/",
+ define_prefix);
for (i = 1; i < ARRAY_SIZE(helper_name); i++)
probe_helper(i, helper_name[i], kernel_version,
- supported_types);
+ supported_types, define_prefix);
exit_close_json:
if (json_output) {
@@ -564,7 +644,7 @@ static int do_help(int argc, char **argv)
}
fprintf(stderr,
- "Usage: %s %s probe [kernel]\n"
+ "Usage: %s %s probe [kernel] [macros [prefix PREFIX]]\n"
" %s %s help\n"
"",
bin_name, argv[-2], bin_name, argv[-2]);
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 8/9] tools: bpftool: add probes for a network device
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
` (6 preceding siblings ...)
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 9/9] tools: bpftool: add bash completion for bpftool probes Quentin Monnet
8 siblings, 0 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
bpftool gained support for probing the current system in order to see
what program and map types, and what helpers are available on that
system. This patch adds the possibility to pass an interface index to
libbpf (and hence to the kernel) when trying to load the programs or to
create the maps, in order to see what items a given network device can
support.
A new keyword "dev <ifname>" can be used as an alternative to "kernel"
to indicate that the given device should be tested. If no target ("dev"
or "kernel") is specified bpftool defaults to probing the kernel.
Sample output:
# bpftool -p feature probe dev lo
{
"syscall_config": {
"kernel_version_code": 267008,
"have_bpf_syscall": true
},
"program_types": {
"have_sched_cls_prog_type": false,
"have_xdp_prog_type": false
},
...
}
As the target is a network device, /proc/ parameters and kernel
configuration are NOT dumped. Availability of the bpf() syscall and
kernel version are still probed, as they are necessary for the remaining
probes (although kernel version is no longer needed on the latest
kernels, it is required for probing availability of kprobes on older
kernels).
Among the program types, only the ones that can be offloaded are probed.
All map types are probed, as there is no specific rule telling which one
could or could not be supported by a device in the future. All helpers
are probed as well, for all supported program types.
Caveat: as bpftool does not attempt to attach programs to the device at
the moment, probes do not entirely reflect what the device accepts:
typically, for Netronome's nfp, results will announce that TC cls
offload is available even if support has been deactivated (with e.g.
ethtool -K eth1 hw-tc-offload off).
v2:
- All helpers are probed, whereas previous version would only probe the
ones compatible with an offload-able program type. This is because we
do not keep a default compatible program type for each helper anymore.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
.../bpftool/Documentation/bpftool-feature.rst | 18 +++++-
tools/bpf/bpftool/common.c | 2 +-
tools/bpf/bpftool/feature.c | 58 +++++++++++++++----
tools/bpf/bpftool/main.h | 1 +
4 files changed, 65 insertions(+), 14 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
index 53092995f46b..8d489a26e3c9 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
@@ -19,14 +19,18 @@ SYNOPSIS
MAP COMMANDS
=============
-| **bpftool** **feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
+| **bpftool** **feature probe** [*COMPONENT*] [**macros** [**prefix** *PREFIX*]]
| **bpftool** **feature help**
+|
+| *COMPONENT* := { **kernel** | **dev** *NAME* }
DESCRIPTION
===========
**bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
Probe the running kernel and dump a number of eBPF-related
- parameters, such as availability of the **bpf()** system call.
+ parameters, such as availability of the **bpf()** system call,
+ JIT status, eBPF program types availability, eBPF helper
+ functions availability, and more.
If the **macros** keyword (but not the **-j** option) is
passed, a subset of the output is dumped as a list of
@@ -37,12 +41,20 @@ DESCRIPTION
avoid conflicts on macro names when including the output of
this command as a header file.
- Keyword **kernel** can be omitted.
+ Keyword **kernel** can be omitted. If no probe target is
+ specified, probing the kernel is the default behaviour.
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 probe dev** *NAME* [**macros** [**prefix** *PREFIX*]]
+ Probe network device for supported eBPF features and dump
+ results to the console.
+
+ The two keywords **macros** and **prefix** have the same
+ role as when probing the kernel.
+
**bpftool feature help**
Print short help message.
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index 897483457bf0..cf454e8eca52 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -542,7 +542,7 @@ static int read_sysfs_hex_int(char *path)
return strtol(vendor_id_buf, NULL, 0);
}
-static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
+int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
{
char full_path[64];
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index cd84617312dc..f1ccd91c1f9f 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -5,6 +5,7 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
+#include <net/if.h>
#include <sys/utsname.h>
#include <sys/vfs.h>
@@ -23,6 +24,7 @@
enum probe_component {
COMPONENT_UNSPEC,
COMPONENT_KERNEL,
+ COMPONENT_DEVICE,
};
#define BPF_HELPER_MAKE_ENTRY(name) [BPF_FUNC_ ## name] = "bpf_" # name
@@ -414,14 +416,24 @@ static bool probe_bpf_syscall(const char *define_prefix)
static void
probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
- bool *supported_types, const char *define_prefix)
+ bool *supported_types, const char *define_prefix, __u32 ifindex)
{
char feat_name[128], plain_desc[128], define_name[128];
const char *plain_comment = "eBPF program_type ";
size_t maxlen;
bool res;
- res = bpf_probe_prog_type(prog_type, kernel_version, 0);
+ if (ifindex)
+ /* Only test offload-able program types */
+ switch (prog_type) {
+ case BPF_PROG_TYPE_SCHED_CLS:
+ case BPF_PROG_TYPE_XDP:
+ break;
+ default:
+ return;
+ }
+
+ res = bpf_probe_prog_type(prog_type, kernel_version, ifindex);
supported_types[prog_type] |= res;
@@ -440,14 +452,15 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
}
static void
-probe_map_type(enum bpf_map_type map_type, const char *define_prefix)
+probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
+ __u32 ifindex)
{
char feat_name[128], plain_desc[128], define_name[128];
const char *plain_comment = "eBPF map_type ";
size_t maxlen;
bool res;
- res = bpf_probe_map_type(map_type, 0);
+ res = bpf_probe_map_type(map_type, ifindex);
maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
if (strlen(map_type_name[map_type]) > maxlen) {
@@ -465,7 +478,7 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix)
static void
probe_helper(__u32 id, const char *name, int kernel_version,
- bool *supported_types, const char *define_prefix)
+ bool *supported_types, const char *define_prefix, __u32 ifindex)
{
char feat_name[128], plain_desc[128], define_name[128];
unsigned int i;
@@ -514,7 +527,9 @@ static int do_probe(int argc, char **argv)
const char *define_prefix = NULL;
bool supported_types[128] = {};
int kernel_version;
+ __u32 ifindex = 0;
unsigned int i;
+ char *ifname;
/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
* Let's approximate, and restrict usage to root user only.
@@ -534,6 +549,24 @@ static int do_probe(int argc, char **argv)
}
target = COMPONENT_KERNEL;
NEXT_ARG();
+ } else if (is_prefix(*argv, "dev")) {
+ NEXT_ARG();
+
+ if (target != COMPONENT_UNSPEC || ifindex) {
+ p_err("component to probe already specified");
+ return -1;
+ }
+ if (!REQ_ARGS(1))
+ return -1;
+
+ target = COMPONENT_DEVICE;
+ ifname = GET_ARG();
+ ifindex = if_nametoindex(ifname);
+ if (!ifindex) {
+ p_err("unrecognized netdevice '%s': %s", ifname,
+ strerror(errno));
+ return -1;
+ }
} else if (is_prefix(*argv, "macros") && !define_prefix) {
define_prefix = "";
NEXT_ARG();
@@ -552,7 +585,7 @@ static int do_probe(int argc, char **argv)
return -1;
define_prefix = GET_ARG();
} else {
- p_err("expected no more arguments, 'kernel', 'macros' or 'prefix', got: '%s'?",
+ p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",
*argv);
return -1;
}
@@ -587,6 +620,8 @@ static int do_probe(int argc, char **argv)
else
printf("\n");
break;
+ default:
+ break;
}
print_start_section("syscall_config",
@@ -594,6 +629,7 @@ static int do_probe(int argc, char **argv)
"/*** System call availability ***/",
define_prefix);
+ /* Get kernel version in all cases, we need it for kprobe programs */
kernel_version = probe_kernel_version(define_prefix);
if (!probe_bpf_syscall(define_prefix))
/* bpf() syscall unavailable, don't probe other BPF features */
@@ -606,7 +642,7 @@ static int do_probe(int argc, char **argv)
for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
probe_prog_type(i, kernel_version, supported_types,
- define_prefix);
+ define_prefix, ifindex);
print_end_then_start_section("map_types",
"Scanning eBPF map types...",
@@ -614,7 +650,7 @@ static int do_probe(int argc, char **argv)
define_prefix);
for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
- probe_map_type(i, define_prefix);
+ probe_map_type(i, define_prefix, ifindex);
print_end_then_start_section("helpers",
"Scanning eBPF helper functions...",
@@ -623,7 +659,7 @@ static int do_probe(int argc, char **argv)
for (i = 1; i < ARRAY_SIZE(helper_name); i++)
probe_helper(i, helper_name[i], kernel_version,
- supported_types, define_prefix);
+ supported_types, define_prefix, ifindex);
exit_close_json:
if (json_output) {
@@ -644,8 +680,10 @@ static int do_help(int argc, char **argv)
}
fprintf(stderr,
- "Usage: %s %s probe [kernel] [macros [prefix PREFIX]]\n"
+ "Usage: %s %s probe [COMPONENT] [macros [prefix PREFIX]]\n"
" %s %s help\n"
+ "\n"
+ " COMPONENT := { kernel | dev NAME }\n"
"",
bin_name, argv[-2], bin_name, argv[-2]);
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index d7dd84d3c660..5caea42d1618 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -185,6 +185,7 @@ void print_hex_data_json(uint8_t *data, size_t len);
unsigned int get_page_size(void);
unsigned int get_possible_cpus(void);
+int read_sysfs_netdev_hex_int(char *devname, const char *entry_name);
const char *
ifindex_to_bfd_params(__u32 ifindex, __u64 ns_dev, __u64 ns_ino,
const char **opt);
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [RFC PATCH bpf-next v2 9/9] tools: bpftool: add bash completion for bpftool probes
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
` (7 preceding siblings ...)
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 8/9] tools: bpftool: add probes for a network device Quentin Monnet
@ 2018-12-20 12:24 ` Quentin Monnet
8 siblings, 0 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 12:24 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann
Cc: netdev, oss-drivers, Quentin Monnet, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
Add the bash completion related to the newly introduced "bpftool feature
probe" command.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
tools/bpf/bpftool/bash-completion/bpftool | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index e4e4fab1b8c7..21d5295936ed 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -624,6 +624,25 @@ _bpftool()
;;
esac
;;
+ feature)
+ case $command in
+ probe)
+ [[ $prev == "dev" ]] && _sysfs_get_netdevs && return 0
+ [[ $prev == "prefix" ]] && return 0
+ if _bpftool_search_list 'macros'; then
+ COMPREPLY+=( $( compgen -W 'prefix' -- "$cur" ) )
+ else
+ COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )
+ fi
+ _bpftool_one_of_list 'kernel dev'
+ return 0
+ ;;
+ *)
+ [[ $prev == $object ]] && \
+ COMPREPLY=( $( compgen -W 'help probe' -- "$cur" ) )
+ ;;
+ esac
+ ;;
esac
} &&
complete -F _bpftool bpftool
--
2.17.1
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes Quentin Monnet
@ 2018-12-20 17:25 ` Daniel Borkmann
2018-12-20 20:05 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Daniel Borkmann @ 2018-12-20 17:25 UTC (permalink / raw)
To: Quentin Monnet, Alexei Starovoitov
Cc: netdev, oss-drivers, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
On 12/20/2018 01:24 PM, Quentin Monnet wrote:
> Make bpftool able to dump a subset of the parameters collected by
> probing the system as a listing of C-style #define macros, so that
> external projects can reuse the result of this probing and build
> BPF-based project in accordance with the features available on the
> system.
>
> The new "macros" keyword is used to select this output. An additional
> "prefix" keyword is added so that users can select a custom prefix for
> macro names, in order to avoid any namespace conflict.
>
> Sample output:
>
> # bpftool feature probe kernel macros prefix FOO_
> /*** System call availability ***/
> #define FOO_HAVE_BPF_SYSCALL
>
> /*** eBPF program types ***/
> #define FOO_HAVE_SOCKET_FILTER_PROG_TYPE
> #define FOO_HAVE_KPROBE_PROG_TYPE
> #define FOO_HAVE_SCHED_CLS_PROG_TYPE
> ...
>
> /*** eBPF map types ***/
> #define FOO_HAVE_HASH_MAP_TYPE
> #define FOO_HAVE_ARRAY_MAP_TYPE
> #define FOO_HAVE_PROG_ARRAY_MAP_TYPE
> ...
>
> /*** eBPF helper functions ***/
> ...
> #define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
> "lwt_xmit " \
> "sk_skb "
> #define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
> "xdp "
> #define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
> "kprobe " \
> "tracepoint " \
> "perf_event " \
> "raw_tracepoint "
> ...
Rest looks good to me, just a comment here. How would programs use the
compat list? Wouldn't it be nicer to provide a PROG(HELPER) availability
query along the lines of ...
#if FOO_HAVE_SOCKET_FILTER(skb_load_bytes) == 1
// ...
#endif
... where the helper provides these macro functions and results in the
header file? So they can be used easily in the code from BPF prog or
normal C applications?
> v2:
> - #define-based output added as a distinct patch.
> - "HAVE_" prefix appended to all macro names.
> - Output limited to bpf() syscall availability, BPF prog and map types,
> helper functions. In this version kernel config options, procfs
> parameter or kernel version are intentionally left aside.
> - Following the change on helper probes, format for helper probes in
> this output style has changed (now a list of compatible program
> types).
>
> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options Quentin Monnet
@ 2018-12-20 17:40 ` Stanislav Fomichev
2018-12-20 18:02 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 17:40 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> Add probes to dump a number of options set (or not set) for compiling
> the kernel image. These parameters provide information about what BPF
> components should be available on the system. A number of them are not
> directly related to eBPF, but are in fact used in the kernel as
> conditions on which to compile, or not to compile, some of the eBPF
> helper functions.
>
> Sample output:
>
> # bpftool feature probe kernel
> Scanning system configuration...
> ...
> CONFIG_BPF is set to y
> CONFIG_BPF_SYSCALL is set to y
> CONFIG_HAVE_EBPF_JIT is set to y
> ...
>
> # bpftool --pretty --json feature probe kernel
> {
> "system_config": {
> ...
> "CONFIG_BPF": "y",
> "CONFIG_BPF_SYSCALL": "y",
> "CONFIG_HAVE_EBPF_JIT": "y",
> ...
> }
> }
>
> v2:
> - Remove C-style macros output from this patch.
> - NOT addressed: grouping of those config options into subsections
> (I don't see an easy way of grouping them at the moment, please see
> also the discussion on v1 thread).
>
> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> ---
> tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 137 insertions(+)
>
> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> index 410d35857cf4..238d7b80f426 100644
> --- a/tools/bpf/bpftool/feature.c
> +++ b/tools/bpf/bpftool/feature.c
> @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res)
> printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
> }
>
> +static void print_kernel_option(const char *name, const char *value)
> +{
> + char *endptr;
> + int res;
> +
> + if (json_output) {
> + if (!value) {
> + jsonw_null_field(json_wtr, name);
> + return;
> + }
> + errno = 0;
> + res = strtol(value, &endptr, 0);
> + if (!errno && *endptr == '\n')
> + jsonw_int_field(json_wtr, name, res);
> + else
> + jsonw_string_field(json_wtr, name, value);
> + } else {
> + if (value)
> + printf("%s is set to %s\n", name, value);
> + else
> + printf("%s is not set\n", name);
> + }
> +}
> +
> static void
> print_start_section(const char *json_title, const char *plain_title)
> {
> @@ -190,6 +214,118 @@ static void probe_jit_kallsyms(void)
> }
> }
>
> +static char *get_kernel_config_option(FILE *fd, const char *option)
> +{
> + size_t line_n = 0, optlen = strlen(option);
> + char *res, *strval, *line = NULL;
> + ssize_t n;
> +
> + rewind(fd);
> + while ((n = getline(&line, &line_n, fd)) > 0) {
> + if (strncmp(line, option, optlen))
> + continue;
> + /* Check we have at least '=', value, and '\n' */
> + if (strlen(line) < optlen + 3)
> + continue;
> + if (*(line + optlen) != '=')
> + continue;
> +
> + /* Trim ending '\n' */
> + line[strlen(line) - 1] = '\0';
> +
> + /* Copy and return config option value */
> + strval = line + optlen + 1;
> + res = strdup(strval);
> + free(line);
> + return res;
> + }
> + free(line);
> +
> + return NULL;
> +}
> +
> +static void probe_kernel_image_config(void)
> +{
> + const char * const options[] = {
> + "CONFIG_BPF",
> + "CONFIG_BPF_SYSCALL",
> + "CONFIG_HAVE_EBPF_JIT",
> + "CONFIG_BPF_JIT",
> + "CONFIG_BPF_JIT_ALWAYS_ON",
> + "CONFIG_NET",
> + "CONFIG_XDP_SOCKETS",
> + "CONFIG_CGROUPS",
> + "CONFIG_CGROUP_BPF",
> + "CONFIG_CGROUP_NET_CLASSID",
> + "CONFIG_BPF_EVENTS",
> + "CONFIG_LWTUNNEL_BPF",
> + "CONFIG_NET_ACT_BPF",
> + "CONFIG_NET_CLS_ACT",
> + "CONFIG_NET_CLS_BPF",
> + "CONFIG_NET_SCH_INGRESS",
> + "CONFIG_XFRM",
> + "CONFIG_SOCK_CGROUP_DATA",
> + "CONFIG_IP_ROUTE_CLASSID",
> + "CONFIG_IPV6_SEG6_BPF",
> + "CONFIG_FUNCTION_ERROR_INJECTION",
> + "CONFIG_BPF_KPROBE_OVERRIDE",
> + "CONFIG_BPF_LIRC_MODE2",
> + "CONFIG_NETFILTER_XT_MATCH_BPF",
> + "CONFIG_TEST_BPF",
> + "CONFIG_BPFILTER",
> + "CONFIG_BPFILTER_UMH",
> + "CONFIG_BPF_STREAM_PARSER",
> + };
> + char *value, *buf = NULL;
> + struct utsname utsn;
> + char path[PATH_MAX];
> + size_t i, n;
> + ssize_t ret;
> + FILE *fd;
> +
> + if (uname(&utsn))
> + goto no_config;
> +
[..]
> + snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
> +
> + fd = fopen(path, "r");
> + if (!fd && errno == ENOENT) {
> + /* Sometimes config is at /proc/config */
> + fd = fopen("/proc/config", "r");
I wonder whether the order here should be reversed?
1. try /proc/config.gz (CONFIG_IKCONFIG_PROC)
2. if not avail, try /boot/config-$(uname -r)
Because, at the end, /proc/config.gz is the real source of truth (if
available).
What is /proc/config btw? I can see only /proc/config.gz being exported.
> + }
> + if (!fd) {
> + p_err("can't open kernel config file: %s", strerror(errno));
> + goto no_config;
> + }
> + /* Sanity checks */
> + ret = getline(&buf, &n, fd);
> + ret = getline(&buf, &n, fd);
> + if (!buf || !ret) {
> + p_err("can't read from kernel config file: %s",
> + strerror(errno));
> + free(buf);
> + goto no_config;
> + }
> + if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
> + p_err("can't find correct kernel config file");
> + free(buf);
> + goto no_config;
> + }
> + free(buf);
> +
> + for (i = 0; i < ARRAY_SIZE(options); i++) {
> + value = get_kernel_config_option(fd, options[i]);
> + print_kernel_option(options[i], value);
> + free(value);
> + }
> + fclose(fd);
> + return;
> +
> +no_config:
> + for (i = 0; i < ARRAY_SIZE(options); i++)
> + print_kernel_option(options[i], NULL);
> +}
> +
> static int probe_kernel_version(void)
> {
> int version, subversion, patchlevel, code = 0;
> @@ -270,6 +406,7 @@ static int do_probe(int argc, char **argv)
> } else {
> p_info("/* procfs not mounted, skipping related probes */");
> }
> + probe_kernel_image_config();
> if (json_output)
> jsonw_end_object(json_wtr);
> else
> --
> 2.17.1
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types Quentin Monnet
@ 2018-12-20 17:45 ` Stanislav Fomichev
2018-12-20 18:06 ` [oss-drivers] " Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 17:45 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> Introduce probes for supported BPF program types in libbpf, and call it
> from bpftool to test what types are available on the system. The probe
> simply consists in loading a very simple program of that type and see if
> the verifier complains or not.
>
> Sample output:
>
> # bpftool feature probe kernel
> ...
> Scanning eBPF program types...
> eBPF program_type socket_filter is available
> eBPF program_type kprobe is available
> eBPF program_type sched_cls is available
> ...
>
> # bpftool --json --pretty feature probe kernel
> {
> ...
> "program_types": {
> "have_socket_filter_prog_type": true,
> "have_kprobe_prog_type": true,
> "have_sched_cls_prog_type": true,
> ...
> }
> }
>
> v2:
> - Move probes from bpftool to libbpf.
> - Remove C-style macros output from this patch.
>
> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
> tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
> tools/lib/bpf/Build | 2 +-
> tools/lib/bpf/libbpf.h | 6 ++++
> tools/lib/bpf/libbpf.map | 1 +
> tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
> 5 files changed, 114 insertions(+), 3 deletions(-)
> create mode 100644 tools/lib/bpf/libbpf_probes.c
>
> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> index 238d7b80f426..3ba0a0a5904c 100644
> --- a/tools/bpf/bpftool/feature.c
> +++ b/tools/bpf/bpftool/feature.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> /* Copyright (c) 2018 Netronome Systems, Inc. */
>
> +#include <ctype.h>
> #include <errno.h>
> #include <string.h>
> #include <unistd.h>
> @@ -11,6 +12,7 @@
> #include <linux/limits.h>
>
> #include <bpf.h>
> +#include <libbpf.h>
>
> #include "main.h"
>
> @@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
> }
> }
>
> +static void
> +print_end_then_start_section(const char *json_title, const char *plain_title)
> +{
> + if (json_output)
> + jsonw_end_object(json_wtr);
> + else
> + printf("\n");
> +
> + print_start_section(json_title, plain_title);
> +}
> +
> /* Probing functions */
>
> static int read_procfs(const char *path)
> @@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)
> return res;
> }
>
> +static void
> +probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> + bool *supported_types)
> +{
> + const char *plain_comment = "eBPF program_type ";
> + char feat_name[128], plain_desc[128];
> + size_t maxlen;
> + bool res;
> +
> + res = bpf_probe_prog_type(prog_type, kernel_version, 0);
> +
> + supported_types[prog_type] |= res;
> +
> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
> + if (strlen(prog_type_name[prog_type]) > maxlen) {
> + p_info("program type name too long");
> + return;
> + }
> +
> + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
> + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
> + print_bool_feature(feat_name, plain_desc, res);
> +}
> +
> static int do_probe(int argc, char **argv)
> {
> enum probe_component target = COMPONENT_UNSPEC;
> + bool supported_types[128] = {};
> + int kernel_version;
> + unsigned int i;
>
> /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
> * Let's approximate, and restrict usage to root user only.
> @@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)
> print_start_section("syscall_config",
> "Scanning system call and kernel version...");
>
> - probe_kernel_version();
> - probe_bpf_syscall();
> + kernel_version = probe_kernel_version();
> + if (!probe_bpf_syscall())
> + /* bpf() syscall unavailable, don't probe other BPF features */
> + goto exit_close_json;
> +
> + print_end_then_start_section("program_types",
> + "Scanning eBPF program types...");
> +
> + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
> + probe_prog_type(i, kernel_version, supported_types);
>
> +exit_close_json:
> if (json_output) {
> /* End current "section" of probes */
> jsonw_end_object(json_wtr);
> diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
> index 197b40f5b5c6..bfd9bfc82c3b 100644
> --- a/tools/lib/bpf/Build
> +++ b/tools/lib/bpf/Build
> @@ -1 +1 @@
> -libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
> +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 5f68d7b75215..f4bb2764ca9a 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -355,6 +355,12 @@ LIBBPF_API const struct bpf_line_info *
> bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
> __u32 insn_off, __u32 nr_skip);
>
> +/*
> + * Probe for supported system features
> + */
> +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
> + int kernel_version, __u32 ifindex);
> +
> #ifdef __cplusplus
> } /* extern "C" */
> #endif
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index cd02cd4e2cc3..6355e4c80a86 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_prog_type;
> bpf_prog_attach;
> bpf_prog_detach;
> bpf_prog_detach2;
> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> new file mode 100644
> index 000000000000..2c5e0cdc9f2f
> --- /dev/null
> +++ b/tools/lib/bpf/libbpf_probes.c
> @@ -0,0 +1,55 @@
> +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
> +
> +/* Copyright (c) 2018 Netronome Systems, Inc. */
> +
> +#include <errno.h>
> +#include <unistd.h>
> +
> +#include <linux/filter.h>
> +#include <linux/kernel.h>
> +
> +#include "bpf.h"
> +#include "libbpf.h"
> +
> +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,
> + __u32 ifindex)
> +{
> + struct bpf_load_program_attr xattr = {};
> + int fd;
> +
> + /* Some prog type require an expected_attach_type */
> + if (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR)
> + xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
> +
> + xattr.prog_type = prog_type;
> + xattr.insns = insns;
> + xattr.insns_cnt = insns_cnt;
> + xattr.license = "GPL";
> + xattr.kern_version = kernel_version;
> + xattr.prog_ifindex = ifindex;
> +
> + fd = bpf_load_program_xattr(&xattr, buf, buf_len);
> + if (fd >= 0)
> + close(fd);
> +}
> +
> +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
Do we really need this kernel_version argument? Isn't it going away in
the future (I saw a patch from Daniel that drops kernel check under
sys_bpf). Going forward, does it make sense to have it at the API
level?
> + __u32 ifindex)
> +{
> + struct bpf_insn insns[2] = {
> + BPF_MOV64_IMM(BPF_REG_0, 0),
> + BPF_EXIT_INSN()
> + };
> +
> + if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS)
> + /* nfp returns -EINVAL on exit(0) with TC offload */
> + insns[0].imm = 2;
> +
> + errno = 0;
> + prog_load(prog_type, insns, ARRAY_SIZE(insns), kernel_version,
> + NULL, 0, ifindex);
> +
> + return errno != EINVAL && errno != EOPNOTSUPP;
> +}
> --
> 2.17.1
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types Quentin Monnet
@ 2018-12-20 17:47 ` Stanislav Fomichev
2018-12-20 18:10 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 17:47 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> Add new probes for eBPF map types, to detect what are the ones available
> on the system. Try creating one map of each type, and see if the kernel
> complains.
>
> Sample output:
>
> # bpftool feature probe kernel
> ...
> Scanning eBPF map types...
> eBPF map_type hash is available
> eBPF map_type array is available
> eBPF map_type prog_array is available
> ...
>
> # bpftool --json --pretty feature probe kernel
> {
> ...
> "map_types": {
> "have_hash_map_type": true,
> "have_array_map_type": true,
> "have_prog_array_map_type": true,
> ...
> }
> }
>
> v2:
> - Move probes from bpftool to libbpf.
> - Remove C-style macros output from this patch.
>
> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
> tools/bpf/bpftool/feature.c | 26 +++++++++++++++
> tools/bpf/bpftool/main.h | 3 ++
> tools/bpf/bpftool/map.c | 4 ++-
> tools/lib/bpf/libbpf.h | 1 +
> tools/lib/bpf/libbpf.map | 1 +
> tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
> 6 files changed, 93 insertions(+), 1 deletion(-)
>
> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> index 3ba0a0a5904c..3c44953ded5a 100644
> --- a/tools/bpf/bpftool/feature.c
> +++ b/tools/bpf/bpftool/feature.c
> @@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> print_bool_feature(feat_name, plain_desc, res);
> }
>
> +static void probe_map_type(enum bpf_map_type map_type)
> +{
> + const char *plain_comment = "eBPF map_type ";
> + char feat_name[128], plain_desc[128];
> + size_t maxlen;
> + bool res;
> +
> + res = bpf_probe_map_type(map_type, 0);
> +
> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
> + if (strlen(map_type_name[map_type]) > maxlen) {
> + p_info("map type name too long");
> + return;
> + }
> +
> + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
> + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
> + print_bool_feature(feat_name, plain_desc, res);
> +}
> +
> static int do_probe(int argc, char **argv)
> {
> enum probe_component target = COMPONENT_UNSPEC;
> @@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
> for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
> probe_prog_type(i, kernel_version, supported_types);
>
> + print_end_then_start_section("map_types",
> + "Scanning eBPF map types...");
> +
> + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
> + probe_map_type(i);
> +
> exit_close_json:
> if (json_output) {
> /* End current "section" of probes */
> diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
> index 5cfc6601de9b..d7dd84d3c660 100644
> --- a/tools/bpf/bpftool/main.h
> +++ b/tools/bpf/bpftool/main.h
> @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
> [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
> };
>
> +extern const char * const map_type_name[];
> +extern const size_t map_type_name_size;
> +
> enum bpf_obj_type {
> BPF_OBJ_UNKNOWN,
> BPF_OBJ_PROG,
> diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
> index 2037e3dc864b..b73985589929 100644
> --- a/tools/bpf/bpftool/map.c
> +++ b/tools/bpf/bpftool/map.c
> @@ -21,7 +21,7 @@
> #include "json_writer.h"
> #include "main.h"
>
> -static const char * const map_type_name[] = {
> +const char * const map_type_name[] = {
> [BPF_MAP_TYPE_UNSPEC] = "unspec",
> [BPF_MAP_TYPE_HASH] = "hash",
> [BPF_MAP_TYPE_ARRAY] = "array",
> @@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
> [BPF_MAP_TYPE_STACK] = "stack",
> };
>
> +const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
> +
> static bool map_is_per_cpu(__u32 type)
> {
> return type == BPF_MAP_TYPE_PERCPU_HASH ||
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index f4bb2764ca9a..202c1ee5c579 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -360,6 +360,7 @@ 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);
>
> #ifdef __cplusplus
> } /* extern "C" */
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 6355e4c80a86..c08f4c726e8e 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_map_type;
> bpf_probe_prog_type;
> bpf_prog_attach;
> bpf_prog_detach;
> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> index 2c5e0cdc9f2f..796fe1e66169 100644
> --- a/tools/lib/bpf/libbpf_probes.c
> +++ b/tools/lib/bpf/libbpf_probes.c
> @@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>
> return errno != EINVAL && errno != EOPNOTSUPP;
> }
> +
> +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
> +{
> + int key_size, value_size, max_entries, map_flags;
> + struct bpf_create_map_attr attr = {};
> + int fd = -1, fd_inner;
> +
> + key_size = sizeof(__u32);
> + value_size = sizeof(__u32);
> + max_entries = 1;
> + map_flags = 0;
> +
> + if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
How about using switch here? So it yells and complains when the new item
is added to the bpf_map_type enum.
> + key_size = sizeof(__u64);
> + value_size = sizeof(__u64);
> + map_flags = BPF_F_NO_PREALLOC;
> + } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
> + value_size = sizeof(__u64);
> + } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
> + map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
> + key_size = sizeof(struct bpf_cgroup_storage_key);
> + value_size = sizeof(__u64);
> + max_entries = 0;
> + } else if (map_type == BPF_MAP_TYPE_QUEUE ||
> + map_type == BPF_MAP_TYPE_STACK) {
> + key_size = 0;
> + }
> +
> + if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
> + map_type == BPF_MAP_TYPE_HASH_OF_MAPS) {
> + /* TODO: probe for device, once libbpf has a function to create
> + * map-in-map for offload
> + */
> + if (ifindex)
> + return false;
> +
> + fd_inner = bpf_create_map(BPF_MAP_TYPE_HASH,
> + sizeof(__u32), sizeof(__u32), 1, 0);
> + if (fd_inner < 0)
> + return false;
> + fd = bpf_create_map_in_map(map_type, NULL, sizeof(__u32),
> + fd_inner, 1, 0);
> + close(fd_inner);
> + } else {
> + /* Note: No other restriction on map type probes for offload */
> + attr.map_type = map_type;
> + attr.key_size = key_size;
> + attr.value_size = value_size;
> + attr.max_entries = max_entries;
> + attr.map_flags = map_flags;
> + attr.map_ifindex = ifindex;
> +
> + fd = bpf_create_map_xattr(&attr);
> + }
> + if (fd >= 0)
> + close(fd);
> +
> + return fd >= 0;
> +}
> --
> 2.17.1
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions Quentin Monnet
@ 2018-12-20 17:53 ` Stanislav Fomichev
2018-12-20 18:14 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 17:53 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
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 <quentin.monnet@netronome.com>
> ---
> .../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 <errno.h>
> +#include <fcntl.h>
> +#include <string.h>
> +#include <stdlib.h>
> #include <unistd.h>
> +#include <net/if.h>
>
> #include <linux/filter.h>
> #include <linux/kernel.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 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
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options
2018-12-20 17:40 ` Stanislav Fomichev
@ 2018-12-20 18:02 ` Quentin Monnet
2018-12-20 18:14 ` Stanislav Fomichev
0 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:02 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 09:40 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> On 12/20, Quentin Monnet wrote:
>> Add probes to dump a number of options set (or not set) for compiling
>> the kernel image. These parameters provide information about what BPF
>> components should be available on the system. A number of them are not
>> directly related to eBPF, but are in fact used in the kernel as
>> conditions on which to compile, or not to compile, some of the eBPF
>> helper functions.
>>
>> Sample output:
>>
>> # bpftool feature probe kernel
>> Scanning system configuration...
>> ...
>> CONFIG_BPF is set to y
>> CONFIG_BPF_SYSCALL is set to y
>> CONFIG_HAVE_EBPF_JIT is set to y
>> ...
>>
>> # bpftool --pretty --json feature probe kernel
>> {
>> "system_config": {
>> ...
>> "CONFIG_BPF": "y",
>> "CONFIG_BPF_SYSCALL": "y",
>> "CONFIG_HAVE_EBPF_JIT": "y",
>> ...
>> }
>> }
>>
>> v2:
>> - Remove C-style macros output from this patch.
>> - NOT addressed: grouping of those config options into subsections
>> (I don't see an easy way of grouping them at the moment, please see
>> also the discussion on v1 thread).
>>
>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
>> ---
>> tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
>> 1 file changed, 137 insertions(+)
>> +static void probe_kernel_image_config(void)
>> +{
>> + const char * const options[] = {
>> + "CONFIG_BPF",
>> + "CONFIG_BPF_SYSCALL",
>> + "CONFIG_HAVE_EBPF_JIT",
>> + "CONFIG_BPF_JIT",
>> + "CONFIG_BPF_JIT_ALWAYS_ON",
>> + "CONFIG_NET",
>> + "CONFIG_XDP_SOCKETS",
>> + "CONFIG_CGROUPS",
>> + "CONFIG_CGROUP_BPF",
>> + "CONFIG_CGROUP_NET_CLASSID",
>> + "CONFIG_BPF_EVENTS",
>> + "CONFIG_LWTUNNEL_BPF",
>> + "CONFIG_NET_ACT_BPF",
>> + "CONFIG_NET_CLS_ACT",
>> + "CONFIG_NET_CLS_BPF",
>> + "CONFIG_NET_SCH_INGRESS",
>> + "CONFIG_XFRM",
>> + "CONFIG_SOCK_CGROUP_DATA",
>> + "CONFIG_IP_ROUTE_CLASSID",
>> + "CONFIG_IPV6_SEG6_BPF",
>> + "CONFIG_FUNCTION_ERROR_INJECTION",
>> + "CONFIG_BPF_KPROBE_OVERRIDE",
>> + "CONFIG_BPF_LIRC_MODE2",
>> + "CONFIG_NETFILTER_XT_MATCH_BPF",
>> + "CONFIG_TEST_BPF",
>> + "CONFIG_BPFILTER",
>> + "CONFIG_BPFILTER_UMH",
>> + "CONFIG_BPF_STREAM_PARSER",
>> + };
>> + char *value, *buf = NULL;
>> + struct utsname utsn;
>> + char path[PATH_MAX];
>> + size_t i, n;
>> + ssize_t ret;
>> + FILE *fd;
>> +
>> + if (uname(&utsn))
>> + goto no_config;
>> +
> [..]
>> + snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
>> +
>> + fd = fopen(path, "r");
>> + if (!fd && errno == ENOENT) {
>> + /* Sometimes config is at /proc/config */
>> + fd = fopen("/proc/config", "r");
> I wonder whether the order here should be reversed?
> 1. try /proc/config.gz (CONFIG_IKCONFIG_PROC)
> 2. if not avail, try /boot/config-$(uname -r)
>
> Because, at the end, /proc/config.gz is the real source of truth (if
> available).
>
> What is /proc/config btw? I can see only /proc/config.gz being exported.
Hi Stanislav,
Cilium checks for /proc/config (apparently this is where CoreOS puts its
config file), /proc/config.gz and /boot/config-$(uname -r) [0]. I took
inspiration on that but didn't implement the /proc/config.gz file,
because it would require linking with the libz and I'm not sure this is
worth it. Could be added as a follow-up if necessary.
[0] https://github.com/cilium/cilium/blob/master/bpf/run_probes.sh#L42
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [oss-drivers] Re: [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types
2018-12-20 17:45 ` Stanislav Fomichev
@ 2018-12-20 18:06 ` Quentin Monnet
2018-12-20 18:17 ` Stanislav Fomichev
0 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:06 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> On 12/20, Quentin Monnet wrote:
>> Introduce probes for supported BPF program types in libbpf, and call it
>> from bpftool to test what types are available on the system. The probe
>> simply consists in loading a very simple program of that type and see if
>> the verifier complains or not.
>>
>> Sample output:
>>
>> # bpftool feature probe kernel
>> ...
>> Scanning eBPF program types...
>> eBPF program_type socket_filter is available
>> eBPF program_type kprobe is available
>> eBPF program_type sched_cls is available
>> ...
>>
>> # bpftool --json --pretty feature probe kernel
>> {
>> ...
>> "program_types": {
>> "have_socket_filter_prog_type": true,
>> "have_kprobe_prog_type": true,
>> "have_sched_cls_prog_type": true,
>> ...
>> }
>> }
>>
>> v2:
>> - Move probes from bpftool to libbpf.
>> - Remove C-style macros output from this patch.
>>
>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>> ---
>> tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
>> tools/lib/bpf/Build | 2 +-
>> tools/lib/bpf/libbpf.h | 6 ++++
>> tools/lib/bpf/libbpf.map | 1 +
>> tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
>> 5 files changed, 114 insertions(+), 3 deletions(-)
>> create mode 100644 tools/lib/bpf/libbpf_probes.c
>>
>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>> index 238d7b80f426..3ba0a0a5904c 100644
>> --- a/tools/bpf/bpftool/feature.c
>> +++ b/tools/bpf/bpftool/feature.c
>> @@ -1,6 +1,7 @@
>> // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> /* Copyright (c) 2018 Netronome Systems, Inc. */
>>
>> +#include <ctype.h>
>> #include <errno.h>
>> #include <string.h>
>> #include <unistd.h>
>> @@ -11,6 +12,7 @@
>> #include <linux/limits.h>
>>
>> #include <bpf.h>
>> +#include <libbpf.h>
>>
>> #include "main.h"
>>
>> @@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
>> }
>> }
>>
>> +static void
>> +print_end_then_start_section(const char *json_title, const char *plain_title)
>> +{
>> + if (json_output)
>> + jsonw_end_object(json_wtr);
>> + else
>> + printf("\n");
>> +
>> + print_start_section(json_title, plain_title);
>> +}
>> +
>> /* Probing functions */
>>
>> static int read_procfs(const char *path)
>> @@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)
>> return res;
>> }
>>
>> +static void
>> +probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>> + bool *supported_types)
>> +{
>> + const char *plain_comment = "eBPF program_type ";
>> + char feat_name[128], plain_desc[128];
>> + size_t maxlen;
>> + bool res;
>> +
>> + res = bpf_probe_prog_type(prog_type, kernel_version, 0);
>> +
>> + supported_types[prog_type] |= res;
>> +
>> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
>> + if (strlen(prog_type_name[prog_type]) > maxlen) {
>> + p_info("program type name too long");
>> + return;
>> + }
>> +
>> + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
>> + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
>> + print_bool_feature(feat_name, plain_desc, res);
>> +}
>> +
>> static int do_probe(int argc, char **argv)
>> {
>> enum probe_component target = COMPONENT_UNSPEC;
>> + bool supported_types[128] = {};
>> + int kernel_version;
>> + unsigned int i;
>>
>> /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
>> * Let's approximate, and restrict usage to root user only.
>> @@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)
>> print_start_section("syscall_config",
>> "Scanning system call and kernel version...");
>>
>> - probe_kernel_version();
>> - probe_bpf_syscall();
>> + kernel_version = probe_kernel_version();
>> + if (!probe_bpf_syscall())
>> + /* bpf() syscall unavailable, don't probe other BPF features */
>> + goto exit_close_json;
>> +
>> + print_end_then_start_section("program_types",
>> + "Scanning eBPF program types...");
>> +
>> + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
>> + probe_prog_type(i, kernel_version, supported_types);
>>
>> +exit_close_json:
>> if (json_output) {
>> /* End current "section" of probes */
>> jsonw_end_object(json_wtr);
>> diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
>> index 197b40f5b5c6..bfd9bfc82c3b 100644
>> --- a/tools/lib/bpf/Build
>> +++ b/tools/lib/bpf/Build
>> @@ -1 +1 @@
>> -libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
>> +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>> index 5f68d7b75215..f4bb2764ca9a 100644
>> --- a/tools/lib/bpf/libbpf.h
>> +++ b/tools/lib/bpf/libbpf.h
>> @@ -355,6 +355,12 @@ LIBBPF_API const struct bpf_line_info *
>> bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
>> __u32 insn_off, __u32 nr_skip);
>>
>> +/*
>> + * Probe for supported system features
>> + */
>> +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
>> + int kernel_version, __u32 ifindex);
>> +
>> #ifdef __cplusplus
>> } /* extern "C" */
>> #endif
>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>> index cd02cd4e2cc3..6355e4c80a86 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_prog_type;
>> bpf_prog_attach;
>> bpf_prog_detach;
>> bpf_prog_detach2;
>> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
>> new file mode 100644
>> index 000000000000..2c5e0cdc9f2f
>> --- /dev/null
>> +++ b/tools/lib/bpf/libbpf_probes.c
>> @@ -0,0 +1,55 @@
>> +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
>> +
>> +/* Copyright (c) 2018 Netronome Systems, Inc. */
>> +
>> +#include <errno.h>
>> +#include <unistd.h>
>> +
>> +#include <linux/filter.h>
>> +#include <linux/kernel.h>
>> +
>> +#include "bpf.h"
>> +#include "libbpf.h"
>> +
>> +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,
>> + __u32 ifindex)
>> +{
>> + struct bpf_load_program_attr xattr = {};
>> + int fd;
>> +
>> + /* Some prog type require an expected_attach_type */
>> + if (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR)
>> + xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
>> +
>> + xattr.prog_type = prog_type;
>> + xattr.insns = insns;
>> + xattr.insns_cnt = insns_cnt;
>> + xattr.license = "GPL";
>> + xattr.kern_version = kernel_version;
>> + xattr.prog_ifindex = ifindex;
>> +
>> + fd = bpf_load_program_xattr(&xattr, buf, buf_len);
>> + if (fd >= 0)
>> + close(fd);
>> +}
>> +
>> +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> Do we really need this kernel_version argument? Isn't it going away in
> the future (I saw a patch from Daniel that drops kernel check under
> sys_bpf). Going forward, does it make sense to have it at the API
> level?
It does go away and should be left aside for new applications. The
objective with bpftool is to be able to probe systems possibly running
older kernels though, and the only way to probe for kprobes support on
anything older than a 4.21 is to keep the version number.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 17:47 ` Stanislav Fomichev
@ 2018-12-20 18:10 ` Quentin Monnet
2018-12-20 18:18 ` Stanislav Fomichev
0 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:10 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> On 12/20, Quentin Monnet wrote:
>> Add new probes for eBPF map types, to detect what are the ones available
>> on the system. Try creating one map of each type, and see if the kernel
>> complains.
>>
>> Sample output:
>>
>> # bpftool feature probe kernel
>> ...
>> Scanning eBPF map types...
>> eBPF map_type hash is available
>> eBPF map_type array is available
>> eBPF map_type prog_array is available
>> ...
>>
>> # bpftool --json --pretty feature probe kernel
>> {
>> ...
>> "map_types": {
>> "have_hash_map_type": true,
>> "have_array_map_type": true,
>> "have_prog_array_map_type": true,
>> ...
>> }
>> }
>>
>> v2:
>> - Move probes from bpftool to libbpf.
>> - Remove C-style macros output from this patch.
>>
>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>> ---
>> tools/bpf/bpftool/feature.c | 26 +++++++++++++++
>> tools/bpf/bpftool/main.h | 3 ++
>> tools/bpf/bpftool/map.c | 4 ++-
>> tools/lib/bpf/libbpf.h | 1 +
>> tools/lib/bpf/libbpf.map | 1 +
>> tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
>> 6 files changed, 93 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>> index 3ba0a0a5904c..3c44953ded5a 100644
>> --- a/tools/bpf/bpftool/feature.c
>> +++ b/tools/bpf/bpftool/feature.c
>> @@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>> print_bool_feature(feat_name, plain_desc, res);
>> }
>>
>> +static void probe_map_type(enum bpf_map_type map_type)
>> +{
>> + const char *plain_comment = "eBPF map_type ";
>> + char feat_name[128], plain_desc[128];
>> + size_t maxlen;
>> + bool res;
>> +
>> + res = bpf_probe_map_type(map_type, 0);
>> +
>> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
>> + if (strlen(map_type_name[map_type]) > maxlen) {
>> + p_info("map type name too long");
>> + return;
>> + }
>> +
>> + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
>> + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
>> + print_bool_feature(feat_name, plain_desc, res);
>> +}
>> +
>> static int do_probe(int argc, char **argv)
>> {
>> enum probe_component target = COMPONENT_UNSPEC;
>> @@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
>> for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
>> probe_prog_type(i, kernel_version, supported_types);
>>
>> + print_end_then_start_section("map_types",
>> + "Scanning eBPF map types...");
>> +
>> + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
>> + probe_map_type(i);
>> +
>> exit_close_json:
>> if (json_output) {
>> /* End current "section" of probes */
>> diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
>> index 5cfc6601de9b..d7dd84d3c660 100644
>> --- a/tools/bpf/bpftool/main.h
>> +++ b/tools/bpf/bpftool/main.h
>> @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
>> [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
>> };
>>
>> +extern const char * const map_type_name[];
>> +extern const size_t map_type_name_size;
>> +
>> enum bpf_obj_type {
>> BPF_OBJ_UNKNOWN,
>> BPF_OBJ_PROG,
>> diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
>> index 2037e3dc864b..b73985589929 100644
>> --- a/tools/bpf/bpftool/map.c
>> +++ b/tools/bpf/bpftool/map.c
>> @@ -21,7 +21,7 @@
>> #include "json_writer.h"
>> #include "main.h"
>>
>> -static const char * const map_type_name[] = {
>> +const char * const map_type_name[] = {
>> [BPF_MAP_TYPE_UNSPEC] = "unspec",
>> [BPF_MAP_TYPE_HASH] = "hash",
>> [BPF_MAP_TYPE_ARRAY] = "array",
>> @@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
>> [BPF_MAP_TYPE_STACK] = "stack",
>> };
>>
>> +const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
>> +
>> static bool map_is_per_cpu(__u32 type)
>> {
>> return type == BPF_MAP_TYPE_PERCPU_HASH ||
>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>> index f4bb2764ca9a..202c1ee5c579 100644
>> --- a/tools/lib/bpf/libbpf.h
>> +++ b/tools/lib/bpf/libbpf.h
>> @@ -360,6 +360,7 @@ 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);
>>
>> #ifdef __cplusplus
>> } /* extern "C" */
>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>> index 6355e4c80a86..c08f4c726e8e 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_map_type;
>> bpf_probe_prog_type;
>> bpf_prog_attach;
>> bpf_prog_detach;
>> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
>> index 2c5e0cdc9f2f..796fe1e66169 100644
>> --- a/tools/lib/bpf/libbpf_probes.c
>> +++ b/tools/lib/bpf/libbpf_probes.c
>> @@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>
>> return errno != EINVAL && errno != EOPNOTSUPP;
>> }
>> +
>> +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
>> +{
>> + int key_size, value_size, max_entries, map_flags;
>> + struct bpf_create_map_attr attr = {};
>> + int fd = -1, fd_inner;
>> +
>> + key_size = sizeof(__u32);
>> + value_size = sizeof(__u32);
>> + max_entries = 1;
>> + map_flags = 0;
>> +
>> + if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
> How about using switch here? So it yells and complains when the new item
> is added to the bpf_map_type enum.
>> + key_size = sizeof(__u64);
>> + value_size = sizeof(__u64);
>> + map_flags = BPF_F_NO_PREALLOC;
>> + } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
>> + value_size = sizeof(__u64);
>> + } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
>> + map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
>> + key_size = sizeof(struct bpf_cgroup_storage_key);
>> + value_size = sizeof(__u64);
>> + max_entries = 0;
>> + } else if (map_type == BPF_MAP_TYPE_QUEUE ||
>> + map_type == BPF_MAP_TYPE_STACK) {
>> + key_size = 0;
>> + }
Hmm so I had a switch in the first place and it complained even with a
"default" label because -Wno-switch-enum is used for compiling. So you
would have me use a switch with all existing map types as explicit
labels, even if we do not use them? To limit the risk of forgetting to
update for new map types that would need specific parameters for the
probe, is that correct?
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions
2018-12-20 17:53 ` Stanislav Fomichev
@ 2018-12-20 18:14 ` Quentin Monnet
0 siblings, 0 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:14 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 09:53 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> 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 <quentin.monnet@netronome.com>
>> ---
>> .../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 <errno.h>
>> +#include <fcntl.h>
>> +#include <string.h>
>> +#include <stdlib.h>
>> #include <unistd.h>
>> +#include <net/if.h>
>>
>> #include <linux/filter.h>
>> #include <linux/kernel.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 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?
Because your program can be rejected for a whole lot of reasons here,
since we do not make any effort on ensuring the call to the helper would
be valid. So if I remember correctly, -EINVAL would be returned if the
helper is not supported, or if the number of arguments is incorrect, or
if the arguments are not of the correct type, or...
So instead I'm looking for those error messages in the log buffer, which
seems much more reliable in this case.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options
2018-12-20 18:02 ` Quentin Monnet
@ 2018-12-20 18:14 ` Stanislav Fomichev
0 siblings, 0 replies; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 18:14 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> 2018-12-20 09:40 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > On 12/20, Quentin Monnet wrote:
> > > Add probes to dump a number of options set (or not set) for compiling
> > > the kernel image. These parameters provide information about what BPF
> > > components should be available on the system. A number of them are not
> > > directly related to eBPF, but are in fact used in the kernel as
> > > conditions on which to compile, or not to compile, some of the eBPF
> > > helper functions.
> > >
> > > Sample output:
> > >
> > > # bpftool feature probe kernel
> > > Scanning system configuration...
> > > ...
> > > CONFIG_BPF is set to y
> > > CONFIG_BPF_SYSCALL is set to y
> > > CONFIG_HAVE_EBPF_JIT is set to y
> > > ...
> > >
> > > # bpftool --pretty --json feature probe kernel
> > > {
> > > "system_config": {
> > > ...
> > > "CONFIG_BPF": "y",
> > > "CONFIG_BPF_SYSCALL": "y",
> > > "CONFIG_HAVE_EBPF_JIT": "y",
> > > ...
> > > }
> > > }
> > >
> > > v2:
> > > - Remove C-style macros output from this patch.
> > > - NOT addressed: grouping of those config options into subsections
> > > (I don't see an easy way of grouping them at the moment, please see
> > > also the discussion on v1 thread).
> > >
> > > Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> > > Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> > > ---
> > > tools/bpf/bpftool/feature.c | 137 ++++++++++++++++++++++++++++++++++++
> > > 1 file changed, 137 insertions(+)
>
> > > +static void probe_kernel_image_config(void)
> > > +{
> > > + const char * const options[] = {
> > > + "CONFIG_BPF",
> > > + "CONFIG_BPF_SYSCALL",
> > > + "CONFIG_HAVE_EBPF_JIT",
> > > + "CONFIG_BPF_JIT",
> > > + "CONFIG_BPF_JIT_ALWAYS_ON",
> > > + "CONFIG_NET",
> > > + "CONFIG_XDP_SOCKETS",
> > > + "CONFIG_CGROUPS",
> > > + "CONFIG_CGROUP_BPF",
> > > + "CONFIG_CGROUP_NET_CLASSID",
> > > + "CONFIG_BPF_EVENTS",
> > > + "CONFIG_LWTUNNEL_BPF",
> > > + "CONFIG_NET_ACT_BPF",
> > > + "CONFIG_NET_CLS_ACT",
> > > + "CONFIG_NET_CLS_BPF",
> > > + "CONFIG_NET_SCH_INGRESS",
> > > + "CONFIG_XFRM",
> > > + "CONFIG_SOCK_CGROUP_DATA",
> > > + "CONFIG_IP_ROUTE_CLASSID",
> > > + "CONFIG_IPV6_SEG6_BPF",
> > > + "CONFIG_FUNCTION_ERROR_INJECTION",
> > > + "CONFIG_BPF_KPROBE_OVERRIDE",
> > > + "CONFIG_BPF_LIRC_MODE2",
> > > + "CONFIG_NETFILTER_XT_MATCH_BPF",
> > > + "CONFIG_TEST_BPF",
> > > + "CONFIG_BPFILTER",
> > > + "CONFIG_BPFILTER_UMH",
> > > + "CONFIG_BPF_STREAM_PARSER",
> > > + };
> > > + char *value, *buf = NULL;
> > > + struct utsname utsn;
> > > + char path[PATH_MAX];
> > > + size_t i, n;
> > > + ssize_t ret;
> > > + FILE *fd;
> > > +
> > > + if (uname(&utsn))
> > > + goto no_config;
> > > +
> > [..]
> > > + snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
> > > +
> > > + fd = fopen(path, "r");
> > > + if (!fd && errno == ENOENT) {
> > > + /* Sometimes config is at /proc/config */
> > > + fd = fopen("/proc/config", "r");
> > I wonder whether the order here should be reversed?
> > 1. try /proc/config.gz (CONFIG_IKCONFIG_PROC)
> > 2. if not avail, try /boot/config-$(uname -r)
> >
> > Because, at the end, /proc/config.gz is the real source of truth (if
> > available).
> >
> > What is /proc/config btw? I can see only /proc/config.gz being exported.
>
> Hi Stanislav,
>
> Cilium checks for /proc/config (apparently this is where CoreOS puts its
> config file), /proc/config.gz and /boot/config-$(uname -r) [0]. I took
> inspiration on that but didn't implement the /proc/config.gz file, because
> it would require linking with the libz and I'm not sure this is worth it.
> Could be added as a follow-up if necessary.
Makes sense, then maybe add a comment? So future readers know
that /proc/config.gz is explicitly missing, /proc/config comes from coreos,
and on the other distros we expect /boot/config-$(uname -r).
I'm mainly wondering because my arch box doesn't have
/boot/config-$(uname -r) and has only /proc/config.gz :-) Sigh..
>
> [0] https://github.com/cilium/cilium/blob/master/bpf/run_probes.sh#L42
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [oss-drivers] Re: [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types
2018-12-20 18:06 ` [oss-drivers] " Quentin Monnet
@ 2018-12-20 18:17 ` Stanislav Fomichev
2018-12-20 18:21 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 18:17 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> 2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > On 12/20, Quentin Monnet wrote:
> > > Introduce probes for supported BPF program types in libbpf, and call it
> > > from bpftool to test what types are available on the system. The probe
> > > simply consists in loading a very simple program of that type and see if
> > > the verifier complains or not.
> > >
> > > Sample output:
> > >
> > > # bpftool feature probe kernel
> > > ...
> > > Scanning eBPF program types...
> > > eBPF program_type socket_filter is available
> > > eBPF program_type kprobe is available
> > > eBPF program_type sched_cls is available
> > > ...
> > >
> > > # bpftool --json --pretty feature probe kernel
> > > {
> > > ...
> > > "program_types": {
> > > "have_socket_filter_prog_type": true,
> > > "have_kprobe_prog_type": true,
> > > "have_sched_cls_prog_type": true,
> > > ...
> > > }
> > > }
> > >
> > > v2:
> > > - Move probes from bpftool to libbpf.
> > > - Remove C-style macros output from this patch.
> > >
> > > Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> > > ---
> > > tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
> > > tools/lib/bpf/Build | 2 +-
> > > tools/lib/bpf/libbpf.h | 6 ++++
> > > tools/lib/bpf/libbpf.map | 1 +
> > > tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
> > > 5 files changed, 114 insertions(+), 3 deletions(-)
> > > create mode 100644 tools/lib/bpf/libbpf_probes.c
> > >
> > > diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> > > index 238d7b80f426..3ba0a0a5904c 100644
> > > --- a/tools/bpf/bpftool/feature.c
> > > +++ b/tools/bpf/bpftool/feature.c
> > > @@ -1,6 +1,7 @@
> > > // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > /* Copyright (c) 2018 Netronome Systems, Inc. */
> > > +#include <ctype.h>
> > > #include <errno.h>
> > > #include <string.h>
> > > #include <unistd.h>
> > > @@ -11,6 +12,7 @@
> > > #include <linux/limits.h>
> > > #include <bpf.h>
> > > +#include <libbpf.h>
> > > #include "main.h"
> > > @@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
> > > }
> > > }
> > > +static void
> > > +print_end_then_start_section(const char *json_title, const char *plain_title)
> > > +{
> > > + if (json_output)
> > > + jsonw_end_object(json_wtr);
> > > + else
> > > + printf("\n");
> > > +
> > > + print_start_section(json_title, plain_title);
> > > +}
> > > +
> > > /* Probing functions */
> > > static int read_procfs(const char *path)
> > > @@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)
> > > return res;
> > > }
> > > +static void
> > > +probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > + bool *supported_types)
> > > +{
> > > + const char *plain_comment = "eBPF program_type ";
> > > + char feat_name[128], plain_desc[128];
> > > + size_t maxlen;
> > > + bool res;
> > > +
> > > + res = bpf_probe_prog_type(prog_type, kernel_version, 0);
> > > +
> > > + supported_types[prog_type] |= res;
> > > +
> > > + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
> > > + if (strlen(prog_type_name[prog_type]) > maxlen) {
> > > + p_info("program type name too long");
> > > + return;
> > > + }
> > > +
> > > + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
> > > + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
> > > + print_bool_feature(feat_name, plain_desc, res);
> > > +}
> > > +
> > > static int do_probe(int argc, char **argv)
> > > {
> > > enum probe_component target = COMPONENT_UNSPEC;
> > > + bool supported_types[128] = {};
> > > + int kernel_version;
> > > + unsigned int i;
> > > /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
> > > * Let's approximate, and restrict usage to root user only.
> > > @@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)
> > > print_start_section("syscall_config",
> > > "Scanning system call and kernel version...");
> > > - probe_kernel_version();
> > > - probe_bpf_syscall();
> > > + kernel_version = probe_kernel_version();
> > > + if (!probe_bpf_syscall())
> > > + /* bpf() syscall unavailable, don't probe other BPF features */
> > > + goto exit_close_json;
> > > +
> > > + print_end_then_start_section("program_types",
> > > + "Scanning eBPF program types...");
> > > +
> > > + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
> > > + probe_prog_type(i, kernel_version, supported_types);
> > > +exit_close_json:
> > > if (json_output) {
> > > /* End current "section" of probes */
> > > jsonw_end_object(json_wtr);
> > > diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
> > > index 197b40f5b5c6..bfd9bfc82c3b 100644
> > > --- a/tools/lib/bpf/Build
> > > +++ b/tools/lib/bpf/Build
> > > @@ -1 +1 @@
> > > -libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
> > > +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
> > > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > > index 5f68d7b75215..f4bb2764ca9a 100644
> > > --- a/tools/lib/bpf/libbpf.h
> > > +++ b/tools/lib/bpf/libbpf.h
> > > @@ -355,6 +355,12 @@ LIBBPF_API const struct bpf_line_info *
> > > bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
> > > __u32 insn_off, __u32 nr_skip);
> > > +/*
> > > + * Probe for supported system features
> > > + */
> > > +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
> > > + int kernel_version, __u32 ifindex);
> > > +
> > > #ifdef __cplusplus
> > > } /* extern "C" */
> > > #endif
> > > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > > index cd02cd4e2cc3..6355e4c80a86 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_prog_type;
> > > bpf_prog_attach;
> > > bpf_prog_detach;
> > > bpf_prog_detach2;
> > > diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> > > new file mode 100644
> > > index 000000000000..2c5e0cdc9f2f
> > > --- /dev/null
> > > +++ b/tools/lib/bpf/libbpf_probes.c
> > > @@ -0,0 +1,55 @@
> > > +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
> > > +
> > > +/* Copyright (c) 2018 Netronome Systems, Inc. */
> > > +
> > > +#include <errno.h>
> > > +#include <unistd.h>
> > > +
> > > +#include <linux/filter.h>
> > > +#include <linux/kernel.h>
> > > +
> > > +#include "bpf.h"
> > > +#include "libbpf.h"
> > > +
> > > +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,
> > > + __u32 ifindex)
> > > +{
> > > + struct bpf_load_program_attr xattr = {};
> > > + int fd;
> > > +
> > > + /* Some prog type require an expected_attach_type */
> > > + if (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR)
> > > + xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
> > > +
> > > + xattr.prog_type = prog_type;
> > > + xattr.insns = insns;
> > > + xattr.insns_cnt = insns_cnt;
> > > + xattr.license = "GPL";
> > > + xattr.kern_version = kernel_version;
> > > + xattr.prog_ifindex = ifindex;
> > > +
> > > + fd = bpf_load_program_xattr(&xattr, buf, buf_len);
> > > + if (fd >= 0)
> > > + close(fd);
> > > +}
> > > +
> > > +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > Do we really need this kernel_version argument? Isn't it going away in
> > the future (I saw a patch from Daniel that drops kernel check under
> > sys_bpf). Going forward, does it make sense to have it at the API
> > level?
>
> It does go away and should be left aside for new applications. The objective
> with bpftool is to be able to probe systems possibly running older kernels
> though, and the only way to probe for kprobes support on anything older than
> a 4.21 is to keep the version number.
But isn't kernel_version irrelevant for probing? For probing, we always want
to have kernel_version == <running kernel version>. So I don't
understand why libbpf user should care about the version.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 18:10 ` Quentin Monnet
@ 2018-12-20 18:18 ` Stanislav Fomichev
2018-12-20 18:27 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 18:18 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> 2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > On 12/20, Quentin Monnet wrote:
> > > Add new probes for eBPF map types, to detect what are the ones available
> > > on the system. Try creating one map of each type, and see if the kernel
> > > complains.
> > >
> > > Sample output:
> > >
> > > # bpftool feature probe kernel
> > > ...
> > > Scanning eBPF map types...
> > > eBPF map_type hash is available
> > > eBPF map_type array is available
> > > eBPF map_type prog_array is available
> > > ...
> > >
> > > # bpftool --json --pretty feature probe kernel
> > > {
> > > ...
> > > "map_types": {
> > > "have_hash_map_type": true,
> > > "have_array_map_type": true,
> > > "have_prog_array_map_type": true,
> > > ...
> > > }
> > > }
> > >
> > > v2:
> > > - Move probes from bpftool to libbpf.
> > > - Remove C-style macros output from this patch.
> > >
> > > Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> > > ---
> > > tools/bpf/bpftool/feature.c | 26 +++++++++++++++
> > > tools/bpf/bpftool/main.h | 3 ++
> > > tools/bpf/bpftool/map.c | 4 ++-
> > > tools/lib/bpf/libbpf.h | 1 +
> > > tools/lib/bpf/libbpf.map | 1 +
> > > tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
> > > 6 files changed, 93 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> > > index 3ba0a0a5904c..3c44953ded5a 100644
> > > --- a/tools/bpf/bpftool/feature.c
> > > +++ b/tools/bpf/bpftool/feature.c
> > > @@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > print_bool_feature(feat_name, plain_desc, res);
> > > }
> > > +static void probe_map_type(enum bpf_map_type map_type)
> > > +{
> > > + const char *plain_comment = "eBPF map_type ";
> > > + char feat_name[128], plain_desc[128];
> > > + size_t maxlen;
> > > + bool res;
> > > +
> > > + res = bpf_probe_map_type(map_type, 0);
> > > +
> > > + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
> > > + if (strlen(map_type_name[map_type]) > maxlen) {
> > > + p_info("map type name too long");
> > > + return;
> > > + }
> > > +
> > > + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
> > > + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
> > > + print_bool_feature(feat_name, plain_desc, res);
> > > +}
> > > +
> > > static int do_probe(int argc, char **argv)
> > > {
> > > enum probe_component target = COMPONENT_UNSPEC;
> > > @@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
> > > for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
> > > probe_prog_type(i, kernel_version, supported_types);
> > > + print_end_then_start_section("map_types",
> > > + "Scanning eBPF map types...");
> > > +
> > > + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
> > > + probe_map_type(i);
> > > +
> > > exit_close_json:
> > > if (json_output) {
> > > /* End current "section" of probes */
> > > diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
> > > index 5cfc6601de9b..d7dd84d3c660 100644
> > > --- a/tools/bpf/bpftool/main.h
> > > +++ b/tools/bpf/bpftool/main.h
> > > @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
> > > [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
> > > };
> > > +extern const char * const map_type_name[];
> > > +extern const size_t map_type_name_size;
> > > +
> > > enum bpf_obj_type {
> > > BPF_OBJ_UNKNOWN,
> > > BPF_OBJ_PROG,
> > > diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
> > > index 2037e3dc864b..b73985589929 100644
> > > --- a/tools/bpf/bpftool/map.c
> > > +++ b/tools/bpf/bpftool/map.c
> > > @@ -21,7 +21,7 @@
> > > #include "json_writer.h"
> > > #include "main.h"
> > > -static const char * const map_type_name[] = {
> > > +const char * const map_type_name[] = {
> > > [BPF_MAP_TYPE_UNSPEC] = "unspec",
> > > [BPF_MAP_TYPE_HASH] = "hash",
> > > [BPF_MAP_TYPE_ARRAY] = "array",
> > > @@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
> > > [BPF_MAP_TYPE_STACK] = "stack",
> > > };
> > > +const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
> > > +
> > > static bool map_is_per_cpu(__u32 type)
> > > {
> > > return type == BPF_MAP_TYPE_PERCPU_HASH ||
> > > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > > index f4bb2764ca9a..202c1ee5c579 100644
> > > --- a/tools/lib/bpf/libbpf.h
> > > +++ b/tools/lib/bpf/libbpf.h
> > > @@ -360,6 +360,7 @@ 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);
> > > #ifdef __cplusplus
> > > } /* extern "C" */
> > > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > > index 6355e4c80a86..c08f4c726e8e 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_map_type;
> > > bpf_probe_prog_type;
> > > bpf_prog_attach;
> > > bpf_prog_detach;
> > > diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> > > index 2c5e0cdc9f2f..796fe1e66169 100644
> > > --- a/tools/lib/bpf/libbpf_probes.c
> > > +++ b/tools/lib/bpf/libbpf_probes.c
> > > @@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > return errno != EINVAL && errno != EOPNOTSUPP;
> > > }
> > > +
> > > +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
> > > +{
> > > + int key_size, value_size, max_entries, map_flags;
> > > + struct bpf_create_map_attr attr = {};
> > > + int fd = -1, fd_inner;
> > > +
> > > + key_size = sizeof(__u32);
> > > + value_size = sizeof(__u32);
> > > + max_entries = 1;
> > > + map_flags = 0;
> > > +
> > > + if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
> > How about using switch here? So it yells and complains when the new item
> > is added to the bpf_map_type enum.
> > > + key_size = sizeof(__u64);
> > > + value_size = sizeof(__u64);
> > > + map_flags = BPF_F_NO_PREALLOC;
> > > + } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
> > > + value_size = sizeof(__u64);
> > > + } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
> > > + map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
> > > + key_size = sizeof(struct bpf_cgroup_storage_key);
> > > + value_size = sizeof(__u64);
> > > + max_entries = 0;
> > > + } else if (map_type == BPF_MAP_TYPE_QUEUE ||
> > > + map_type == BPF_MAP_TYPE_STACK) {
> > > + key_size = 0;
> > > + }
>
> Hmm so I had a switch in the first place and it complained even with a
> "default" label because -Wno-switch-enum is used for compiling. So you would
> have me use a switch with all existing map types as explicit labels, even if
> we do not use them? To limit the risk of forgetting to update for new map
> types that would need specific parameters for the probe, is that correct?
Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
looks more rigid, it makes sure we never forget to update the probes
when adding new map types.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [oss-drivers] Re: [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types
2018-12-20 18:17 ` Stanislav Fomichev
@ 2018-12-20 18:21 ` Quentin Monnet
2018-12-20 18:29 ` Stanislav Fomichev
0 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:21 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 10:17 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> On 12/20, Quentin Monnet wrote:
>> 2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
>>> On 12/20, Quentin Monnet wrote:
>>>> Introduce probes for supported BPF program types in libbpf, and call it
>>>> from bpftool to test what types are available on the system. The probe
>>>> simply consists in loading a very simple program of that type and see if
>>>> the verifier complains or not.
>>>>
>>>> Sample output:
>>>>
>>>> # bpftool feature probe kernel
>>>> ...
>>>> Scanning eBPF program types...
>>>> eBPF program_type socket_filter is available
>>>> eBPF program_type kprobe is available
>>>> eBPF program_type sched_cls is available
>>>> ...
>>>>
>>>> # bpftool --json --pretty feature probe kernel
>>>> {
>>>> ...
>>>> "program_types": {
>>>> "have_socket_filter_prog_type": true,
>>>> "have_kprobe_prog_type": true,
>>>> "have_sched_cls_prog_type": true,
>>>> ...
>>>> }
>>>> }
>>>>
>>>> v2:
>>>> - Move probes from bpftool to libbpf.
>>>> - Remove C-style macros output from this patch.
>>>>
>>>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>>>> ---
>>>> tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
>>>> tools/lib/bpf/Build | 2 +-
>>>> tools/lib/bpf/libbpf.h | 6 ++++
>>>> tools/lib/bpf/libbpf.map | 1 +
>>>> tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
>>>> 5 files changed, 114 insertions(+), 3 deletions(-)
>>>> create mode 100644 tools/lib/bpf/libbpf_probes.c
>>>>
>>>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>>>> index 238d7b80f426..3ba0a0a5904c 100644
>>>> --- a/tools/bpf/bpftool/feature.c
>>>> +++ b/tools/bpf/bpftool/feature.c
>>>> @@ -1,6 +1,7 @@
>>>> // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>> /* Copyright (c) 2018 Netronome Systems, Inc. */
>>>> +#include <ctype.h>
>>>> #include <errno.h>
>>>> #include <string.h>
>>>> #include <unistd.h>
>>>> @@ -11,6 +12,7 @@
>>>> #include <linux/limits.h>
>>>> #include <bpf.h>
>>>> +#include <libbpf.h>
>>>> #include "main.h"
>>>> @@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
>>>> }
>>>> }
>>>> +static void
>>>> +print_end_then_start_section(const char *json_title, const char *plain_title)
>>>> +{
>>>> + if (json_output)
>>>> + jsonw_end_object(json_wtr);
>>>> + else
>>>> + printf("\n");
>>>> +
>>>> + print_start_section(json_title, plain_title);
>>>> +}
>>>> +
>>>> /* Probing functions */
>>>> static int read_procfs(const char *path)
>>>> @@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)
>>>> return res;
>>>> }
>>>> +static void
>>>> +probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>>> + bool *supported_types)
>>>> +{
>>>> + const char *plain_comment = "eBPF program_type ";
>>>> + char feat_name[128], plain_desc[128];
>>>> + size_t maxlen;
>>>> + bool res;
>>>> +
>>>> + res = bpf_probe_prog_type(prog_type, kernel_version, 0);
>>>> +
>>>> + supported_types[prog_type] |= res;
>>>> +
>>>> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
>>>> + if (strlen(prog_type_name[prog_type]) > maxlen) {
>>>> + p_info("program type name too long");
>>>> + return;
>>>> + }
>>>> +
>>>> + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
>>>> + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
>>>> + print_bool_feature(feat_name, plain_desc, res);
>>>> +}
>>>> +
>>>> static int do_probe(int argc, char **argv)
>>>> {
>>>> enum probe_component target = COMPONENT_UNSPEC;
>>>> + bool supported_types[128] = {};
>>>> + int kernel_version;
>>>> + unsigned int i;
>>>> /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
>>>> * Let's approximate, and restrict usage to root user only.
>>>> @@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)
>>>> print_start_section("syscall_config",
>>>> "Scanning system call and kernel version...");
>>>> - probe_kernel_version();
>>>> - probe_bpf_syscall();
>>>> + kernel_version = probe_kernel_version();
>>>> + if (!probe_bpf_syscall())
>>>> + /* bpf() syscall unavailable, don't probe other BPF features */
>>>> + goto exit_close_json;
>>>> +
>>>> + print_end_then_start_section("program_types",
>>>> + "Scanning eBPF program types...");
>>>> +
>>>> + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
>>>> + probe_prog_type(i, kernel_version, supported_types);
>>>> +exit_close_json:
>>>> if (json_output) {
>>>> /* End current "section" of probes */
>>>> jsonw_end_object(json_wtr);
>>>> diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
>>>> index 197b40f5b5c6..bfd9bfc82c3b 100644
>>>> --- a/tools/lib/bpf/Build
>>>> +++ b/tools/lib/bpf/Build
>>>> @@ -1 +1 @@
>>>> -libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
>>>> +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
>>>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>>>> index 5f68d7b75215..f4bb2764ca9a 100644
>>>> --- a/tools/lib/bpf/libbpf.h
>>>> +++ b/tools/lib/bpf/libbpf.h
>>>> @@ -355,6 +355,12 @@ LIBBPF_API const struct bpf_line_info *
>>>> bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
>>>> __u32 insn_off, __u32 nr_skip);
>>>> +/*
>>>> + * Probe for supported system features
>>>> + */
>>>> +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
>>>> + int kernel_version, __u32 ifindex);
>>>> +
>>>> #ifdef __cplusplus
>>>> } /* extern "C" */
>>>> #endif
>>>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>>>> index cd02cd4e2cc3..6355e4c80a86 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_prog_type;
>>>> bpf_prog_attach;
>>>> bpf_prog_detach;
>>>> bpf_prog_detach2;
>>>> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
>>>> new file mode 100644
>>>> index 000000000000..2c5e0cdc9f2f
>>>> --- /dev/null
>>>> +++ b/tools/lib/bpf/libbpf_probes.c
>>>> @@ -0,0 +1,55 @@
>>>> +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
>>>> +
>>>> +/* Copyright (c) 2018 Netronome Systems, Inc. */
>>>> +
>>>> +#include <errno.h>
>>>> +#include <unistd.h>
>>>> +
>>>> +#include <linux/filter.h>
>>>> +#include <linux/kernel.h>
>>>> +
>>>> +#include "bpf.h"
>>>> +#include "libbpf.h"
>>>> +
>>>> +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,
>>>> + __u32 ifindex)
>>>> +{
>>>> + struct bpf_load_program_attr xattr = {};
>>>> + int fd;
>>>> +
>>>> + /* Some prog type require an expected_attach_type */
>>>> + if (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR)
>>>> + xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
>>>> +
>>>> + xattr.prog_type = prog_type;
>>>> + xattr.insns = insns;
>>>> + xattr.insns_cnt = insns_cnt;
>>>> + xattr.license = "GPL";
>>>> + xattr.kern_version = kernel_version;
>>>> + xattr.prog_ifindex = ifindex;
>>>> +
>>>> + fd = bpf_load_program_xattr(&xattr, buf, buf_len);
>>>> + if (fd >= 0)
>>>> + close(fd);
>>>> +}
>>>> +
>>>> +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>> Do we really need this kernel_version argument? Isn't it going away in
>>> the future (I saw a patch from Daniel that drops kernel check under
>>> sys_bpf). Going forward, does it make sense to have it at the API
>>> level?
>>
>> It does go away and should be left aside for new applications. The objective
>> with bpftool is to be able to probe systems possibly running older kernels
>> though, and the only way to probe for kprobes support on anything older than
>> a 4.21 is to keep the version number.
> But isn't kernel_version irrelevant for probing? For probing, we always want
> to have kernel_version == <running kernel version>. So I don't
> understand why libbpf user should care about the version.
Oh, do you mean it should be collected in libbpf instead of bpftool and
not passed by the caller? Sorry I didn't understand in the first place.
If so, you're absolutely right, I should fix that, thanks!
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 18:18 ` Stanislav Fomichev
@ 2018-12-20 18:27 ` Quentin Monnet
2018-12-20 18:42 ` Stanislav Fomichev
0 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:27 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 10:18 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> On 12/20, Quentin Monnet wrote:
>> 2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
>>> On 12/20, Quentin Monnet wrote:
>>>> Add new probes for eBPF map types, to detect what are the ones available
>>>> on the system. Try creating one map of each type, and see if the kernel
>>>> complains.
>>>>
>>>> Sample output:
>>>>
>>>> # bpftool feature probe kernel
>>>> ...
>>>> Scanning eBPF map types...
>>>> eBPF map_type hash is available
>>>> eBPF map_type array is available
>>>> eBPF map_type prog_array is available
>>>> ...
>>>>
>>>> # bpftool --json --pretty feature probe kernel
>>>> {
>>>> ...
>>>> "map_types": {
>>>> "have_hash_map_type": true,
>>>> "have_array_map_type": true,
>>>> "have_prog_array_map_type": true,
>>>> ...
>>>> }
>>>> }
>>>>
>>>> v2:
>>>> - Move probes from bpftool to libbpf.
>>>> - Remove C-style macros output from this patch.
>>>>
>>>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>>>> ---
>>>> tools/bpf/bpftool/feature.c | 26 +++++++++++++++
>>>> tools/bpf/bpftool/main.h | 3 ++
>>>> tools/bpf/bpftool/map.c | 4 ++-
>>>> tools/lib/bpf/libbpf.h | 1 +
>>>> tools/lib/bpf/libbpf.map | 1 +
>>>> tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
>>>> 6 files changed, 93 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>>>> index 3ba0a0a5904c..3c44953ded5a 100644
>>>> --- a/tools/bpf/bpftool/feature.c
>>>> +++ b/tools/bpf/bpftool/feature.c
>>>> @@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>>> print_bool_feature(feat_name, plain_desc, res);
>>>> }
>>>> +static void probe_map_type(enum bpf_map_type map_type)
>>>> +{
>>>> + const char *plain_comment = "eBPF map_type ";
>>>> + char feat_name[128], plain_desc[128];
>>>> + size_t maxlen;
>>>> + bool res;
>>>> +
>>>> + res = bpf_probe_map_type(map_type, 0);
>>>> +
>>>> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
>>>> + if (strlen(map_type_name[map_type]) > maxlen) {
>>>> + p_info("map type name too long");
>>>> + return;
>>>> + }
>>>> +
>>>> + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
>>>> + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
>>>> + print_bool_feature(feat_name, plain_desc, res);
>>>> +}
>>>> +
>>>> static int do_probe(int argc, char **argv)
>>>> {
>>>> enum probe_component target = COMPONENT_UNSPEC;
>>>> @@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
>>>> for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
>>>> probe_prog_type(i, kernel_version, supported_types);
>>>> + print_end_then_start_section("map_types",
>>>> + "Scanning eBPF map types...");
>>>> +
>>>> + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
>>>> + probe_map_type(i);
>>>> +
>>>> exit_close_json:
>>>> if (json_output) {
>>>> /* End current "section" of probes */
>>>> diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
>>>> index 5cfc6601de9b..d7dd84d3c660 100644
>>>> --- a/tools/bpf/bpftool/main.h
>>>> +++ b/tools/bpf/bpftool/main.h
>>>> @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
>>>> [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
>>>> };
>>>> +extern const char * const map_type_name[];
>>>> +extern const size_t map_type_name_size;
>>>> +
>>>> enum bpf_obj_type {
>>>> BPF_OBJ_UNKNOWN,
>>>> BPF_OBJ_PROG,
>>>> diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
>>>> index 2037e3dc864b..b73985589929 100644
>>>> --- a/tools/bpf/bpftool/map.c
>>>> +++ b/tools/bpf/bpftool/map.c
>>>> @@ -21,7 +21,7 @@
>>>> #include "json_writer.h"
>>>> #include "main.h"
>>>> -static const char * const map_type_name[] = {
>>>> +const char * const map_type_name[] = {
>>>> [BPF_MAP_TYPE_UNSPEC] = "unspec",
>>>> [BPF_MAP_TYPE_HASH] = "hash",
>>>> [BPF_MAP_TYPE_ARRAY] = "array",
>>>> @@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
>>>> [BPF_MAP_TYPE_STACK] = "stack",
>>>> };
>>>> +const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
>>>> +
>>>> static bool map_is_per_cpu(__u32 type)
>>>> {
>>>> return type == BPF_MAP_TYPE_PERCPU_HASH ||
>>>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>>>> index f4bb2764ca9a..202c1ee5c579 100644
>>>> --- a/tools/lib/bpf/libbpf.h
>>>> +++ b/tools/lib/bpf/libbpf.h
>>>> @@ -360,6 +360,7 @@ 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);
>>>> #ifdef __cplusplus
>>>> } /* extern "C" */
>>>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>>>> index 6355e4c80a86..c08f4c726e8e 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_map_type;
>>>> bpf_probe_prog_type;
>>>> bpf_prog_attach;
>>>> bpf_prog_detach;
>>>> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
>>>> index 2c5e0cdc9f2f..796fe1e66169 100644
>>>> --- a/tools/lib/bpf/libbpf_probes.c
>>>> +++ b/tools/lib/bpf/libbpf_probes.c
>>>> @@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>>> return errno != EINVAL && errno != EOPNOTSUPP;
>>>> }
>>>> +
>>>> +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
>>>> +{
>>>> + int key_size, value_size, max_entries, map_flags;
>>>> + struct bpf_create_map_attr attr = {};
>>>> + int fd = -1, fd_inner;
>>>> +
>>>> + key_size = sizeof(__u32);
>>>> + value_size = sizeof(__u32);
>>>> + max_entries = 1;
>>>> + map_flags = 0;
>>>> +
>>>> + if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
>>> How about using switch here? So it yells and complains when the new item
>>> is added to the bpf_map_type enum.
>>>> + key_size = sizeof(__u64);
>>>> + value_size = sizeof(__u64);
>>>> + map_flags = BPF_F_NO_PREALLOC;
>>>> + } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
>>>> + value_size = sizeof(__u64);
>>>> + } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
>>>> + map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
>>>> + key_size = sizeof(struct bpf_cgroup_storage_key);
>>>> + value_size = sizeof(__u64);
>>>> + max_entries = 0;
>>>> + } else if (map_type == BPF_MAP_TYPE_QUEUE ||
>>>> + map_type == BPF_MAP_TYPE_STACK) {
>>>> + key_size = 0;
>>>> + }
>>
>> Hmm so I had a switch in the first place and it complained even with a
>> "default" label because -Wno-switch-enum is used for compiling. So you would
>> have me use a switch with all existing map types as explicit labels, even if
>> we do not use them? To limit the risk of forgetting to update for new map
>> types that would need specific parameters for the probe, is that correct?
> Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
> looks more rigid, it makes sure we never forget to update the probes
> when adding new map types.
True... If it is acceptable to take the risk of breaking compilation for
libbpf if we forget to update the list when new map types are added (I
mean, this is the desired effect for keeping the list of parameters
up-to-date, but people e.g. just trying to compile bpftool might have
issues in such a case), I can change my code to use a switch.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [oss-drivers] Re: [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types
2018-12-20 18:21 ` Quentin Monnet
@ 2018-12-20 18:29 ` Stanislav Fomichev
0 siblings, 0 replies; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 18:29 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> 2018-12-20 10:17 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > On 12/20, Quentin Monnet wrote:
> > > 2018-12-20 09:45 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > > > On 12/20, Quentin Monnet wrote:
> > > > > Introduce probes for supported BPF program types in libbpf, and call it
> > > > > from bpftool to test what types are available on the system. The probe
> > > > > simply consists in loading a very simple program of that type and see if
> > > > > the verifier complains or not.
> > > > >
> > > > > Sample output:
> > > > >
> > > > > # bpftool feature probe kernel
> > > > > ...
> > > > > Scanning eBPF program types...
> > > > > eBPF program_type socket_filter is available
> > > > > eBPF program_type kprobe is available
> > > > > eBPF program_type sched_cls is available
> > > > > ...
> > > > >
> > > > > # bpftool --json --pretty feature probe kernel
> > > > > {
> > > > > ...
> > > > > "program_types": {
> > > > > "have_socket_filter_prog_type": true,
> > > > > "have_kprobe_prog_type": true,
> > > > > "have_sched_cls_prog_type": true,
> > > > > ...
> > > > > }
> > > > > }
> > > > >
> > > > > v2:
> > > > > - Move probes from bpftool to libbpf.
> > > > > - Remove C-style macros output from this patch.
> > > > >
> > > > > Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> > > > > ---
> > > > > tools/bpf/bpftool/feature.c | 53 +++++++++++++++++++++++++++++++--
> > > > > tools/lib/bpf/Build | 2 +-
> > > > > tools/lib/bpf/libbpf.h | 6 ++++
> > > > > tools/lib/bpf/libbpf.map | 1 +
> > > > > tools/lib/bpf/libbpf_probes.c | 55 +++++++++++++++++++++++++++++++++++
> > > > > 5 files changed, 114 insertions(+), 3 deletions(-)
> > > > > create mode 100644 tools/lib/bpf/libbpf_probes.c
> > > > >
> > > > > diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> > > > > index 238d7b80f426..3ba0a0a5904c 100644
> > > > > --- a/tools/bpf/bpftool/feature.c
> > > > > +++ b/tools/bpf/bpftool/feature.c
> > > > > @@ -1,6 +1,7 @@
> > > > > // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > > /* Copyright (c) 2018 Netronome Systems, Inc. */
> > > > > +#include <ctype.h>
> > > > > #include <errno.h>
> > > > > #include <string.h>
> > > > > #include <unistd.h>
> > > > > @@ -11,6 +12,7 @@
> > > > > #include <linux/limits.h>
> > > > > #include <bpf.h>
> > > > > +#include <libbpf.h>
> > > > > #include "main.h"
> > > > > @@ -83,6 +85,17 @@ print_start_section(const char *json_title, const char *plain_title)
> > > > > }
> > > > > }
> > > > > +static void
> > > > > +print_end_then_start_section(const char *json_title, const char *plain_title)
> > > > > +{
> > > > > + if (json_output)
> > > > > + jsonw_end_object(json_wtr);
> > > > > + else
> > > > > + printf("\n");
> > > > > +
> > > > > + print_start_section(json_title, plain_title);
> > > > > +}
> > > > > +
> > > > > /* Probing functions */
> > > > > static int read_procfs(const char *path)
> > > > > @@ -361,9 +374,36 @@ static bool probe_bpf_syscall(void)
> > > > > return res;
> > > > > }
> > > > > +static void
> > > > > +probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > > > + bool *supported_types)
> > > > > +{
> > > > > + const char *plain_comment = "eBPF program_type ";
> > > > > + char feat_name[128], plain_desc[128];
> > > > > + size_t maxlen;
> > > > > + bool res;
> > > > > +
> > > > > + res = bpf_probe_prog_type(prog_type, kernel_version, 0);
> > > > > +
> > > > > + supported_types[prog_type] |= res;
> > > > > +
> > > > > + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
> > > > > + if (strlen(prog_type_name[prog_type]) > maxlen) {
> > > > > + p_info("program type name too long");
> > > > > + return;
> > > > > + }
> > > > > +
> > > > > + sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
> > > > > + sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
> > > > > + print_bool_feature(feat_name, plain_desc, res);
> > > > > +}
> > > > > +
> > > > > static int do_probe(int argc, char **argv)
> > > > > {
> > > > > enum probe_component target = COMPONENT_UNSPEC;
> > > > > + bool supported_types[128] = {};
> > > > > + int kernel_version;
> > > > > + unsigned int i;
> > > > > /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
> > > > > * Let's approximate, and restrict usage to root user only.
> > > > > @@ -417,9 +457,18 @@ static int do_probe(int argc, char **argv)
> > > > > print_start_section("syscall_config",
> > > > > "Scanning system call and kernel version...");
> > > > > - probe_kernel_version();
> > > > > - probe_bpf_syscall();
> > > > > + kernel_version = probe_kernel_version();
> > > > > + if (!probe_bpf_syscall())
> > > > > + /* bpf() syscall unavailable, don't probe other BPF features */
> > > > > + goto exit_close_json;
> > > > > +
> > > > > + print_end_then_start_section("program_types",
> > > > > + "Scanning eBPF program types...");
> > > > > +
> > > > > + for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
> > > > > + probe_prog_type(i, kernel_version, supported_types);
> > > > > +exit_close_json:
> > > > > if (json_output) {
> > > > > /* End current "section" of probes */
> > > > > jsonw_end_object(json_wtr);
> > > > > diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
> > > > > index 197b40f5b5c6..bfd9bfc82c3b 100644
> > > > > --- a/tools/lib/bpf/Build
> > > > > +++ b/tools/lib/bpf/Build
> > > > > @@ -1 +1 @@
> > > > > -libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o
> > > > > +libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o netlink.o bpf_prog_linfo.o libbpf_probes.o
> > > > > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > > > > index 5f68d7b75215..f4bb2764ca9a 100644
> > > > > --- a/tools/lib/bpf/libbpf.h
> > > > > +++ b/tools/lib/bpf/libbpf.h
> > > > > @@ -355,6 +355,12 @@ LIBBPF_API const struct bpf_line_info *
> > > > > bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
> > > > > __u32 insn_off, __u32 nr_skip);
> > > > > +/*
> > > > > + * Probe for supported system features
> > > > > + */
> > > > > +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
> > > > > + int kernel_version, __u32 ifindex);
> > > > > +
> > > > > #ifdef __cplusplus
> > > > > } /* extern "C" */
> > > > > #endif
> > > > > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > > > > index cd02cd4e2cc3..6355e4c80a86 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_prog_type;
> > > > > bpf_prog_attach;
> > > > > bpf_prog_detach;
> > > > > bpf_prog_detach2;
> > > > > diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> > > > > new file mode 100644
> > > > > index 000000000000..2c5e0cdc9f2f
> > > > > --- /dev/null
> > > > > +++ b/tools/lib/bpf/libbpf_probes.c
> > > > > @@ -0,0 +1,55 @@
> > > > > +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
> > > > > +
> > > > > +/* Copyright (c) 2018 Netronome Systems, Inc. */
> > > > > +
> > > > > +#include <errno.h>
> > > > > +#include <unistd.h>
> > > > > +
> > > > > +#include <linux/filter.h>
> > > > > +#include <linux/kernel.h>
> > > > > +
> > > > > +#include "bpf.h"
> > > > > +#include "libbpf.h"
> > > > > +
> > > > > +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,
> > > > > + __u32 ifindex)
> > > > > +{
> > > > > + struct bpf_load_program_attr xattr = {};
> > > > > + int fd;
> > > > > +
> > > > > + /* Some prog type require an expected_attach_type */
> > > > > + if (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR)
> > > > > + xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
> > > > > +
> > > > > + xattr.prog_type = prog_type;
> > > > > + xattr.insns = insns;
> > > > > + xattr.insns_cnt = insns_cnt;
> > > > > + xattr.license = "GPL";
> > > > > + xattr.kern_version = kernel_version;
> > > > > + xattr.prog_ifindex = ifindex;
> > > > > +
> > > > > + fd = bpf_load_program_xattr(&xattr, buf, buf_len);
> > > > > + if (fd >= 0)
> > > > > + close(fd);
> > > > > +}
> > > > > +
> > > > > +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > > Do we really need this kernel_version argument? Isn't it going away in
> > > > the future (I saw a patch from Daniel that drops kernel check under
> > > > sys_bpf). Going forward, does it make sense to have it at the API
> > > > level?
> > >
> > > It does go away and should be left aside for new applications. The objective
> > > with bpftool is to be able to probe systems possibly running older kernels
> > > though, and the only way to probe for kprobes support on anything older than
> > > a 4.21 is to keep the version number.
> > But isn't kernel_version irrelevant for probing? For probing, we always want
> > to have kernel_version == <running kernel version>. So I don't
> > understand why libbpf user should care about the version.
>
> Oh, do you mean it should be collected in libbpf instead of bpftool and not
> passed by the caller? Sorry I didn't understand in the first place. If so,
> you're absolutely right, I should fix that, thanks!
Yes, I was basically trying to understand why the user needs to pass it.
Querying/setting it for type==trace_point in prog_load seems like a way to go.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 18:27 ` Quentin Monnet
@ 2018-12-20 18:42 ` Stanislav Fomichev
2018-12-20 18:45 ` Quentin Monnet
0 siblings, 1 reply; 29+ messages in thread
From: Stanislav Fomichev @ 2018-12-20 18:42 UTC (permalink / raw)
To: Quentin Monnet
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
On 12/20, Quentin Monnet wrote:
> 2018-12-20 10:18 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > On 12/20, Quentin Monnet wrote:
> > > 2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> > > > On 12/20, Quentin Monnet wrote:
> > > > > Add new probes for eBPF map types, to detect what are the ones available
> > > > > on the system. Try creating one map of each type, and see if the kernel
> > > > > complains.
> > > > >
> > > > > Sample output:
> > > > >
> > > > > # bpftool feature probe kernel
> > > > > ...
> > > > > Scanning eBPF map types...
> > > > > eBPF map_type hash is available
> > > > > eBPF map_type array is available
> > > > > eBPF map_type prog_array is available
> > > > > ...
> > > > >
> > > > > # bpftool --json --pretty feature probe kernel
> > > > > {
> > > > > ...
> > > > > "map_types": {
> > > > > "have_hash_map_type": true,
> > > > > "have_array_map_type": true,
> > > > > "have_prog_array_map_type": true,
> > > > > ...
> > > > > }
> > > > > }
> > > > >
> > > > > v2:
> > > > > - Move probes from bpftool to libbpf.
> > > > > - Remove C-style macros output from this patch.
> > > > >
> > > > > Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> > > > > ---
> > > > > tools/bpf/bpftool/feature.c | 26 +++++++++++++++
> > > > > tools/bpf/bpftool/main.h | 3 ++
> > > > > tools/bpf/bpftool/map.c | 4 ++-
> > > > > tools/lib/bpf/libbpf.h | 1 +
> > > > > tools/lib/bpf/libbpf.map | 1 +
> > > > > tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
> > > > > 6 files changed, 93 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> > > > > index 3ba0a0a5904c..3c44953ded5a 100644
> > > > > --- a/tools/bpf/bpftool/feature.c
> > > > > +++ b/tools/bpf/bpftool/feature.c
> > > > > @@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > > > print_bool_feature(feat_name, plain_desc, res);
> > > > > }
> > > > > +static void probe_map_type(enum bpf_map_type map_type)
> > > > > +{
> > > > > + const char *plain_comment = "eBPF map_type ";
> > > > > + char feat_name[128], plain_desc[128];
> > > > > + size_t maxlen;
> > > > > + bool res;
> > > > > +
> > > > > + res = bpf_probe_map_type(map_type, 0);
> > > > > +
> > > > > + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
> > > > > + if (strlen(map_type_name[map_type]) > maxlen) {
> > > > > + p_info("map type name too long");
> > > > > + return;
> > > > > + }
> > > > > +
> > > > > + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
> > > > > + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
> > > > > + print_bool_feature(feat_name, plain_desc, res);
> > > > > +}
> > > > > +
> > > > > static int do_probe(int argc, char **argv)
> > > > > {
> > > > > enum probe_component target = COMPONENT_UNSPEC;
> > > > > @@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
> > > > > for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
> > > > > probe_prog_type(i, kernel_version, supported_types);
> > > > > + print_end_then_start_section("map_types",
> > > > > + "Scanning eBPF map types...");
> > > > > +
> > > > > + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
> > > > > + probe_map_type(i);
> > > > > +
> > > > > exit_close_json:
> > > > > if (json_output) {
> > > > > /* End current "section" of probes */
> > > > > diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
> > > > > index 5cfc6601de9b..d7dd84d3c660 100644
> > > > > --- a/tools/bpf/bpftool/main.h
> > > > > +++ b/tools/bpf/bpftool/main.h
> > > > > @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
> > > > > [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
> > > > > };
> > > > > +extern const char * const map_type_name[];
> > > > > +extern const size_t map_type_name_size;
> > > > > +
> > > > > enum bpf_obj_type {
> > > > > BPF_OBJ_UNKNOWN,
> > > > > BPF_OBJ_PROG,
> > > > > diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
> > > > > index 2037e3dc864b..b73985589929 100644
> > > > > --- a/tools/bpf/bpftool/map.c
> > > > > +++ b/tools/bpf/bpftool/map.c
> > > > > @@ -21,7 +21,7 @@
> > > > > #include "json_writer.h"
> > > > > #include "main.h"
> > > > > -static const char * const map_type_name[] = {
> > > > > +const char * const map_type_name[] = {
> > > > > [BPF_MAP_TYPE_UNSPEC] = "unspec",
> > > > > [BPF_MAP_TYPE_HASH] = "hash",
> > > > > [BPF_MAP_TYPE_ARRAY] = "array",
> > > > > @@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
> > > > > [BPF_MAP_TYPE_STACK] = "stack",
> > > > > };
> > > > > +const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
> > > > > +
> > > > > static bool map_is_per_cpu(__u32 type)
> > > > > {
> > > > > return type == BPF_MAP_TYPE_PERCPU_HASH ||
> > > > > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > > > > index f4bb2764ca9a..202c1ee5c579 100644
> > > > > --- a/tools/lib/bpf/libbpf.h
> > > > > +++ b/tools/lib/bpf/libbpf.h
> > > > > @@ -360,6 +360,7 @@ 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);
> > > > > #ifdef __cplusplus
> > > > > } /* extern "C" */
> > > > > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > > > > index 6355e4c80a86..c08f4c726e8e 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_map_type;
> > > > > bpf_probe_prog_type;
> > > > > bpf_prog_attach;
> > > > > bpf_prog_detach;
> > > > > diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> > > > > index 2c5e0cdc9f2f..796fe1e66169 100644
> > > > > --- a/tools/lib/bpf/libbpf_probes.c
> > > > > +++ b/tools/lib/bpf/libbpf_probes.c
> > > > > @@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
> > > > > return errno != EINVAL && errno != EOPNOTSUPP;
> > > > > }
> > > > > +
> > > > > +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
> > > > > +{
> > > > > + int key_size, value_size, max_entries, map_flags;
> > > > > + struct bpf_create_map_attr attr = {};
> > > > > + int fd = -1, fd_inner;
> > > > > +
> > > > > + key_size = sizeof(__u32);
> > > > > + value_size = sizeof(__u32);
> > > > > + max_entries = 1;
> > > > > + map_flags = 0;
> > > > > +
> > > > > + if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
> > > > How about using switch here? So it yells and complains when the new item
> > > > is added to the bpf_map_type enum.
> > > > > + key_size = sizeof(__u64);
> > > > > + value_size = sizeof(__u64);
> > > > > + map_flags = BPF_F_NO_PREALLOC;
> > > > > + } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
> > > > > + value_size = sizeof(__u64);
> > > > > + } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
> > > > > + map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
> > > > > + key_size = sizeof(struct bpf_cgroup_storage_key);
> > > > > + value_size = sizeof(__u64);
> > > > > + max_entries = 0;
> > > > > + } else if (map_type == BPF_MAP_TYPE_QUEUE ||
> > > > > + map_type == BPF_MAP_TYPE_STACK) {
> > > > > + key_size = 0;
> > > > > + }
> > >
> > > Hmm so I had a switch in the first place and it complained even with a
> > > "default" label because -Wno-switch-enum is used for compiling. So you would
> > > have me use a switch with all existing map types as explicit labels, even if
> > > we do not use them? To limit the risk of forgetting to update for new map
> > > types that would need specific parameters for the probe, is that correct?
> > Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
> > looks more rigid, it makes sure we never forget to update the probes
> > when adding new map types.
>
> True... If it is acceptable to take the risk of breaking compilation for
> libbpf if we forget to update the list when new map types are added (I mean,
> this is the desired effect for keeping the list of parameters up-to-date,
> but people e.g. just trying to compile bpftool might have issues in such a
> case), I can change my code to use a switch.
I think we already have a precedent, see bpf_prog_type__needs_kver in
libbpf ;-)
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types
2018-12-20 18:42 ` Stanislav Fomichev
@ 2018-12-20 18:45 ` Quentin Monnet
0 siblings, 0 replies; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 18:45 UTC (permalink / raw)
To: Stanislav Fomichev
Cc: Alexei Starovoitov, Daniel Borkmann, netdev, oss-drivers,
Arnaldo Carvalho de Melo, Jesper Dangaard Brouer,
Stanislav Fomichev
2018-12-20 10:42 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
> On 12/20, Quentin Monnet wrote:
>> 2018-12-20 10:18 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
>>> On 12/20, Quentin Monnet wrote:
>>>> 2018-12-20 09:47 UTC-0800 ~ Stanislav Fomichev <sdf@fomichev.me>
>>>>> On 12/20, Quentin Monnet wrote:
>>>>>> Add new probes for eBPF map types, to detect what are the ones available
>>>>>> on the system. Try creating one map of each type, and see if the kernel
>>>>>> complains.
>>>>>>
>>>>>> Sample output:
>>>>>>
>>>>>> # bpftool feature probe kernel
>>>>>> ...
>>>>>> Scanning eBPF map types...
>>>>>> eBPF map_type hash is available
>>>>>> eBPF map_type array is available
>>>>>> eBPF map_type prog_array is available
>>>>>> ...
>>>>>>
>>>>>> # bpftool --json --pretty feature probe kernel
>>>>>> {
>>>>>> ...
>>>>>> "map_types": {
>>>>>> "have_hash_map_type": true,
>>>>>> "have_array_map_type": true,
>>>>>> "have_prog_array_map_type": true,
>>>>>> ...
>>>>>> }
>>>>>> }
>>>>>>
>>>>>> v2:
>>>>>> - Move probes from bpftool to libbpf.
>>>>>> - Remove C-style macros output from this patch.
>>>>>>
>>>>>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>>>>>> ---
>>>>>> tools/bpf/bpftool/feature.c | 26 +++++++++++++++
>>>>>> tools/bpf/bpftool/main.h | 3 ++
>>>>>> tools/bpf/bpftool/map.c | 4 ++-
>>>>>> tools/lib/bpf/libbpf.h | 1 +
>>>>>> tools/lib/bpf/libbpf.map | 1 +
>>>>>> tools/lib/bpf/libbpf_probes.c | 59 +++++++++++++++++++++++++++++++++++
>>>>>> 6 files changed, 93 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>>>>>> index 3ba0a0a5904c..3c44953ded5a 100644
>>>>>> --- a/tools/bpf/bpftool/feature.c
>>>>>> +++ b/tools/bpf/bpftool/feature.c
>>>>>> @@ -398,6 +398,26 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>>>>> print_bool_feature(feat_name, plain_desc, res);
>>>>>> }
>>>>>> +static void probe_map_type(enum bpf_map_type map_type)
>>>>>> +{
>>>>>> + const char *plain_comment = "eBPF map_type ";
>>>>>> + char feat_name[128], plain_desc[128];
>>>>>> + size_t maxlen;
>>>>>> + bool res;
>>>>>> +
>>>>>> + res = bpf_probe_map_type(map_type, 0);
>>>>>> +
>>>>>> + maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
>>>>>> + if (strlen(map_type_name[map_type]) > maxlen) {
>>>>>> + p_info("map type name too long");
>>>>>> + return;
>>>>>> + }
>>>>>> +
>>>>>> + sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
>>>>>> + sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
>>>>>> + print_bool_feature(feat_name, plain_desc, res);
>>>>>> +}
>>>>>> +
>>>>>> static int do_probe(int argc, char **argv)
>>>>>> {
>>>>>> enum probe_component target = COMPONENT_UNSPEC;
>>>>>> @@ -468,6 +488,12 @@ static int do_probe(int argc, char **argv)
>>>>>> for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
>>>>>> probe_prog_type(i, kernel_version, supported_types);
>>>>>> + print_end_then_start_section("map_types",
>>>>>> + "Scanning eBPF map types...");
>>>>>> +
>>>>>> + for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
>>>>>> + probe_map_type(i);
>>>>>> +
>>>>>> exit_close_json:
>>>>>> if (json_output) {
>>>>>> /* End current "section" of probes */
>>>>>> diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
>>>>>> index 5cfc6601de9b..d7dd84d3c660 100644
>>>>>> --- a/tools/bpf/bpftool/main.h
>>>>>> +++ b/tools/bpf/bpftool/main.h
>>>>>> @@ -75,6 +75,9 @@ static const char * const prog_type_name[] = {
>>>>>> [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
>>>>>> };
>>>>>> +extern const char * const map_type_name[];
>>>>>> +extern const size_t map_type_name_size;
>>>>>> +
>>>>>> enum bpf_obj_type {
>>>>>> BPF_OBJ_UNKNOWN,
>>>>>> BPF_OBJ_PROG,
>>>>>> diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
>>>>>> index 2037e3dc864b..b73985589929 100644
>>>>>> --- a/tools/bpf/bpftool/map.c
>>>>>> +++ b/tools/bpf/bpftool/map.c
>>>>>> @@ -21,7 +21,7 @@
>>>>>> #include "json_writer.h"
>>>>>> #include "main.h"
>>>>>> -static const char * const map_type_name[] = {
>>>>>> +const char * const map_type_name[] = {
>>>>>> [BPF_MAP_TYPE_UNSPEC] = "unspec",
>>>>>> [BPF_MAP_TYPE_HASH] = "hash",
>>>>>> [BPF_MAP_TYPE_ARRAY] = "array",
>>>>>> @@ -48,6 +48,8 @@ static const char * const map_type_name[] = {
>>>>>> [BPF_MAP_TYPE_STACK] = "stack",
>>>>>> };
>>>>>> +const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
>>>>>> +
>>>>>> static bool map_is_per_cpu(__u32 type)
>>>>>> {
>>>>>> return type == BPF_MAP_TYPE_PERCPU_HASH ||
>>>>>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>>>>>> index f4bb2764ca9a..202c1ee5c579 100644
>>>>>> --- a/tools/lib/bpf/libbpf.h
>>>>>> +++ b/tools/lib/bpf/libbpf.h
>>>>>> @@ -360,6 +360,7 @@ 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);
>>>>>> #ifdef __cplusplus
>>>>>> } /* extern "C" */
>>>>>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>>>>>> index 6355e4c80a86..c08f4c726e8e 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_map_type;
>>>>>> bpf_probe_prog_type;
>>>>>> bpf_prog_attach;
>>>>>> bpf_prog_detach;
>>>>>> diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
>>>>>> index 2c5e0cdc9f2f..796fe1e66169 100644
>>>>>> --- a/tools/lib/bpf/libbpf_probes.c
>>>>>> +++ b/tools/lib/bpf/libbpf_probes.c
>>>>>> @@ -53,3 +53,62 @@ bool bpf_probe_prog_type(enum bpf_prog_type prog_type, int kernel_version,
>>>>>> return errno != EINVAL && errno != EOPNOTSUPP;
>>>>>> }
>>>>>> +
>>>>>> +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
>>>>>> +{
>>>>>> + int key_size, value_size, max_entries, map_flags;
>>>>>> + struct bpf_create_map_attr attr = {};
>>>>>> + int fd = -1, fd_inner;
>>>>>> +
>>>>>> + key_size = sizeof(__u32);
>>>>>> + value_size = sizeof(__u32);
>>>>>> + max_entries = 1;
>>>>>> + map_flags = 0;
>>>>>> +
>>>>>> + if (map_type == BPF_MAP_TYPE_LPM_TRIE) {
>>>>> How about using switch here? So it yells and complains when the new item
>>>>> is added to the bpf_map_type enum.
>>>>>> + key_size = sizeof(__u64);
>>>>>> + value_size = sizeof(__u64);
>>>>>> + map_flags = BPF_F_NO_PREALLOC;
>>>>>> + } else if (map_type == BPF_MAP_TYPE_STACK_TRACE) {
>>>>>> + value_size = sizeof(__u64);
>>>>>> + } else if (map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
>>>>>> + map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
>>>>>> + key_size = sizeof(struct bpf_cgroup_storage_key);
>>>>>> + value_size = sizeof(__u64);
>>>>>> + max_entries = 0;
>>>>>> + } else if (map_type == BPF_MAP_TYPE_QUEUE ||
>>>>>> + map_type == BPF_MAP_TYPE_STACK) {
>>>>>> + key_size = 0;
>>>>>> + }
>>>>
>>>> Hmm so I had a switch in the first place and it complained even with a
>>>> "default" label because -Wno-switch-enum is used for compiling. So you would
>>>> have me use a switch with all existing map types as explicit labels, even if
>>>> we do not use them? To limit the risk of forgetting to update for new map
>>>> types that would need specific parameters for the probe, is that correct?
>>> Correct. You'd have to list all enum items to pass -Wno-switch-enum. It
>>> looks more rigid, it makes sure we never forget to update the probes
>>> when adding new map types.
>>
>> True... If it is acceptable to take the risk of breaking compilation for
>> libbpf if we forget to update the list when new map types are added (I mean,
>> this is the desired effect for keeping the list of parameters up-to-date,
>> but people e.g. just trying to compile bpftool might have issues in such a
>> case), I can change my code to use a switch.
> I think we already have a precedent, see bpf_prog_type__needs_kver in
> libbpf ;-)
True, thanks! Although that function might well disappear in the future.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes
2018-12-20 17:25 ` Daniel Borkmann
@ 2018-12-20 20:05 ` Quentin Monnet
2018-12-20 22:29 ` Daniel Borkmann
0 siblings, 1 reply; 29+ messages in thread
From: Quentin Monnet @ 2018-12-20 20:05 UTC (permalink / raw)
To: Daniel Borkmann, Alexei Starovoitov
Cc: netdev, oss-drivers, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
2018-12-20 18:25 UTC+0100 ~ Daniel Borkmann <daniel@iogearbox.net>
> On 12/20/2018 01:24 PM, Quentin Monnet wrote:
>> Make bpftool able to dump a subset of the parameters collected by
>> probing the system as a listing of C-style #define macros, so that
>> external projects can reuse the result of this probing and build
>> BPF-based project in accordance with the features available on the
>> system.
>>
>> The new "macros" keyword is used to select this output. An additional
>> "prefix" keyword is added so that users can select a custom prefix for
>> macro names, in order to avoid any namespace conflict.
>>
>> Sample output:
>>
>> # bpftool feature probe kernel macros prefix FOO_
>> /*** System call availability ***/
>> #define FOO_HAVE_BPF_SYSCALL
>>
>> /*** eBPF program types ***/
>> #define FOO_HAVE_SOCKET_FILTER_PROG_TYPE
>> #define FOO_HAVE_KPROBE_PROG_TYPE
>> #define FOO_HAVE_SCHED_CLS_PROG_TYPE
>> ...
>>
>> /*** eBPF map types ***/
>> #define FOO_HAVE_HASH_MAP_TYPE
>> #define FOO_HAVE_ARRAY_MAP_TYPE
>> #define FOO_HAVE_PROG_ARRAY_MAP_TYPE
>> ...
>>
>> /*** eBPF helper functions ***/
>> ...
>> #define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
>> "lwt_xmit " \
>> "sk_skb "
>> #define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
>> "xdp "
>> #define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
>> "kprobe " \
>> "tracepoint " \
>> "perf_event " \
>> "raw_tracepoint "
>> ...
>
> Rest looks good to me, just a comment here. How would programs use the
> compat list? Wouldn't it be nicer to provide a PROG(HELPER) availability
> query along the lines of ...
>
> #if FOO_HAVE_SOCKET_FILTER(skb_load_bytes) == 1
> // ...
> #endif
>
> ... where the helper provides these macro functions and results in the
> header file? So they can be used easily in the code from BPF prog or
> normal C applications?
I was thinking about strstr() here, but it is true it is limited to
runtime and will not work with the preprocessor :s.
How would you dump parameters to make your PROG(HELPER) work? Something
along the following?
#define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_skb_load_bytes 1
#define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_bind 0
...
#define HAVE_SOCKET_FILTER_HELPER(name) \
BPF__PROG_TYPE_SOCKET_FILTER__HELPER_ ## name
or even
#define HAVE_PROG_TYPE_HELPER(type, helper) \
BPF__PROG_TYPE_ ## type ## __HELPER_ ## helper
Does that correspond to what you have in mind?
Thanks,
Quentin
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes
2018-12-20 20:05 ` Quentin Monnet
@ 2018-12-20 22:29 ` Daniel Borkmann
0 siblings, 0 replies; 29+ messages in thread
From: Daniel Borkmann @ 2018-12-20 22:29 UTC (permalink / raw)
To: Quentin Monnet, Alexei Starovoitov
Cc: netdev, oss-drivers, Arnaldo Carvalho de Melo,
Jesper Dangaard Brouer, Stanislav Fomichev
On 12/20/2018 09:05 PM, Quentin Monnet wrote:
> 2018-12-20 18:25 UTC+0100 ~ Daniel Borkmann <daniel@iogearbox.net>
>> On 12/20/2018 01:24 PM, Quentin Monnet wrote:
>>> Make bpftool able to dump a subset of the parameters collected by
>>> probing the system as a listing of C-style #define macros, so that
>>> external projects can reuse the result of this probing and build
>>> BPF-based project in accordance with the features available on the
>>> system.
>>>
>>> The new "macros" keyword is used to select this output. An additional
>>> "prefix" keyword is added so that users can select a custom prefix for
>>> macro names, in order to avoid any namespace conflict.
>>>
>>> Sample output:
>>>
>>> # bpftool feature probe kernel macros prefix FOO_
>>> /*** System call availability ***/
>>> #define FOO_HAVE_BPF_SYSCALL
>>>
>>> /*** eBPF program types ***/
>>> #define FOO_HAVE_SOCKET_FILTER_PROG_TYPE
>>> #define FOO_HAVE_KPROBE_PROG_TYPE
>>> #define FOO_HAVE_SCHED_CLS_PROG_TYPE
>>> ...
>>>
>>> /*** eBPF map types ***/
>>> #define FOO_HAVE_HASH_MAP_TYPE
>>> #define FOO_HAVE_ARRAY_MAP_TYPE
>>> #define FOO_HAVE_PROG_ARRAY_MAP_TYPE
>>> ...
>>>
>>> /*** eBPF helper functions ***/
>>> ...
>>> #define FOO_BPF_SKB_CHANGE_HEAD_HELPER_COMPAT_LIST "" \
>>> "lwt_xmit " \
>>> "sk_skb "
>>> #define FOO_BPF_XDP_ADJUST_HEAD_HELPER_COMPAT_LIST "" \
>>> "xdp "
>>> #define FOO_BPF_PROBE_READ_STR_HELPER_COMPAT_LIST "" \
>>> "kprobe " \
>>> "tracepoint " \
>>> "perf_event " \
>>> "raw_tracepoint "
>>> ...
>>
>> Rest looks good to me, just a comment here. How would programs use the
>> compat list? Wouldn't it be nicer to provide a PROG(HELPER) availability
>> query along the lines of ...
>>
>> #if FOO_HAVE_SOCKET_FILTER(skb_load_bytes) == 1
>> // ...
>> #endif
>>
>> ... where the helper provides these macro functions and results in the
>> header file? So they can be used easily in the code from BPF prog or
>> normal C applications?
>
> I was thinking about strstr() here, but it is true it is limited to runtime and will not work with the preprocessor :s.
>
> How would you dump parameters to make your PROG(HELPER) work? Something along the following?
>
> #define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_skb_load_bytes 1
> #define BPF__PROG_TYPE_SOCKET_FILTER__HELPER_bind 0
> ...
>
> #define HAVE_SOCKET_FILTER_HELPER(name) \
> BPF__PROG_TYPE_SOCKET_FILTER__HELPER_ ## name
>
> or even
>
> #define HAVE_PROG_TYPE_HELPER(type, helper) \
> BPF__PROG_TYPE_ ## type ## __HELPER_ ## helper
>
> Does that correspond to what you have in mind?
Yeah, something like this; slight preference to the latter.
Thanks,
Daniel
^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2018-12-20 22:29 UTC | newest]
Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-12-20 12:24 [RFC PATCH bpf-next v2 0/9] tools: bpftool: add probes for system and device Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 1/9] tools: bpftool: add basic probe capability, probe syscall and kversion Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 2/9] tools: bpftool: add probes for /proc/ eBPF parameters Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 3/9] tools: bpftool: add probes for kernel configuration options Quentin Monnet
2018-12-20 17:40 ` Stanislav Fomichev
2018-12-20 18:02 ` Quentin Monnet
2018-12-20 18:14 ` Stanislav Fomichev
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 4/9] tools: bpftool: add probes for eBPF program types Quentin Monnet
2018-12-20 17:45 ` Stanislav Fomichev
2018-12-20 18:06 ` [oss-drivers] " Quentin Monnet
2018-12-20 18:17 ` Stanislav Fomichev
2018-12-20 18:21 ` Quentin Monnet
2018-12-20 18:29 ` Stanislav Fomichev
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 5/9] tools: bpftool: add probes for eBPF map types Quentin Monnet
2018-12-20 17:47 ` Stanislav Fomichev
2018-12-20 18:10 ` Quentin Monnet
2018-12-20 18:18 ` Stanislav Fomichev
2018-12-20 18:27 ` Quentin Monnet
2018-12-20 18:42 ` Stanislav Fomichev
2018-12-20 18:45 ` Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 6/9] tools: bpftool: add probes for eBPF helper functions Quentin Monnet
2018-12-20 17:53 ` Stanislav Fomichev
2018-12-20 18:14 ` Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 7/9] tools: bpftool: add C-style "#define" output for probes Quentin Monnet
2018-12-20 17:25 ` Daniel Borkmann
2018-12-20 20:05 ` Quentin Monnet
2018-12-20 22:29 ` Daniel Borkmann
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 8/9] tools: bpftool: add probes for a network device Quentin Monnet
2018-12-20 12:24 ` [RFC PATCH bpf-next v2 9/9] tools: bpftool: add bash completion for bpftool probes Quentin Monnet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).