diff mbox series

[v2,bpf-next] bpftool: Restore support for BPF offload-enabled feature probing

Message ID 20220309085452.298278-1-niklas.soderlund@corigine.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series [v2,bpf-next] bpftool: Restore support for BPF offload-enabled feature probing | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/tree_selection success Clearly marked for bpf-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 10 maintainers not CCed: kuba@kernel.org paul@isovalent.com hawk@kernel.org kpsingh@kernel.org john.fastabend@gmail.com kafai@fb.com songliubraving@fb.com yhs@fb.com netdev@vger.kernel.org davem@davemloft.net
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 222 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next success VM_Test

Commit Message

Niklas Söderlund March 9, 2022, 8:54 a.m. UTC
Commit 1a56c18e6c2e4e74 ("bpftool: Stop supporting BPF offload-enabled
feature probing") removed the support to probe for BPF offload features.
This is still something that is useful for NFP NIC that can support
offloading of BPF programs.

The reason for the dropped support was that libbpf starting with v1.0
would drop support for passing the ifindex to the BPF prog/map/helper
feature probing APIs. In order to keep this useful feature for NFP
restore the functionality by moving it directly into bpftool.

The code restored is a simplified version of the code that existed in
libbpf which supposed passing the ifindex. The simplification is that it
only targets the cases where ifindex is given and call into libbpf for
the cases where it's not.

Before restoring support for probing offload features:

  # bpftool feature probe dev ens4np0
  Scanning system call availability...
  bpf() syscall is available

  Scanning eBPF program types...

  Scanning eBPF map types...

  Scanning eBPF helper functions...
  eBPF helpers supported for program type sched_cls:
  eBPF helpers supported for program type xdp:

  Scanning miscellaneous eBPF features...
  Large program size limit is NOT available
  Bounded loop support is NOT available
  ISA extension v2 is NOT available
  ISA extension v3 is NOT available

With support for probing offload features restored:

  # bpftool feature probe dev ens4np0
  Scanning system call availability...
  bpf() syscall is available

  Scanning eBPF program types...
  eBPF program_type sched_cls is available
  eBPF program_type xdp is available

  Scanning eBPF map types...
  eBPF map_type hash is available
  eBPF map_type array is available
  eBPF map_type prog_array is NOT available
  eBPF map_type perf_event_array is NOT available
  eBPF map_type percpu_hash is NOT available
  eBPF map_type percpu_array is NOT available
  eBPF map_type stack_trace is NOT available
  eBPF map_type cgroup_array is NOT available
  eBPF map_type lru_hash is NOT available
  eBPF map_type lru_percpu_hash is NOT available
  eBPF map_type lpm_trie is NOT available
  eBPF map_type array_of_maps is NOT available
  eBPF map_type hash_of_maps is NOT available
  eBPF map_type devmap is NOT available
  eBPF map_type sockmap is NOT available
  eBPF map_type cpumap is NOT available
  eBPF map_type xskmap is NOT available
  eBPF map_type sockhash is NOT available
  eBPF map_type cgroup_storage is NOT available
  eBPF map_type reuseport_sockarray is NOT available
  eBPF map_type percpu_cgroup_storage is NOT available
  eBPF map_type queue is NOT available
  eBPF map_type stack is NOT available
  eBPF map_type sk_storage is NOT available
  eBPF map_type devmap_hash is NOT available
  eBPF map_type struct_ops is NOT available
  eBPF map_type ringbuf is NOT available
  eBPF map_type inode_storage is NOT available
  eBPF map_type task_storage is NOT available
  eBPF map_type bloom_filter is NOT available

  Scanning eBPF helper functions...
  eBPF helpers supported for program type sched_cls:
  	- bpf_map_lookup_elem
  	- bpf_get_prandom_u32
  	- bpf_perf_event_output
  eBPF helpers supported for program type xdp:
  	- bpf_map_lookup_elem
  	- bpf_get_prandom_u32
  	- bpf_perf_event_output
  	- bpf_xdp_adjust_head
  	- bpf_xdp_adjust_tail

  Scanning miscellaneous eBPF features...
  Large program size limit is NOT available
  Bounded loop support is NOT available
  ISA extension v2 is NOT available
  ISA extension v3 is NOT available

Signed-off-by: Niklas Söderlund <niklas.soderlund@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
---
* Changes since v1
- Drop unneeded logic in probe_prog_load_ifindex() that was kept for
  incorrect reasons while resurrecting the functionality from the more
  generic use-case in libbpf.
- Change the return type of probe_prog_load_ifindex() from int to bool
  and perform all error checks directly.
---
 tools/bpf/bpftool/feature.c | 166 ++++++++++++++++++++++++++++++++----
 1 file changed, 151 insertions(+), 15 deletions(-)

Comments

Quentin Monnet March 9, 2022, 2:31 p.m. UTC | #1
2022-03-09 09:54 UTC+0100 ~ Niklas Söderlund <niklas.soderlund@corigine.com>
> Commit 1a56c18e6c2e4e74 ("bpftool: Stop supporting BPF offload-enabled
> feature probing") removed the support to probe for BPF offload features.
> This is still something that is useful for NFP NIC that can support
> offloading of BPF programs.
> 
> The reason for the dropped support was that libbpf starting with v1.0
> would drop support for passing the ifindex to the BPF prog/map/helper
> feature probing APIs. In order to keep this useful feature for NFP
> restore the functionality by moving it directly into bpftool.
> 
> The code restored is a simplified version of the code that existed in
> libbpf which supposed passing the ifindex. The simplification is that it
> only targets the cases where ifindex is given and call into libbpf for
> the cases where it's not.

[...]

> +static bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex)
> +{
> +	struct bpf_insn insns[2] = {
> +		BPF_MOV64_IMM(BPF_REG_0, 0),
> +		BPF_EXIT_INSN()
> +	};
> +
> +	switch (prog_type) {
> +	case BPF_PROG_TYPE_SCHED_CLS:
> +		/* nfp returns -EINVAL on exit(0) with TC offload */
> +		insns[0].imm = 2;
> +		break;
> +	case BPF_PROG_TYPE_XDP:
> +		break;
> +	default:
> +		return false;
> +	}

Looking again at this block, you can remove this switch/case completely.
Moving 0 by default into R0 was best in generic libbpf's probes because
there were a number of types that wouldn't accept 2 as a return value;
but here you can just return 2 all the time, for XDP this will mean
XDP_PASS and the nfp accepts it. (Do keep the comment on nfp returning
-EINVAL on exit(0), though, please.)

> +
> +	return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns),
> +				       NULL, 0, ifindex);
> +}
> +
>  static void
>  probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
>  		const char *define_prefix, __u32 ifindex)
> @@ -488,11 +564,19 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
>  	bool res;
>  
>  	if (ifindex) {
> -		p_info("BPF offload feature probing is not supported");
> -		return;
> +		switch (prog_type) {
> +		case BPF_PROG_TYPE_SCHED_CLS:
> +		case BPF_PROG_TYPE_XDP:
> +			break;
> +		default:
> +			return;
> +		}
> +
> +		res = probe_prog_type_ifindex(prog_type, ifindex);
> +	} else {
> +		res = libbpf_probe_bpf_prog_type(prog_type, NULL);
>  	}
>  
> -	res = libbpf_probe_bpf_prog_type(prog_type, NULL);
>  #ifdef USE_LIBCAP
>  	/* Probe may succeed even if program load fails, for unprivileged users
>  	 * check that we did not fail because of insufficient permissions
> @@ -521,6 +605,35 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
>  			   define_prefix);
>  }
>  
> +static bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex)
> +{
> +	LIBBPF_OPTS(bpf_map_create_opts, opts);
> +	int key_size, value_size, max_entries;
> +	int fd;
> +
> +	opts.map_ifindex = ifindex;
> +
> +	key_size	= sizeof(__u32);
> +	value_size	= sizeof(__u32);
> +	max_entries	= 1;
> +
> +	switch (map_type) {
> +	case BPF_MAP_TYPE_HASH:
> +	case BPF_MAP_TYPE_ARRAY:
> +		break;
> +	default:
> +		return false;
> +	}

This switch/case should probably not be here. Either skip
probe_map_type_ifindex() entirely if you assume other map types are not
supported, or just probe all types for real.

Speaking of which, we agreed yesterday on probing for all map types. But
I think my suggestion was wrong (apologies!), because the simplified
probes that you're adding back does not contain the necessary
adjustments to work with all map types (the switch(map_type) in libbpf's
probe_map_create()). So we should probably probe just hash/array maps,
but move the switch/case above to probe_map_type(), like we do for prog
types, to avoid printing any output for other map types. This will
prevent other drivers to probe for additional map types, but a large
portion of those probes would be broken, so it's for the best. We can
always extend probe_map_type_ifindex() in the future to support more
types if necessary.

> +
> +	fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries,
> +			    &opts);
> +
> +	if (fd >= 0)
> +		close(fd);
> +
> +	return fd >= 0;
> +}
> +
>  static void
>  probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
>  	       __u32 ifindex)
Niklas Söderlund March 9, 2022, 3:31 p.m. UTC | #2
Hi Quentin,

Thanks for your review.

On 2022-03-09 14:31:18 +0000, Quentin Monnet wrote:
> 2022-03-09 09:54 UTC+0100 ~ Niklas Söderlund <niklas.soderlund@corigine.com>
> > Commit 1a56c18e6c2e4e74 ("bpftool: Stop supporting BPF offload-enabled
> > feature probing") removed the support to probe for BPF offload features.
> > This is still something that is useful for NFP NIC that can support
> > offloading of BPF programs.
> > 
> > The reason for the dropped support was that libbpf starting with v1.0
> > would drop support for passing the ifindex to the BPF prog/map/helper
> > feature probing APIs. In order to keep this useful feature for NFP
> > restore the functionality by moving it directly into bpftool.
> > 
> > The code restored is a simplified version of the code that existed in
> > libbpf which supposed passing the ifindex. The simplification is that it
> > only targets the cases where ifindex is given and call into libbpf for
> > the cases where it's not.
> 
> [...]
> 
> > +static bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex)
> > +{
> > +	struct bpf_insn insns[2] = {
> > +		BPF_MOV64_IMM(BPF_REG_0, 0),
> > +		BPF_EXIT_INSN()
> > +	};
> > +
> > +	switch (prog_type) {
> > +	case BPF_PROG_TYPE_SCHED_CLS:
> > +		/* nfp returns -EINVAL on exit(0) with TC offload */
> > +		insns[0].imm = 2;
> > +		break;
> > +	case BPF_PROG_TYPE_XDP:
> > +		break;
> > +	default:
> > +		return false;
> > +	}
> 
> Looking again at this block, you can remove this switch/case completely.
> Moving 0 by default into R0 was best in generic libbpf's probes because
> there were a number of types that wouldn't accept 2 as a return value;
> but here you can just return 2 all the time, for XDP this will mean
> XDP_PASS and the nfp accepts it. (Do keep the comment on nfp returning
> -EINVAL on exit(0), though, please.)

