From patchwork Fri Aug 28 19:35:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 261791 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F3388C433E2 for ; Fri, 28 Aug 2020 19:36:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CEBD52075B for ; Fri, 28 Aug 2020 19:36:35 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=google.com header.i=@google.com header.b="pY+Ggws5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726386AbgH1Tg1 (ORCPT ); Fri, 28 Aug 2020 15:36:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47204 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726236AbgH1TgJ (ORCPT ); Fri, 28 Aug 2020 15:36:09 -0400 Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DCD35C061233 for ; Fri, 28 Aug 2020 12:36:08 -0700 (PDT) Received: by mail-qk1-x749.google.com with SMTP id e63so81275qkd.14 for ; Fri, 28 Aug 2020 12:36:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=YZ5LeBPQpyyJvO3Gz2DvdMpJFZa5a8BnHcIIYG0Iagw=; b=pY+Ggws5AtyVpGHz0EVJPQp2z9kcNcC/JTDgCH85LK0dpIfdHq+5UX0kgIDo4TBBmT KinRhgEdXFzq3C/OwXz70G/gctXv6nlyK0HCFkKj/RLNhrUH5h7rMHlY6IF0txRvHwti hpyN4s7d9eSgEBzquBDiwQTS4dw71miSo1z4aHNJwMC9WGFZZ2xoSrcGEsMO45H9MbFC tX25lba408V799PVIGDuF9jKxWdNDoSCFzNPp74IlRkpVRz79+h8yNI0Oax+bj8ZrhPO BhyU2ACeT+CJVUUD69cU+e5T8RwRguIyOv5sMISnBwnyEBUQq4X4DePOI8KkHZ+rI2LI ZKnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=YZ5LeBPQpyyJvO3Gz2DvdMpJFZa5a8BnHcIIYG0Iagw=; b=ZgxUr5RuDq0OV8AesrBXVrv1xLzBqUwC+6CzCcMVUw4h2dQV+i9rcrjvc38FK4Xn0B 8fuqtqcogpBhlwxJ/IvvlkNDqjmQnfBirlK4thozq70fw8ed99oEEvwsr8m/aifbC0QZ cLiF8ryIWlL5GGhhltgIpNs/rU30FvGNgv/8lC3E6RVBtUJYf7whEBTni0MZ5DoNfzIl OLJx/DoLVKCPCsvHKP3HveNqXWPGShdJnfmobl+rCZGU4o6PD5DXbFASP3Ej2374+QCS iX/LoIjnWTs/NEJNe9vUOd4GhOS1cWEFfHlCq154uD1Ut6FE1AFH3TqdiONqEGaiJsiQ y2xw== X-Gm-Message-State: AOAM531mjZ8CeY2zuFsNmc0HgwKB80wfGIlskwZJPQBKND5WzXjPPVXx aeEj7ZM/LlJEi4tKEwsiI2zQxAIZSuTLN96yPtmv9Cn42w7wjbWMJHXmwthv6drtuRUDZh6z2Aj mfpekVt+eG+DkX22dFFlp6/KdOrg2IcOFG1vayZU9AX4OHMke5T6jog== X-Google-Smtp-Source: ABdhPJzGzVA7YrBKO8m6gcU3S2aTUjqbGTq3JUVU7hvPSIeIfkkU9HDAW9qeAtG3ICXvrbZZNeaT7rU= X-Received: from sdf2.svl.corp.google.com ([2620:15c:2c4:1:7220:84ff:fe09:7732]) (user=sdf job=sendgmr) by 2002:a0c:b61c:: with SMTP id f28mr128057qve.92.1598643367280; Fri, 28 Aug 2020 12:36:07 -0700 (PDT) Date: Fri, 28 Aug 2020 12:35:56 -0700 In-Reply-To: <20200828193603.335512-1-sdf@google.com> Message-Id: <20200828193603.335512-2-sdf@google.com> Mime-Version: 1.0 References: <20200828193603.335512-1-sdf@google.com> X-Mailer: git-send-email 2.28.0.402.g5ffc5be6b7-goog Subject: [PATCH bpf-next v3 1/8] bpf: Mutex protect used_maps array and count From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, YiFei Zhu , YiFei Zhu , Stanislav Fomichev Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: YiFei Zhu To support modifying the used_maps array, we use a mutex to protect the use of the counter and the array. The mutex is initialized right after the prog aux is allocated, and destroyed right before prog aux is freed. This way we guarantee it's initialized for both cBPF and eBPF. Cc: YiFei Zhu Signed-off-by: YiFei Zhu Signed-off-by: Stanislav Fomichev --- .../net/ethernet/netronome/nfp/bpf/offload.c | 18 ++++++++++++------ include/linux/bpf.h | 1 + kernel/bpf/core.c | 15 +++++++++++---- kernel/bpf/syscall.c | 16 ++++++++++++---- net/core/dev.c | 11 ++++++++--- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index ac02369174a9..53851853562c 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -111,7 +111,9 @@ static int nfp_map_ptrs_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog, struct bpf_prog *prog) { - int i, cnt, err; + int i, cnt, err = 0; + + mutex_lock(&prog->aux->used_maps_mutex); /* Quickly count the maps we will have to remember */ cnt = 0; @@ -119,13 +121,15 @@ nfp_map_ptrs_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog, if (bpf_map_offload_neutral(prog->aux->used_maps[i])) cnt++; if (!cnt) - return 0; + goto out; nfp_prog->map_records = kmalloc_array(cnt, sizeof(nfp_prog->map_records[0]), GFP_KERNEL); - if (!nfp_prog->map_records) - return -ENOMEM; + if (!nfp_prog->map_records) { + err = -ENOMEM; + goto out; + } for (i = 0; i < prog->aux->used_map_cnt; i++) if (bpf_map_offload_neutral(prog->aux->used_maps[i])) { @@ -133,12 +137,14 @@ nfp_map_ptrs_record(struct nfp_app_bpf *bpf, struct nfp_prog *nfp_prog, prog->aux->used_maps[i]); if (err) { nfp_map_ptrs_forget(bpf, nfp_prog); - return err; + goto out; } } WARN_ON(cnt != nfp_prog->map_records_cnt); - return 0; +out: + mutex_unlock(&prog->aux->used_maps_mutex); + return err; } static int diff --git a/include/linux/bpf.h b/include/linux/bpf.h index dbba82a80087..1b404b034775 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -748,6 +748,7 @@ struct bpf_prog_aux { struct bpf_ksym ksym; const struct bpf_prog_ops *ops; struct bpf_map **used_maps; + struct mutex used_maps_mutex; /* mutex for used_maps and used_map_cnt */ struct bpf_prog *prog; struct user_struct *user; u64 load_time; /* ns since boottime */ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index ed0b3578867c..2a20c2833996 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -98,6 +98,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag fp->jit_requested = ebpf_jit_enabled(); INIT_LIST_HEAD_RCU(&fp->aux->ksym.lnode); + mutex_init(&fp->aux->used_maps_mutex); return fp; } @@ -253,6 +254,7 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size, void __bpf_prog_free(struct bpf_prog *fp) { if (fp->aux) { + mutex_destroy(&fp->aux->used_maps_mutex); free_percpu(fp->aux->stats); kfree(fp->aux->poke_tab); kfree(fp->aux); @@ -1747,8 +1749,9 @@ bool bpf_prog_array_compatible(struct bpf_array *array, static int bpf_check_tail_call(const struct bpf_prog *fp) { struct bpf_prog_aux *aux = fp->aux; - int i; + int i, ret = 0; + mutex_lock(&aux->used_maps_mutex); for (i = 0; i < aux->used_map_cnt; i++) { struct bpf_map *map = aux->used_maps[i]; struct bpf_array *array; @@ -1757,11 +1760,15 @@ static int bpf_check_tail_call(const struct bpf_prog *fp) continue; array = container_of(map, struct bpf_array, map); - if (!bpf_prog_array_compatible(array, fp)) - return -EINVAL; + if (!bpf_prog_array_compatible(array, fp)) { + ret = -EINVAL; + goto out; + } } - return 0; +out: + mutex_unlock(&aux->used_maps_mutex); + return ret; } static void bpf_prog_select_func(struct bpf_prog *fp) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b86b1155b748..c9b8a97fbbdf 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3155,21 +3155,25 @@ static const struct bpf_map *bpf_map_from_imm(const struct bpf_prog *prog, const struct bpf_map *map; int i; + mutex_lock(&prog->aux->used_maps_mutex); for (i = 0, *off = 0; i < prog->aux->used_map_cnt; i++) { map = prog->aux->used_maps[i]; if (map == (void *)addr) { *type = BPF_PSEUDO_MAP_FD; - return map; + goto out; } if (!map->ops->map_direct_value_meta) continue; if (!map->ops->map_direct_value_meta(map, addr, off)) { *type = BPF_PSEUDO_MAP_VALUE; - return map; + goto out; } } + map = NULL; - return NULL; +out: + mutex_unlock(&prog->aux->used_maps_mutex); + return map; } static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog, @@ -3287,6 +3291,7 @@ static int bpf_prog_get_info_by_fd(struct file *file, memcpy(info.tag, prog->tag, sizeof(prog->tag)); memcpy(info.name, prog->aux->name, sizeof(prog->aux->name)); + mutex_lock(&prog->aux->used_maps_mutex); ulen = info.nr_map_ids; info.nr_map_ids = prog->aux->used_map_cnt; ulen = min_t(u32, info.nr_map_ids, ulen); @@ -3296,9 +3301,12 @@ static int bpf_prog_get_info_by_fd(struct file *file, for (i = 0; i < ulen; i++) if (put_user(prog->aux->used_maps[i]->id, - &user_map_ids[i])) + &user_map_ids[i])) { + mutex_unlock(&prog->aux->used_maps_mutex); return -EFAULT; + } } + mutex_unlock(&prog->aux->used_maps_mutex); err = set_info_rec_size(&info); if (err) diff --git a/net/core/dev.c b/net/core/dev.c index b5d1129d8310..6957b31127d9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5441,15 +5441,20 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) if (new) { u32 i; + mutex_lock(&new->aux->used_maps_mutex); + /* generic XDP does not work with DEVMAPs that can * have a bpf_prog installed on an entry */ for (i = 0; i < new->aux->used_map_cnt; i++) { - if (dev_map_can_have_prog(new->aux->used_maps[i])) - return -EINVAL; - if (cpu_map_prog_allowed(new->aux->used_maps[i])) + if (dev_map_can_have_prog(new->aux->used_maps[i]) || + cpu_map_prog_allowed(new->aux->used_maps[i])) { + mutex_unlock(&new->aux->used_maps_mutex); return -EINVAL; + } } + + mutex_unlock(&new->aux->used_maps_mutex); } switch (xdp->command) { From patchwork Fri Aug 28 19:35:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 261790 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, UNWANTED_LANGUAGE_BODY,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F0BEDC433E2 for ; Fri, 28 Aug 2020 19:36:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C82702075B for ; Fri, 28 Aug 2020 19:36:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=google.com header.i=@google.com header.b="vhj11wnW" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726798AbgH1Tgi (ORCPT ); Fri, 28 Aug 2020 15:36:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47220 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725969AbgH1TgM (ORCPT ); Fri, 28 Aug 2020 15:36:12 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 094EAC061235 for ; Fri, 28 Aug 2020 12:36:12 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 83so327595ybf.2 for ; Fri, 28 Aug 2020 12:36:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=tDZ6kf3qnF108YJ8FqrzfUCa129eRJXC+UYCuWDM+2Y=; b=vhj11wnWyqvvn0AUOrDbZAlHJIETQY1drh4H0GCsZgL9K+0Vvbr9VNRJDQV83xkfkZ vwdZIdgt3WIg40J2LV98WyWwOUosSBCw/lmrgbI1gkk5kg7BE+WR2U3k6kl7i1w2xDxf nvKBC+o5VKL5oL/oWOsTWlc64ocMAPAJlczMcylvgCDvaUl8TcL4ghUZHqiQMI7yLj+Z KYZue7zdmzCNszWeHFok8h2K7kD/CzI4js0offvjYAB6/QrbhUxUzWDvPZ4Niey/HLzq eqqUfy8w+y2itze5q7fcoMX4WepeUQ1xyw0XCIPIaE0vDhhWFTcylFfo0X6HLBeDmzke GYSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=tDZ6kf3qnF108YJ8FqrzfUCa129eRJXC+UYCuWDM+2Y=; b=D9tOMxAnQaMdlGGtl4KeJFyG6CGknzoPa4ktKezRvStdezQjCARYoEqlK87qXUuVdV DBLnmExFGHNHOAwIDUgomyXzaK66o6vALmOgLt4pC1hAj5LMxzIxKpFzT20pKLdyb3B6 tnIyBZOfvdWCRHqhbEgHFOVrSqJuoiCj+c0ixeYedLpYg0/8n3Q2hB5EuTqhXovptiOy Ar+S+c7fKdr4YnQWSOd+343srgsEGK4IUisgrg2QOo2rKoZU8JDSgpGbtkxtChsGaLYh Si54ApGYUqR/kLZr7vZbHucwpRqlfLa5wPpBgNup6pU5eu5vnrJf2SnQPPgqpBHvaUIE l+Kg== X-Gm-Message-State: AOAM5332uQylGXHgR6qiSCXVI4WARUrVUfLiHgjHLAlnDplgvwNuQ5dm 9Ae9MhS1Xe1M8X7CWilhd724QeO9cL+ZWKi81uDj9Go4p8WwFgJUmDut7uSfFrRsFtaei6IVrI7 3vfc4zIjXOiwYW+eOQ2MNdJl3FyZdujQpdLHWXQv+ImvlbVe4O0Q1cQ== X-Google-Smtp-Source: ABdhPJxVz2sA93H+xe6AmLuUpKL4UB6BJzQm75pqqMLQzJpVvHNMcBwFwm+B58NvrZZUEQtRrIf4zpM= X-Received: from sdf2.svl.corp.google.com ([2620:15c:2c4:1:7220:84ff:fe09:7732]) (user=sdf job=sendgmr) by 2002:a25:ab34:: with SMTP id u49mr4553934ybi.516.1598643371155; Fri, 28 Aug 2020 12:36:11 -0700 (PDT) Date: Fri, 28 Aug 2020 12:35:58 -0700 In-Reply-To: <20200828193603.335512-1-sdf@google.com> Message-Id: <20200828193603.335512-4-sdf@google.com> Mime-Version: 1.0 References: <20200828193603.335512-1-sdf@google.com> X-Mailer: git-send-email 2.28.0.402.g5ffc5be6b7-goog Subject: [PATCH bpf-next v3 3/8] libbpf: Add BPF_PROG_BIND_MAP syscall and use it on .metadata section From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, YiFei Zhu , YiFei Zhu , Stanislav Fomichev Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: YiFei Zhu The patch adds a simple wrapper bpf_prog_bind_map around the syscall. And when using libbpf to load a program, it will probe the kernel for the support of this syscall, and scan for the .metadata ELF section and load it as an internal map like a .data section. In the case that kernel supports the BPF_PROG_BIND_MAP syscall and a .metadata section exists, the map will be explicitly bound to the program via the syscall immediately after program is loaded. -EEXIST is ignored for this syscall. Cc: YiFei Zhu Signed-off-by: YiFei Zhu Signed-off-by: Stanislav Fomichev --- tools/lib/bpf/bpf.c | 13 ++++ tools/lib/bpf/bpf.h | 8 +++ tools/lib/bpf/libbpf.c | 130 ++++++++++++++++++++++++++++++++------- tools/lib/bpf/libbpf.map | 1 + 4 files changed, 131 insertions(+), 21 deletions(-) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 82b983ff6569..5f6c5676cc45 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -872,3 +872,16 @@ int bpf_enable_stats(enum bpf_stats_type type) return sys_bpf(BPF_ENABLE_STATS, &attr, sizeof(attr)); } + +int bpf_prog_bind_map(int prog_fd, int map_fd, + const struct bpf_prog_bind_opts *opts) +{ + union bpf_attr attr; + + memset(&attr, 0, sizeof(attr)); + attr.prog_bind_map.prog_fd = prog_fd; + attr.prog_bind_map.map_fd = map_fd; + attr.prog_bind_map.flags = OPTS_GET(opts, flags, 0); + + return sys_bpf(BPF_PROG_BIND_MAP, &attr, sizeof(attr)); +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 015d13f25fcc..8c1ac4b42f90 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -243,6 +243,14 @@ LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, enum bpf_stats_type; /* defined in up-to-date linux/bpf.h */ LIBBPF_API int bpf_enable_stats(enum bpf_stats_type type); +struct bpf_prog_bind_opts { + size_t sz; /* size of this struct for forward/backward compatibility */ + __u32 flags; +}; +#define bpf_prog_bind_opts__last_field flags + +LIBBPF_API int bpf_prog_bind_map(int prog_fd, int map_fd, + const struct bpf_prog_bind_opts *opts); #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 8cdb2528482e..2b21021b66bb 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -176,6 +176,8 @@ enum kern_feature_id { FEAT_EXP_ATTACH_TYPE, /* bpf_probe_read_{kernel,user}[_str] helpers */ FEAT_PROBE_READ_KERN, + /* BPF_PROG_BIND_MAP is supported */ + FEAT_PROG_BIND_MAP, __FEAT_CNT, }; @@ -285,6 +287,7 @@ struct bpf_struct_ops { #define KCONFIG_SEC ".kconfig" #define KSYMS_SEC ".ksyms" #define STRUCT_OPS_SEC ".struct_ops" +#define METADATA_SEC ".metadata" enum libbpf_map_type { LIBBPF_MAP_UNSPEC, @@ -292,6 +295,7 @@ enum libbpf_map_type { LIBBPF_MAP_BSS, LIBBPF_MAP_RODATA, LIBBPF_MAP_KCONFIG, + LIBBPF_MAP_METADATA, }; static const char * const libbpf_type_to_btf_name[] = { @@ -299,6 +303,7 @@ static const char * const libbpf_type_to_btf_name[] = { [LIBBPF_MAP_BSS] = BSS_SEC, [LIBBPF_MAP_RODATA] = RODATA_SEC, [LIBBPF_MAP_KCONFIG] = KCONFIG_SEC, + [LIBBPF_MAP_METADATA] = METADATA_SEC, }; struct bpf_map { @@ -381,6 +386,7 @@ struct bpf_object { struct extern_desc *externs; int nr_extern; int kconfig_map_idx; + int metadata_map_idx; bool loaded; bool has_pseudo_calls; @@ -400,6 +406,7 @@ struct bpf_object { Elf_Data *rodata; Elf_Data *bss; Elf_Data *st_ops_data; + Elf_Data *metadata; size_t shstrndx; /* section index for section name strings */ size_t strtabidx; struct { @@ -416,6 +423,7 @@ struct bpf_object { int rodata_shndx; int bss_shndx; int st_ops_shndx; + int metadata_shndx; } efile; /* * All loaded bpf_object is linked in a list, which is @@ -1027,11 +1035,13 @@ static struct bpf_object *bpf_object__new(const char *path, obj->efile.obj_buf_sz = obj_buf_sz; obj->efile.maps_shndx = -1; obj->efile.btf_maps_shndx = -1; + obj->efile.metadata_shndx = -1; obj->efile.data_shndx = -1; obj->efile.rodata_shndx = -1; obj->efile.bss_shndx = -1; obj->efile.st_ops_shndx = -1; obj->kconfig_map_idx = -1; + obj->metadata_map_idx = -1; obj->kern_version = get_kernel_version(); obj->loaded = false; @@ -1343,7 +1353,8 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, def->key_size = sizeof(int); def->value_size = data_sz; def->max_entries = 1; - def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG + def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG || + type == LIBBPF_MAP_METADATA ? BPF_F_RDONLY_PROG : 0; def->map_flags |= BPF_F_MMAPABLE; @@ -1399,6 +1410,16 @@ static int bpf_object__init_global_data_maps(struct bpf_object *obj) if (err) return err; } + if (obj->efile.metadata_shndx >= 0) { + err = bpf_object__init_internal_map(obj, LIBBPF_MAP_METADATA, + obj->efile.metadata_shndx, + obj->efile.metadata->d_buf, + obj->efile.metadata->d_size); + if (err) + return err; + + obj->metadata_map_idx = obj->nr_maps - 1; + } return 0; } @@ -2790,6 +2811,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj) } else if (strcmp(name, STRUCT_OPS_SEC) == 0) { obj->efile.st_ops_data = data; obj->efile.st_ops_shndx = idx; + } else if (strcmp(name, METADATA_SEC) == 0) { + obj->efile.metadata = data; + obj->efile.metadata_shndx = idx; } else { pr_info("elf: skipping unrecognized data section(%d) %s\n", idx, name); @@ -3201,7 +3225,8 @@ static bool bpf_object__shndx_is_data(const struct bpf_object *obj, { return shndx == obj->efile.data_shndx || shndx == obj->efile.bss_shndx || - shndx == obj->efile.rodata_shndx; + shndx == obj->efile.rodata_shndx || + shndx == obj->efile.metadata_shndx; } static bool bpf_object__shndx_is_maps(const struct bpf_object *obj, @@ -3222,6 +3247,8 @@ bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx) return LIBBPF_MAP_RODATA; else if (shndx == obj->efile.symbols_shndx) return LIBBPF_MAP_KCONFIG; + else if (shndx == obj->efile.metadata_shndx) + return LIBBPF_MAP_METADATA; else return LIBBPF_MAP_UNSPEC; } @@ -3592,18 +3619,13 @@ static int probe_kern_prog_name(void) return probe_fd(ret); } -static int probe_kern_global_data(void) +static void __probe_create_global_data(int *prog, int *map, + struct bpf_insn *insns, size_t insns_cnt) { struct bpf_load_program_attr prg_attr; struct bpf_create_map_attr map_attr; char *cp, errmsg[STRERR_BUFSIZE]; - struct bpf_insn insns[] = { - BPF_LD_MAP_VALUE(BPF_REG_1, 0, 16), - BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42), - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }; - int ret, map; + int err; memset(&map_attr, 0, sizeof(map_attr)); map_attr.map_type = BPF_MAP_TYPE_ARRAY; @@ -3611,26 +3633,40 @@ static int probe_kern_global_data(void) map_attr.value_size = 32; map_attr.max_entries = 1; - map = bpf_create_map_xattr(&map_attr); - if (map < 0) { - ret = -errno; - cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg)); + *map = bpf_create_map_xattr(&map_attr); + if (*map < 0) { + err = errno; + cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); pr_warn("Error in %s():%s(%d). Couldn't create simple array map.\n", - __func__, cp, -ret); - return ret; + __func__, cp, -err); + return; } - insns[0].imm = map; + insns[0].imm = *map; memset(&prg_attr, 0, sizeof(prg_attr)); prg_attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER; prg_attr.insns = insns; - prg_attr.insns_cnt = ARRAY_SIZE(insns); + prg_attr.insns_cnt = insns_cnt; prg_attr.license = "GPL"; - ret = bpf_load_program_xattr(&prg_attr, NULL, 0); + *prog = bpf_load_program_xattr(&prg_attr, NULL, 0); +} + +static int probe_kern_global_data(void) +{ + struct bpf_insn insns[] = { + BPF_LD_MAP_VALUE(BPF_REG_1, 0, 16), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog = -1, map = -1; + + __probe_create_global_data(&prog, &map, insns, ARRAY_SIZE(insns)); + close(map); - return probe_fd(ret); + return probe_fd(prog); } static int probe_kern_btf(void) @@ -3757,6 +3793,32 @@ static int probe_kern_probe_read_kernel(void) return probe_fd(bpf_load_program_xattr(&attr, NULL, 0)); } +static int probe_prog_bind_map(void) +{ + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + int prog = -1, map = -1, ret = 0; + + if (!kernel_supports(FEAT_GLOBAL_DATA)) + return 0; + + __probe_create_global_data(&prog, &map, insns, ARRAY_SIZE(insns)); + + if (map >= 0 && prog < 0) { + close(map); + return 0; + } + + if (!bpf_prog_bind_map(prog, map, NULL)) + ret = 1; + + close(map); + close(prog); + return ret; +} + enum kern_feature_result { FEAT_UNKNOWN = 0, FEAT_SUPPORTED = 1, @@ -3797,6 +3859,9 @@ static struct kern_feature_desc { }, [FEAT_PROBE_READ_KERN] = { "bpf_probe_read_kernel() helper", probe_kern_probe_read_kernel, + }, + [FEAT_PROG_BIND_MAP] = { + "BPF_PROG_BIND_MAP support", probe_prog_bind_map, } }; @@ -3897,7 +3962,8 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) } /* Freeze .rodata and .kconfig map as read-only from syscall side. */ - if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_KCONFIG) { + if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_KCONFIG || + map_type == LIBBPF_MAP_METADATA) { err = bpf_map_freeze(map->fd); if (err) { err = -errno; @@ -6057,6 +6123,28 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt, if (ret >= 0) { if (log_buf && load_attr.log_level) pr_debug("verifier log:\n%s", log_buf); + + if (prog->obj->metadata_map_idx >= 0 && + kernel_supports(FEAT_PROG_BIND_MAP)) { + struct bpf_map *metadata_map = + &prog->obj->maps[prog->obj->metadata_map_idx]; + + /* EEXIST to bpf_prog_bind_map means the map is already + * bound to the program. This can happen if the program + * refers to the map in its code. Since all we are doing + * is to make sure when we iterate the program's maps + * metadata map is always inside, EXIST is okay; we + * ignore this errno + */ + if (bpf_prog_bind_map(ret, bpf_map__fd(metadata_map), NULL) && + errno != EEXIST) { + cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); + pr_warn("prog '%s': failed to bind .metadata map: %s\n", + prog->name, cp); + /* Don't fail hard if can't load metadata. */ + } + } + *pfd = ret; ret = 0; goto out; diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 66a6286d0716..529b99c0c2c3 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -302,6 +302,7 @@ LIBBPF_0.1.0 { LIBBPF_0.2.0 { global: + bpf_prog_bind_map; perf_buffer__buffer_cnt; perf_buffer__buffer_fd; perf_buffer__epoll_fd; From patchwork Fri Aug 28 19:36:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 261789 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3777BC433E2 for ; Fri, 28 Aug 2020 19:36:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 105542087D for ; Fri, 28 Aug 2020 19:36:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=google.com header.i=@google.com header.b="KddCvohZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726851AbgH1Tgv (ORCPT ); Fri, 28 Aug 2020 15:36:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47204 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726594AbgH1TgV (ORCPT ); Fri, 28 Aug 2020 15:36:21 -0400 Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E1B17C06123A for ; Fri, 28 Aug 2020 12:36:15 -0700 (PDT) Received: by mail-qk1-x74a.google.com with SMTP id c67so112478qkd.0 for ; Fri, 28 Aug 2020 12:36:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=zBAe1FVufzoZpYTUTqiD804lLPIX6uCCT1yy+dgE1R0=; b=KddCvohZ56o2AVqqwXHkreAu5TqUzxOPbr24pE24K7DzR01RvFXwmCOlfOs9aDDXJH 1Iep7lvJr0v8uXSQoyNz4oSw77YKZRtY+iK9bUJzo7CUKcy+/Nci2cg8i8I+t5s7w/Gq 55KSTmPErCfB4gC9lo9VIynDwlFgogKCKrGnW1d6yqLNdlsxqArVdMqNsJ3q83fD4IvG zJQyN6QwGw2HppVeLsAAj2Cdz5Oez7V64BdVi+ulD53Bz0oWBfbdx1NuohIFiWVUIZs3 fiQowoObbFpvQVGvSe+m24XyuoLr2ob1ufRmoQimUUS1v7TgfWyEqqbekIBiR6muthy/ Iolw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=zBAe1FVufzoZpYTUTqiD804lLPIX6uCCT1yy+dgE1R0=; b=l2E07p1TaLvXNTjLnKuC3AMZFl8qFi+QUWXoe5gfXrKpdixpIgYTaH3a4JCCVfwg26 wZyAMBgxXjvg5TLcbvIcQu/d7DeG36ioXSxO32G0ZRGalV6zqw7uVvufNMjR42L+jB1f Dxc93ynuJ5gKrl6j0XXMgdR2I19r+QEX8rIiwpfhL7jrX17jO5DBNnEkjCceKsegL0yF 1aP+UgV2QpTNbEoo+lPG7j/WeYq3FhPCB4E0GHuzTOjpGlkz+HhUqjNpzn+bhsOCs6aZ Ux57OmMCbvvrXm/tFece5aUa06c7QTHFvPjozYlYCgm9pEAri12ZSvgBJ9jdBNd3SrDw TIXg== X-Gm-Message-State: AOAM533jhNUUo/l3DZ1+mLFH+rfoIpE98AGaiwjjZg2Vj8Sr9mQ2Na55 WMIqvIGeLBnfR2Lj8HIl7Ash2Oa5CKOlRAzLPCa8F1zukL8Xr2tK56batiBsVPo8KWW0tdblKgQ 7ACY0CyXzV9CFIjNpLQr9WbUY3uLIGo/q6TLg2xY2mMlwIWiG1AMoFw== X-Google-Smtp-Source: ABdhPJyu6SsQjLtQW2oPoP8elya5iZ2x/CAWBmuOtKU9ApWz33BUmLgX2ZiWysjht3J5TmAOfnjQYo0= X-Received: from sdf2.svl.corp.google.com ([2620:15c:2c4:1:7220:84ff:fe09:7732]) (user=sdf job=sendgmr) by 2002:ad4:458e:: with SMTP id x14mr86689qvu.111.1598643374913; Fri, 28 Aug 2020 12:36:14 -0700 (PDT) Date: Fri, 28 Aug 2020 12:36:00 -0700 In-Reply-To: <20200828193603.335512-1-sdf@google.com> Message-Id: <20200828193603.335512-6-sdf@google.com> Mime-Version: 1.0 References: <20200828193603.335512-1-sdf@google.com> X-Mailer: git-send-email 2.28.0.402.g5ffc5be6b7-goog Subject: [PATCH bpf-next v3 5/8] bpftool: support dumping metadata From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, YiFei Zhu , YiFei Zhu , Stanislav Fomichev Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: YiFei Zhu Added a flag "--metadata" to `bpftool prog list` to dump the metadata contents. For some formatting some BTF code is put directly in the metadata dumping. Sanity checks on the map and the kind of the btf_type to make sure we are actually dumping what we are expecting. A helper jsonw_reset is added to json writer so we can reuse the same json writer without having extraneous commas. Sample output: $ bpftool prog --metadata 6: cgroup_skb name prog tag bcf7977d3b93787c gpl [...] btf_id 4 metadata: metadata_a = "foo" metadata_b = 1 $ bpftool prog --metadata --json --pretty [{ "id": 6, [...] "btf_id": 4, "metadata": { "metadata_a": "foo", "metadata_b": 1 } } ] Cc: YiFei Zhu Signed-off-by: YiFei Zhu Signed-off-by: Stanislav Fomichev --- tools/bpf/bpftool/json_writer.c | 6 ++ tools/bpf/bpftool/json_writer.h | 3 + tools/bpf/bpftool/main.c | 10 +++ tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/prog.c | 130 ++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+) diff --git a/tools/bpf/bpftool/json_writer.c b/tools/bpf/bpftool/json_writer.c index 86501cd3c763..7fea83bedf48 100644 --- a/tools/bpf/bpftool/json_writer.c +++ b/tools/bpf/bpftool/json_writer.c @@ -119,6 +119,12 @@ void jsonw_pretty(json_writer_t *self, bool on) self->pretty = on; } +void jsonw_reset(json_writer_t *self) +{ + assert(self->depth == 0); + self->sep = '\0'; +} + /* Basic blocks */ static void jsonw_begin(json_writer_t *self, int c) { diff --git a/tools/bpf/bpftool/json_writer.h b/tools/bpf/bpftool/json_writer.h index 35cf1f00f96c..8ace65cdb92f 100644 --- a/tools/bpf/bpftool/json_writer.h +++ b/tools/bpf/bpftool/json_writer.h @@ -27,6 +27,9 @@ void jsonw_destroy(json_writer_t **self_p); /* Cause output to have pretty whitespace */ void jsonw_pretty(json_writer_t *self, bool on); +/* Reset separator to create new JSON */ +void jsonw_reset(json_writer_t *self); + /* Add property name */ void jsonw_name(json_writer_t *self, const char *name); diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 4a191fcbeb82..a681d568cfa7 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -28,6 +28,7 @@ bool show_pinned; bool block_mount; bool verifier_logs; bool relaxed_maps; +bool dump_metadata; struct pinned_obj_table prog_table; struct pinned_obj_table map_table; struct pinned_obj_table link_table; @@ -351,6 +352,10 @@ static int do_batch(int argc, char **argv) return err; } +enum bpftool_longonly_opts { + OPT_METADATA = 256, +}; + int main(int argc, char **argv) { static const struct option options[] = { @@ -362,6 +367,7 @@ int main(int argc, char **argv) { "mapcompat", no_argument, NULL, 'm' }, { "nomount", no_argument, NULL, 'n' }, { "debug", no_argument, NULL, 'd' }, + { "metadata", no_argument, NULL, OPT_METADATA }, { 0 } }; int opt, ret; @@ -371,6 +377,7 @@ int main(int argc, char **argv) json_output = false; show_pinned = false; block_mount = false; + dump_metadata = false; bin_name = argv[0]; hash_init(prog_table.table); @@ -412,6 +419,9 @@ int main(int argc, char **argv) libbpf_set_print(print_all_levels); verifier_logs = true; break; + case OPT_METADATA: + dump_metadata = true; + break; default: p_err("unrecognized option '%s'", argv[optind - 1]); if (json_output) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index c46e52137b87..8750758e9150 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -90,6 +90,7 @@ extern bool show_pids; extern bool block_mount; extern bool verifier_logs; extern bool relaxed_maps; +extern bool dump_metadata; extern struct pinned_obj_table prog_table; extern struct pinned_obj_table map_table; extern struct pinned_obj_table link_table; diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index d393eb8263a6..5d626c134e7d 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -151,6 +151,130 @@ static void show_prog_maps(int fd, __u32 num_maps) } } +static void show_prog_metadata(int fd, __u32 num_maps) +{ + const struct btf_type *t_datasec, *t_var; + struct bpf_map_info map_info = {}; + struct btf_var_secinfo *vsi; + struct btf *btf = NULL; + unsigned int i, vlen; + __u32 map_info_len; + void *value = NULL; + int key = 0; + int map_id; + int map_fd; + int err; + + if (!num_maps) + return; + + map_id = bpf_prog_find_metadata(fd); + if (map_id < 0) + return; + + map_fd = bpf_map_get_fd_by_id(map_id); + if (map_fd < 0) { + p_err("can't get map by id (%u): %s", map_id, strerror(errno)); + return; + } + + map_info_len = sizeof(map_info); + err = bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len); + if (err) { + p_err("can't get map info of id (%u): %s", map_id, + strerror(errno)); + goto out_close; + } + + value = malloc(map_info.value_size); + if (!value) { + p_err("mem alloc failed"); + goto out_close; + } + + if (bpf_map_lookup_elem(map_fd, &key, value)) { + p_err("metadata map lookup failed: %s", strerror(errno)); + goto out_free; + } + + err = btf__get_from_id(map_info.btf_id, &btf); + if (err || !btf) { + p_err("metadata BTF get failed: %s", strerror(-err)); + goto out_free; + } + + t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id); + if (BTF_INFO_KIND(t_datasec->info) != BTF_KIND_DATASEC) { + p_err("bad metadata BTF"); + goto out_free; + } + + vlen = BTF_INFO_VLEN(t_datasec->info); + vsi = (struct btf_var_secinfo *)(t_datasec + 1); + + /* We don't proceed to check the kinds of the elements of the DATASEC. + * The verifier enforce then to be BTF_KIND_VAR. + */ + + if (json_output) { + struct btf_dumper d = { + .btf = btf, + .jw = json_wtr, + .is_plain_text = false, + }; + + jsonw_name(json_wtr, "metadata"); + + jsonw_start_object(json_wtr); + for (i = 0; i < vlen; i++) { + t_var = btf__type_by_id(btf, vsi[i].type); + + jsonw_name(json_wtr, btf__name_by_offset(btf, t_var->name_off)); + err = btf_dumper_type(&d, t_var->type, value + vsi[i].offset); + if (err) { + p_err("btf dump failed"); + break; + } + } + jsonw_end_object(json_wtr); + } else { + json_writer_t *btf_wtr = jsonw_new(stdout); + struct btf_dumper d = { + .btf = btf, + .jw = btf_wtr, + .is_plain_text = true, + }; + if (!btf_wtr) { + p_err("jsonw alloc failed"); + goto out_free; + } + + printf("\tmetadata:"); + + for (i = 0; i < vlen; i++) { + t_var = btf__type_by_id(btf, vsi[i].type); + + printf("\n\t\t%s = ", btf__name_by_offset(btf, t_var->name_off)); + + jsonw_reset(btf_wtr); + err = btf_dumper_type(&d, t_var->type, value + vsi[i].offset); + if (err) { + p_err("btf dump failed"); + break; + } + } + + jsonw_destroy(&btf_wtr); + } + +out_free: + btf__free(btf); + free(value); + +out_close: + close(map_fd); +} + static void print_prog_header_json(struct bpf_prog_info *info) { jsonw_uint_field(json_wtr, "id", info->id); @@ -228,6 +352,9 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) emit_obj_refs_json(&refs_table, info->id, json_wtr); + if (dump_metadata) + show_prog_metadata(fd, info->nr_map_ids); + jsonw_end_object(json_wtr); } @@ -297,6 +424,9 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) emit_obj_refs_plain(&refs_table, info->id, "\n\tpids "); printf("\n"); + + if (dump_metadata) + show_prog_metadata(fd, info->nr_map_ids); } static int show_prog(int fd) From patchwork Fri Aug 28 19:36:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stanislav Fomichev X-Patchwork-Id: 261788 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.5 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_INVALID, DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AC9A3C433E2 for ; Fri, 28 Aug 2020 19:36:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8D7802087D for ; Fri, 28 Aug 2020 19:36:57 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=google.com header.i=@google.com header.b="XqtFeGtH" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726867AbgH1Tg4 (ORCPT ); Fri, 28 Aug 2020 15:36:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47218 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726762AbgH1Tgc (ORCPT ); Fri, 28 Aug 2020 15:36:32 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7662AC06123E for ; Fri, 28 Aug 2020 12:36:21 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id p138so291498yba.12 for ; Fri, 28 Aug 2020 12:36:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=WaTo6ZJcGLHZksayG/dsTkSRXBIBZTSwi40c3afpT9s=; b=XqtFeGtHuu0gx/158ocomVxIYVECyAvi9+H75KLQftxOdEFwbu0W7GEHg6Qv2z11N7 ZuavD9SOP+WDH5DTjzLRIzlpeMT56zH+NVAeD8t3uipSvKdzQifXfl3guW19o6maqdhw zW3WLNb6Zuy/sUSXZUsn8/7m6Li4C44m4rim+k0kfDHjtjkDtj+9ZrvZV8OkAQDUvvoM C0ZbGaw0kbKeHF8NKOdLO2e3FwpuADnVxvp5jzHGZqiVbnCfDoAVsglRSR3XcTQXfZxK WmaZaLLU4yO3eDLXoBqmSDAwsXTAP9VW4oumdYmlYbeB/yVfW3s4rPF+EEJ5xgzKQDt9 vbIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=WaTo6ZJcGLHZksayG/dsTkSRXBIBZTSwi40c3afpT9s=; b=A6ig9erZQzmpvrdySUX3ziNa48N5lH3DHr4FB3/IS2y1jGhlaLlwtqdEtYUWPaRk0G JXdYF55qN3YR1e3zW0DhF0uDVHzSFfkDg8frzjJPVYIppA0jRNB+Ss2Xo0+/o4PoLYwD woEw/FB6OuUcH81L76Eg1EOQ3VnVlgnaO60bZRfWk19fId/bEMnH4m8MzoFCuyxPvGaB zKOSY1Ecu9Ka+BRxWku+K9C6OvTNuWvr3I5ZJBQZyEYY3k3PJygRHWhMHkcU96lZPndB rydaTjsTYiyX8ZWnBZJGdXH5olW8okLaGFnYqmiQEYDTGTVfqnVrjq92dEiAn4vblowB /9Vw== X-Gm-Message-State: AOAM5314L2T2b09+eVAoiU24eLf2hfERYRwt08La4HzwafWg27BoHq0u 3a/uVvYFoTVkIDspyLUV6j6OwcvzTmJ8Xxdvbs1KW5igxm/RiOASlSwOi+GcHYUnUCokoFGEy/R IgVLR2bCav2Y9x91pDEgGbu393oOabe2iiqYOLQVHyKgLsMCcv49lyA== X-Google-Smtp-Source: ABdhPJxP/DYvvsPeuQ4BlDskYz65IqwhgmzcHtsDjMXmG7aRmZ9JUcD5A8yWlH7pMn0+e8qGDmB27pM= X-Received: from sdf2.svl.corp.google.com ([2620:15c:2c4:1:7220:84ff:fe09:7732]) (user=sdf job=sendgmr) by 2002:a25:8b89:: with SMTP id j9mr4834608ybl.31.1598643380602; Fri, 28 Aug 2020 12:36:20 -0700 (PDT) Date: Fri, 28 Aug 2020 12:36:03 -0700 In-Reply-To: <20200828193603.335512-1-sdf@google.com> Message-Id: <20200828193603.335512-9-sdf@google.com> Mime-Version: 1.0 References: <20200828193603.335512-1-sdf@google.com> X-Mailer: git-send-email 2.28.0.402.g5ffc5be6b7-goog Subject: [PATCH bpf-next v3 8/8] selftests/bpf: Test load and dump metadata with btftool and skel From: Stanislav Fomichev To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: davem@davemloft.net, ast@kernel.org, daniel@iogearbox.net, YiFei Zhu , YiFei Zhu , Stanislav Fomichev Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: YiFei Zhu This is a simple test to check that loading and dumping metadata in btftool works, whether or not metadata contents are used by the program. A C test is also added to make sure the skeleton code can read the metadata values, and we also check that trying to re-bind the map causes EEXIST, so we are sure the map is already bound by libbpf when loading skeleton. Cc: YiFei Zhu Signed-off-by: YiFei Zhu Signed-off-by: Stanislav Fomichev --- tools/testing/selftests/bpf/Makefile | 3 +- .../selftests/bpf/prog_tests/metadata.c | 83 +++++++++++++++++++ .../selftests/bpf/progs/metadata_unused.c | 15 ++++ .../selftests/bpf/progs/metadata_used.c | 15 ++++ .../selftests/bpf/test_bpftool_metadata.sh | 82 ++++++++++++++++++ 5 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/metadata.c create mode 100644 tools/testing/selftests/bpf/progs/metadata_unused.c create mode 100644 tools/testing/selftests/bpf/progs/metadata_used.c create mode 100755 tools/testing/selftests/bpf/test_bpftool_metadata.sh diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 09657d0afb5c..f7fd1503d210 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -68,7 +68,8 @@ TEST_PROGS := test_kmod.sh \ test_tc_edt.sh \ test_xdping.sh \ test_bpftool_build.sh \ - test_bpftool.sh + test_bpftool.sh \ + test_bpftool_metadata.sh \ TEST_PROGS_EXTENDED := with_addr.sh \ with_tunnels.sh \ diff --git a/tools/testing/selftests/bpf/prog_tests/metadata.c b/tools/testing/selftests/bpf/prog_tests/metadata.c new file mode 100644 index 000000000000..086a601a3f74 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/metadata.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright 2020 Google LLC. + */ + +#include +#include +#include + +#include "metadata_unused.skel.h" +#include "metadata_used.skel.h" + +static int duration; + +static void test_metadata_unused(void) +{ + struct metadata_unused *obj; + int err; + + obj = metadata_unused__open_and_load(); + if (CHECK(!obj, "skel-load", "errno %d", errno)) + return; + + /* Assert that we can access the metadata in skel and the values are + * what we expect. + */ + if (CHECK(strncmp(obj->metadata->metadata_a, "foo", + sizeof(obj->metadata->metadata_a)), + "metadata_a", "expected \"foo\", value differ")) + goto close_bpf_object; + if (CHECK(obj->metadata->metadata_b != 1, "metadata_b", + "expected 1, got %d", obj->metadata->metadata_b)) + goto close_bpf_object; + + /* Assert that binding metadata map to prog again results in EEXIST. */ + err = bpf_prog_bind_map(bpf_program__fd(obj->progs.prog), + bpf_map__fd(obj->maps.metadata), NULL); + CHECK(!err || errno != EEXIST, "rebind_map", + "errno %d, expected EEXIST", errno); + +close_bpf_object: + metadata_unused__destroy(obj); +} + +static void test_metadata_used(void) +{ + struct metadata_used *obj; + int err; + + obj = metadata_used__open_and_load(); + if (CHECK(!obj, "skel-load", "errno %d", errno)) + return; + + /* Assert that we can access the metadata in skel and the values are + * what we expect. + */ + if (CHECK(strncmp(obj->metadata->metadata_a, "bar", + sizeof(obj->metadata->metadata_a)), + "metadata_a", "expected \"bar\", value differ")) + goto close_bpf_object; + if (CHECK(obj->metadata->metadata_b != 2, "metadata_b", + "expected 2, got %d", obj->metadata->metadata_b)) + goto close_bpf_object; + + /* Assert that binding metadata map to prog again results in EEXIST. */ + err = bpf_prog_bind_map(bpf_program__fd(obj->progs.prog), + bpf_map__fd(obj->maps.metadata), NULL); + CHECK(!err || errno != EEXIST, "rebind_map", + "errno %d, expected EEXIST", errno); + +close_bpf_object: + metadata_used__destroy(obj); +} + +void test_metadata(void) +{ + if (test__start_subtest("unused")) + test_metadata_unused(); + + if (test__start_subtest("used")) + test_metadata_used(); +} diff --git a/tools/testing/selftests/bpf/progs/metadata_unused.c b/tools/testing/selftests/bpf/progs/metadata_unused.c new file mode 100644 index 000000000000..523b3c332426 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/metadata_unused.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +char metadata_a[] SEC(".metadata") = "foo"; +int metadata_b SEC(".metadata") = 1; + +SEC("cgroup_skb/egress") +int prog(struct xdp_md *ctx) +{ + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/metadata_used.c b/tools/testing/selftests/bpf/progs/metadata_used.c new file mode 100644 index 000000000000..59785404f7bb --- /dev/null +++ b/tools/testing/selftests/bpf/progs/metadata_used.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +char metadata_a[] SEC(".metadata") = "bar"; +int metadata_b SEC(".metadata") = 2; + +SEC("cgroup_skb/egress") +int prog(struct xdp_md *ctx) +{ + return metadata_b ? 1 : 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_bpftool_metadata.sh b/tools/testing/selftests/bpf/test_bpftool_metadata.sh new file mode 100755 index 000000000000..a7515c09dc2d --- /dev/null +++ b/tools/testing/selftests/bpf/test_bpftool_metadata.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +# Kselftest framework requirement - SKIP code is 4. +ksft_skip=4 + +TESTNAME=bpftool_metadata +BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts) +BPF_DIR=$BPF_FS/test_$TESTNAME + +_cleanup() +{ + set +e + rm -rf $BPF_DIR 2> /dev/null +} + +cleanup_skip() +{ + echo "selftests: $TESTNAME [SKIP]" + _cleanup + + exit $ksft_skip +} + +cleanup() +{ + if [ "$?" = 0 ]; then + echo "selftests: $TESTNAME [PASS]" + else + echo "selftests: $TESTNAME [FAILED]" + fi + _cleanup +} + +if [ $(id -u) -ne 0 ]; then + echo "selftests: $TESTNAME [SKIP] Need root privileges" + exit $ksft_skip +fi + +if [ -z "$BPF_FS" ]; then + echo "selftests: $TESTNAME [SKIP] Could not run test without bpffs mounted" + exit $ksft_skip +fi + +if ! bpftool version > /dev/null 2>&1; then + echo "selftests: $TESTNAME [SKIP] Could not run test without bpftool" + exit $ksft_skip +fi + +set -e + +trap cleanup_skip EXIT + +mkdir $BPF_DIR + +trap cleanup EXIT + +bpftool prog load metadata_unused.o $BPF_DIR/unused + +METADATA_PLAIN="$(bpftool prog --metadata)" +echo "$METADATA_PLAIN" | grep 'metadata_a = "foo"' > /dev/null +echo "$METADATA_PLAIN" | grep 'metadata_b = 1' > /dev/null + +bpftool prog --metadata --json | grep '"metadata":{"metadata_a":"foo","metadata_b":1}' > /dev/null + +bpftool map | grep 'metada.metadata' > /dev/null + +rm $BPF_DIR/unused + +bpftool prog load metadata_used.o $BPF_DIR/used + +METADATA_PLAIN="$(bpftool prog --metadata)" +echo "$METADATA_PLAIN" | grep 'metadata_a = "bar"' > /dev/null +echo "$METADATA_PLAIN" | grep 'metadata_b = 2' > /dev/null + +bpftool prog --metadata --json | grep '"metadata":{"metadata_a":"bar","metadata_b":2}' > /dev/null + +bpftool map | grep 'metada.metadata' > /dev/null + +rm $BPF_DIR/used + +exit 0