From patchwork Thu Dec 13 12:19:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Quentin Monnet X-Patchwork-Id: 1012831 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=netronome.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=netronome-com.20150623.gappssmtp.com header.i=@netronome-com.20150623.gappssmtp.com header.b="QgO2Yf9T"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43Ft743vFrz9s5c for ; Thu, 13 Dec 2018 23:20:32 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729186AbeLMMUa (ORCPT ); Thu, 13 Dec 2018 07:20:30 -0500 Received: from mail-wr1-f68.google.com ([209.85.221.68]:36138 "EHLO mail-wr1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729013AbeLMMUP (ORCPT ); Thu, 13 Dec 2018 07:20:15 -0500 Received: by mail-wr1-f68.google.com with SMTP id u4so811349wrp.3 for ; Thu, 13 Dec 2018 04:20:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=netronome-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=8f8jlmP8cUXG+y5g8nlyWC+YvPsJ69ADfFua5jGABtc=; b=QgO2Yf9Tx5lFzGK8aSYSrRt6vQvHlT7gVSW9bS01C5mqPBzz4PFurSQBJG0hwzZm6F mFzzJp5rxXjYMf1AfDQLYY73t74pw6K0WauEA+BXHgD1aU552XyY/aSEGg+o/UxRG9Z2 +6nLEUYW7hng+D6EK9wPqzoVgdpkpTTZeEPO2Cl8ueM+pS2qNZ7ZxFJvTFAm5LTeEG+p mnTegrkYIAbp/1OjVm1BX0JiDaT8UPjL938igg7ZhOmrVFaIeX+uSDv0tHCl+eVreRAc ofGltzKQVy8uD0ALKQsaTPKnMPeWImdYoMGrHH1erjHVpPmI2E19+QXq0hGz8AggArus OIMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=8f8jlmP8cUXG+y5g8nlyWC+YvPsJ69ADfFua5jGABtc=; b=VPzdquVEF3QNEq8Hxf1p4TPjfNcLpmE+u3mulCv/T6iT2agMuqdKeBFG6qswal9Gzo O6mycRWaORsWcJZEkbasQGEh9ig1yreRUmhUK11mP4Euq9tT30+a0fceKBoqnX43nGHj mDEwkP08dv3dnTXHieqNw/pa+Q5g4VIptRGiNdIWZbrzywtBQ7ciL5DC2ePqvBzfrkHe u4Ee8GfZAYuHdaD9wfhEZxUC/IceMc5VAwSXGpsbLd8kSNGsUCpwUTqUmPe9EDiN7deG SK/molkBCBdRWbpV/CM+VqvRJyK8f3hsxxNsQn5qv0PNDxrw7VRH/aww5z7/pqDc/or9 x8rg== X-Gm-Message-State: AA+aEWbxd6yLp0zOitnJsvcfUuFlfo6QHIqDRcNcHaKThlKNZSmYqqf9 bbYn1LH+YX/oXkB0zAiDKafecNX2zls= X-Google-Smtp-Source: AFSGD/W59s64N9AfU8007sAMV6klhFy3hWwK4ZQ+umBO9/YNW03fj2I1jFBkCW5LtPgk7/dzevX6fQ== X-Received: by 2002:adf:c108:: with SMTP id r8mr21862503wre.233.1544703613063; Thu, 13 Dec 2018 04:20:13 -0800 (PST) Received: from cbtest32.netronome.com ([217.38.71.146]) by smtp.gmail.com with ESMTPSA id t70sm2410070wmd.36.2018.12.13.04.20.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Dec 2018 04:20:11 -0800 (PST) From: Quentin Monnet To: Alexei Starovoitov , Daniel Borkmann Cc: netdev@vger.kernel.org, oss-drivers@netronome.com, Quentin Monnet , Arnaldo Carvalho de Melo , Jesper Dangaard Brouer , Stanislav Fomichev Subject: [PATCH bpf-next 7/8] tools: bpftool: add probes for a network device Date: Thu, 13 Dec 2018 12:19:21 +0000 Message-Id: <20181213121922.6652-8-quentin.monnet@netronome.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181213121922.6652-1-quentin.monnet@netronome.com> References: <20181213121922.6652-1-quentin.monnet@netronome.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 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 " 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. Among the program types, only the ones that can be offloaded are probed. Among the helpers, only the ones that can work with the latter program types 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. 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). Signed-off-by: Quentin Monnet Reviewed-by: Jakub Kicinski --- .../bpftool/Documentation/bpftool-feature.rst | 18 +++- tools/bpf/bpftool/common.c | 2 +- tools/bpf/bpftool/feature.c | 97 ++++++++++++++++--- tools/bpf/bpftool/main.h | 1 + 4 files changed, 103 insertions(+), 15 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst index 083d30510cce..9f366461fb72 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst @@ -19,16 +19,21 @@ 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. - Keyword **kernel** can be omitted. + Keyword **kernel** can be omitted. If no probe target is + specified, probing the kernel is the default behaviour. If the **macros** keyword (but not the **-j** option) is passed, output is dumped as a list of **#define** macros that @@ -43,6 +48,13 @@ DESCRIPTION **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 99e4027dde75..9cd5b24e4778 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -521,7 +521,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 77221fff6ba9..0283faacf1dc 100644 --- a/tools/bpf/bpftool/feature.c +++ b/tools/bpf/bpftool/feature.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ enum probe_component { COMPONENT_UNSPEC, COMPONENT_KERNEL, + COMPONENT_DEVICE, }; #define MAX_HELPER_NAME_LEN 32 @@ -616,7 +618,8 @@ static bool probe_bpf_syscall(const char *define_prefix) 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) + size_t insns_cnt, int kernel_version, char *buf, size_t buf_len, + __u32 ifindex) { struct bpf_load_program_attr xattr = {}; int fd; @@ -634,6 +637,7 @@ prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *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) @@ -642,7 +646,7 @@ prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, 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 buf[4096], feat_name[128], define_name[128], plain_desc[128]; const char *plain_comment = "eBPF program_type "; @@ -653,9 +657,22 @@ probe_prog_type(enum bpf_prog_type prog_type, int kernel_version, size_t maxlen; bool res; + if (ifindex) + /* Only test offload-able program types */ + switch (prog_type) { + case BPF_PROG_TYPE_SCHED_CLS: + /* nfp returns -EINVAL on exit(0) with TC offload */ + insns[0].imm = 2; + /* fall through */ + case BPF_PROG_TYPE_XDP: + break; + default: + return; + } + errno = 0; prog_load(prog_type, insns, ARRAY_SIZE(insns), kernel_version, - buf, sizeof(buf)); + buf, sizeof(buf), ifindex); res = (errno != EINVAL && errno != EOPNOTSUPP); supported_types[prog_type] |= res; @@ -675,7 +692,8 @@ 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], define_name[128], plain_desc[128]; int key_size, value_size, max_entries, map_flags; @@ -716,6 +734,12 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix) switch (map_type) { case BPF_MAP_TYPE_ARRAY_OF_MAPS: case BPF_MAP_TYPE_HASH_OF_MAPS: + /* TODO: probe for device, once libbpf has an API to create + * map-in-map for offload + */ + if (ifindex) + break; + fd_inner = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(__u32), sizeof(__u32), 1, 0); if (fd_inner < 0) @@ -731,6 +755,7 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix) 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); break; @@ -757,7 +782,7 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix) static void probe_helper(__u32 id, enum bpf_prog_type prog_type, const char *name, int kernel_version, bool *supported_types, - const char *define_prefix) + const char *define_prefix, __u32 ifindex, int vendor_id) { char buf[4096], feat_name[128], define_name[128], plain_desc[128]; struct bpf_insn insns[2] = { @@ -766,15 +791,35 @@ probe_helper(__u32 id, enum bpf_prog_type prog_type, const char *name, }; bool res = false; + if (ifindex) + /* Only test helpers compatible with offload-able prog types */ + switch (prog_type) { + case BPF_PROG_TYPE_XDP: + case BPF_PROG_TYPE_SCHED_CLS: + break; + default: + return; + } + if (!supported_types[prog_type]) goto do_print; /* Reset buffer in case no debug info was written at previous probe */ *buf = '\0'; prog_load(prog_type, insns, ARRAY_SIZE(insns), kernel_version, - buf, sizeof(buf)); + buf, sizeof(buf), ifindex); res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); + if (ifindex) + switch (vendor_id) { + case 0x19ee: /* Netronome specific */ + res = res && !grep(buf, "not supported by FW") && + !grep(buf, "unsupported function id"); + break; + default: + break; + } + do_print: sprintf(feat_name, "have_%s_helper", name); sprintf(define_name, "%s_helper", name); @@ -790,7 +835,10 @@ static int do_probe(int argc, char **argv) const char *define_prefix = NULL; bool supported_types[128] = {}; int kernel_version; + __u32 ifindex = 0; + int vendor_id = 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. @@ -810,6 +858,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(); @@ -828,7 +894,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; } @@ -858,6 +924,8 @@ static int do_probe(int argc, char **argv) else printf("\n"); break; + default: + break; } print_start_section("syscall_config", @@ -865,6 +933,7 @@ static int do_probe(int argc, char **argv) "Scanning system call and kernel version...", 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 */ @@ -878,7 +947,7 @@ static int do_probe(int argc, char **argv) for (i = BPF_PROG_TYPE_SOCKET_FILTER; 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", "/*** eBPF map types ***/", @@ -886,17 +955,21 @@ static int do_probe(int argc, char **argv) define_prefix); for (i = BPF_MAP_TYPE_HASH; 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", "/*** eBPF helper functions ***/", "Scanning eBPF helper functions...", define_prefix); + if (ifindex) + vendor_id = read_sysfs_netdev_hex_int(ifname, "vendor"); + for (i = 1; i < ARRAY_SIZE(helper_progtype_and_name); i++) probe_helper(i, helper_progtype_and_name[i].progtype, helper_progtype_and_name[i].name, - kernel_version, supported_types, define_prefix); + kernel_version, supported_types, define_prefix, + ifindex, vendor_id); exit_close_json: if (json_output) { @@ -917,8 +990,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 d9a2b1eafae7..f8518c7ec2cc 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -181,6 +181,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);