This make sens and I will do so in a v3. I appreciate your expertise in 
this area, it allows this patch to do more then just restore the code 
from libbpf with as few modifications as possible out of fear of breaking 
things.

> 
> > +
> > +	return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns),
> > +				       NULL, 0, ifindex);
> > +}
> > +
> >  static void
> >  probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
> >  		const char *define_prefix, __u32 ifindex)
> > @@ -488,11 +564,19 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
> >  	bool res;
> >  
> >  	if (ifindex) {
> > -		p_info("BPF offload feature probing is not supported");
> > -		return;
> > +		switch (prog_type) {
> > +		case BPF_PROG_TYPE_SCHED_CLS:
> > +		case BPF_PROG_TYPE_XDP:
> > +			break;
> > +		default:
> > +			return;
> > +		}
> > +
> > +		res = probe_prog_type_ifindex(prog_type, ifindex);
> > +	} else {
> > +		res = libbpf_probe_bpf_prog_type(prog_type, NULL);
> >  	}
> >  
> > -	res = libbpf_probe_bpf_prog_type(prog_type, NULL);
> >  #ifdef USE_LIBCAP
> >  	/* Probe may succeed even if program load fails, for unprivileged users
> >  	 * check that we did not fail because of insufficient permissions
> > @@ -521,6 +605,35 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
> >  			   define_prefix);
> >  }
> >  
> > +static bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex)
> > +{
> > +	LIBBPF_OPTS(bpf_map_create_opts, opts);
> > +	int key_size, value_size, max_entries;
> > +	int fd;
> > +
> > +	opts.map_ifindex = ifindex;
> > +
> > +	key_size	= sizeof(__u32);
> > +	value_size	= sizeof(__u32);
> > +	max_entries	= 1;
> > +
> > +	switch (map_type) {
> > +	case BPF_MAP_TYPE_HASH:
> > +	case BPF_MAP_TYPE_ARRAY:
> > +		break;
> > +	default:
> > +		return false;
> > +	}
> 
> This switch/case should probably not be here. Either skip
> probe_map_type_ifindex() entirely if you assume other map types are not
> supported, or just probe all types for real.
> 
> Speaking of which, we agreed yesterday on probing for all map types. But
> I think my suggestion was wrong (apologies!), because the simplified
> probes that you're adding back does not contain the necessary
> adjustments to work with all map types (the switch(map_type) in libbpf's
> probe_map_create()). So we should probably probe just hash/array maps,
> but move the switch/case above to probe_map_type(), like we do for prog
> types, to avoid printing any output for other map types. This will
> prevent other drivers to probe for additional map types, but a large
> portion of those probes would be broken, so it's for the best. We can
> always extend probe_map_type_ifindex() in the future to support more
> types if necessary.

Make sens, will do so in v3.

> 
> > +
> > +	fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries,
> > +			    &opts);
> > +
> > +	if (fd >= 0)
> > +		close(fd);
> > +
> > +	return fd >= 0;
> > +}
> > +
> >  static void
> >  probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
> >  	       __u32 ifindex)
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 9c894b1447de8cf0..85696339e2500dbe 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -3,6 +3,7 @@ 
 
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <string.h>
 #include <unistd.h>
 #include <net/if.h>
@@ -45,6 +46,11 @@  static bool run_as_unprivileged;
 
 /* Miscellaneous utility functions */
 
+static bool grep(const char *buffer, const char *pattern)
+{
+	return !!strstr(buffer, pattern);
+}
+
 static bool check_procfs(void)
 {
 	struct statfs st_fs;
@@ -135,6 +141,32 @@  static void print_end_section(void)
 
 /* Probing functions */
 
+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 | O_CLOEXEC);
+	if (fd < 0)
+		return -1;
+
+	len = read(fd, buf, sizeof(buf));
+	close(fd);
+	if (len < 0)
+		return -1;
+	if (len >= (ssize_t)sizeof(buf))
+		return -1;
+	buf[len] = '\0';
+
+	return strtol(buf, NULL, 0);
+}
+
 static int read_procfs(const char *path)
 {
 	char *endptr, *line = NULL;
@@ -478,6 +510,50 @@  static bool probe_bpf_syscall(const char *define_prefix)
 	return res;
 }
 
+static bool
+probe_prog_load_ifindex(enum bpf_prog_type prog_type,
+			const struct bpf_insn *insns, size_t insns_cnt,
+			char *log_buf, size_t log_buf_sz,
+			__u32 ifindex)
+{
+	LIBBPF_OPTS(bpf_prog_load_opts, opts,
+		    .log_buf = log_buf,
+		    .log_size = log_buf_sz,
+		    .log_level = log_buf ? 1 : 0,
+		    .prog_ifindex = ifindex,
+		   );
+	int fd;
+
+	errno = 0;
+	fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);
+	if (fd >= 0)
+		close(fd);
+
+	return fd >= 0 && errno != EINVAL && errno != EOPNOTSUPP;
+}
+
+static bool probe_prog_type_ifindex(enum bpf_prog_type prog_type, __u32 ifindex)
+{
+	struct bpf_insn insns[2] = {
+		BPF_MOV64_IMM(BPF_REG_0, 0),
+		BPF_EXIT_INSN()
+	};
+
+	switch (prog_type) {
+	case BPF_PROG_TYPE_SCHED_CLS:
+		/* nfp returns -EINVAL on exit(0) with TC offload */
+		insns[0].imm = 2;
+		break;
+	case BPF_PROG_TYPE_XDP:
+		break;
+	default:
+		return false;
+	}
+
+	return probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns),
+				       NULL, 0, ifindex);
+}
+
 static void
 probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
 		const char *define_prefix, __u32 ifindex)
@@ -488,11 +564,19 @@  probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
 	bool res;
 
 	if (ifindex) {
-		p_info("BPF offload feature probing is not supported");
-		return;
+		switch (prog_type) {
+		case BPF_PROG_TYPE_SCHED_CLS:
+		case BPF_PROG_TYPE_XDP:
+			break;
+		default:
+			return;
+		}
+
+		res = probe_prog_type_ifindex(prog_type, ifindex);
+	} else {
+		res = libbpf_probe_bpf_prog_type(prog_type, NULL);
 	}
 
-	res = libbpf_probe_bpf_prog_type(prog_type, NULL);
 #ifdef USE_LIBCAP
 	/* Probe may succeed even if program load fails, for unprivileged users
 	 * check that we did not fail because of insufficient permissions
@@ -521,6 +605,35 @@  probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
 			   define_prefix);
 }
 
+static bool probe_map_type_ifindex(enum bpf_map_type map_type, __u32 ifindex)
+{
+	LIBBPF_OPTS(bpf_map_create_opts, opts);
+	int key_size, value_size, max_entries;
+	int fd;
+
+	opts.map_ifindex = ifindex;
+
+	key_size	= sizeof(__u32);
+	value_size	= sizeof(__u32);
+	max_entries	= 1;
+
+	switch (map_type) {
+	case BPF_MAP_TYPE_HASH:
+	case BPF_MAP_TYPE_ARRAY:
+		break;
+	default:
+		return false;
+	}
+
+	fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries,
+			    &opts);
+
+	if (fd >= 0)
+		close(fd);
+
+	return fd >= 0;
+}
+
 static void
 probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
 	       __u32 ifindex)
@@ -530,12 +643,10 @@  probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
 	size_t maxlen;
 	bool res;
 
-	if (ifindex) {
-		p_info("BPF offload feature probing is not supported");
-		return;
-	}
-
-	res = libbpf_probe_bpf_map_type(map_type, NULL);
+	if (ifindex)
+		res = probe_map_type_ifindex(map_type, ifindex);
+	else
+		res = libbpf_probe_bpf_map_type(map_type, NULL);
 
 	/* Probe result depends on the success of map creation, no additional
 	 * check required for unprivileged users
@@ -559,6 +670,33 @@  probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
 			   define_prefix);
 }
 
+static bool
+probe_helper_ifindex(enum bpf_func_id id, enum bpf_prog_type prog_type,
+		     __u32 ifindex)
+{
+	struct bpf_insn insns[2] = {
+		BPF_EMIT_CALL(id),
+		BPF_EXIT_INSN()
+	};
+	char buf[4096] = {};
+	bool res;
+
+	probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), buf,
+				sizeof(buf), ifindex);
+	res = !grep(buf, "invalid func ") && !grep(buf, "unknown func ");
+
+	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;
+}
+
 static void
 probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
 			  const char *define_prefix, unsigned int id,
@@ -567,12 +705,10 @@  probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
 	bool res = false;
 
 	if (supported_type) {
-		if (ifindex) {
-			p_info("BPF offload feature probing is not supported");
-			return;
-		}
-
-		res = libbpf_probe_bpf_helper(prog_type, id, NULL);
+		if (ifindex)
+			res = probe_helper_ifindex(id, prog_type, ifindex);
+		else
+			res = libbpf_probe_bpf_helper(prog_type, id, NULL);
 #ifdef USE_LIBCAP
 		/* Probe may succeed even if program load fails, for
 		 * unprivileged users check that we did not fail because of