Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next

Daniel Borkmann says:

====================
pull-request: bpf-next 2019-02-07

The following pull-request contains BPF updates for your *net-next* tree.

The main changes are:

1) Add a riscv64 JIT for BPF, from Björn.

2) Implement BTF deduplication algorithm for libbpf which takes BTF type
   information containing duplicate per-compilation unit information and
   reduces it to an equivalent set of BTF types with no duplication and
   without loss of information, from Andrii.

3) Offloaded and native BPF XDP programs can coexist today, enable also
   offloaded and generic ones as well, from Jakub.

4) Expose various BTF related helper functions in libbpf as API which
   are in particular helpful for JITed programs, from Yonghong.

5) Fix the recently added JMP32 code emission in s390x JIT, from Heiko.

6) Fix BPF kselftests' tcp_{server,client}.py to be able to run inside
   a network namespace, also add a fix for libbpf to get libbpf_print()
   working, from Stanislav.

7) Fixes for bpftool documentation, from Prashant.

8) Type cleanup in BPF kselftests' test_maps.c to silence a gcc8 warning,
   from Breno.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-02-06 16:56:20 -08:00
commit e90b1fd83c
34 changed files with 4354 additions and 392 deletions

View file

@ -464,10 +464,11 @@ breakpoints: 0 1
JIT compiler
------------
The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC, PowerPC,
ARM, ARM64, MIPS and s390 and can be enabled through CONFIG_BPF_JIT. The JIT
compiler is transparently invoked for each attached filter from user space
or for internal kernel users if it has been previously enabled by root:
The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC,
PowerPC, ARM, ARM64, MIPS, RISC-V and s390 and can be enabled through
CONFIG_BPF_JIT. The JIT compiler is transparently invoked for each
attached filter from user space or for internal kernel users if it has
been previously enabled by root:
echo 1 > /proc/sys/net/core/bpf_jit_enable
@ -603,9 +604,10 @@ got from bpf_prog_create(), and 'ctx' the given context (e.g.
skb pointer). All constraints and restrictions from bpf_check_classic() apply
before a conversion to the new layout is being done behind the scenes!
Currently, the classic BPF format is being used for JITing on most 32-bit
architectures, whereas x86-64, aarch64, s390x, powerpc64, sparc64, arm32 perform
JIT compilation from eBPF instruction set.
Currently, the classic BPF format is being used for JITing on most
32-bit architectures, whereas x86-64, aarch64, s390x, powerpc64,
sparc64, arm32, riscv (RV64G) perform JIT compilation from eBPF
instruction set.
Some core changes of the new internal format:

View file

@ -52,6 +52,7 @@ two flavors of JITs, the newer eBPF JIT currently supported on:
- sparc64
- mips64
- s390x
- riscv
And the older cBPF JIT supported on the following archs:
- mips

View file

@ -2907,6 +2907,12 @@ L: netdev@vger.kernel.org
S: Maintained
F: arch/powerpc/net/
BPF JIT for RISC-V (RV64G)
M: Björn Töpel <bjorn.topel@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: arch/riscv/net/
BPF JIT for S390
M: Martin Schwidefsky <schwidefsky@de.ibm.com>
M: Heiko Carstens <heiko.carstens@de.ibm.com>

View file

@ -49,6 +49,7 @@ config RISCV
select RISCV_TIMER
select GENERIC_IRQ_MULTI_HANDLER
select ARCH_HAS_PTE_SPECIAL
select HAVE_EBPF_JIT if 64BIT
config MMU
def_bool y

View file

@ -77,7 +77,7 @@ KBUILD_IMAGE := $(boot)/Image.gz
head-y := arch/riscv/kernel/head.o
core-y += arch/riscv/kernel/ arch/riscv/mm/
core-y += arch/riscv/kernel/ arch/riscv/mm/ arch/riscv/net/
libs-y += arch/riscv/lib/

1
arch/riscv/net/Makefile Normal file
View file

@ -0,0 +1 @@
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o

File diff suppressed because it is too large Load diff

View file

@ -1154,7 +1154,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
mask = 0x7000; /* jnz */
if (BPF_CLASS(insn->code) == BPF_JMP32) {
/* llilf %w1,imm (load zero extend imm) */
EMIT6_IMM(0xc0010000, REG_W1, imm);
EMIT6_IMM(0xc00f0000, REG_W1, imm);
/* nr %w1,%dst */
EMIT2(0x1400, REG_W1, dst_reg);
} else {
@ -1216,6 +1216,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
REG_W1, dst_reg, src_reg);
goto branch_oc;
branch_ks:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* lgfi %w1,imm (load sign extend imm) */
EMIT6_IMM(0xc0010000, REG_W1, imm);
/* crj or cgrj %dst,%w1,mask,off */
@ -1223,6 +1224,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
dst_reg, REG_W1, i, off, mask);
break;
branch_ku:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* lgfi %w1,imm (load sign extend imm) */
EMIT6_IMM(0xc0010000, REG_W1, imm);
/* clrj or clgrj %dst,%w1,mask,off */
@ -1230,11 +1232,13 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i
dst_reg, REG_W1, i, off, mask);
break;
branch_xs:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* crj or cgrj %dst,%src,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0076 : 0x0064),
dst_reg, src_reg, i, off, mask);
break;
branch_xu:
is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32;
/* clrj or clgrj %dst,%src,mask,off */
EMIT6_PCREL(0xec000000, (is_jmp32 ? 0x0077 : 0x0065),
dst_reg, src_reg, i, off, mask);

View file

@ -8033,11 +8033,13 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
enum bpf_netdev_command query;
struct bpf_prog *prog = NULL;
bpf_op_t bpf_op, bpf_chk;
bool offload;
int err;
ASSERT_RTNL();
query = flags & XDP_FLAGS_HW_MODE ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG;
offload = flags & XDP_FLAGS_HW_MODE;
query = offload ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG;
bpf_op = bpf_chk = ops->ndo_bpf;
if (!bpf_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE))) {
@ -8050,8 +8052,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
bpf_chk = generic_xdp_install;
if (fd >= 0) {
if (__dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG) ||
__dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG_HW)) {
if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) {
NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time");
return -EEXIST;
}
@ -8066,8 +8067,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
if (IS_ERR(prog))
return PTR_ERR(prog);
if (!(flags & XDP_FLAGS_HW_MODE) &&
bpf_prog_is_dev_bound(prog->aux)) {
if (!offload && bpf_prog_is_dev_bound(prog->aux)) {
NL_SET_ERR_MSG(extack, "using device-bound program without HW_MODE flag is not supported");
bpf_prog_put(prog);
return -EINVAL;

View file

@ -17,8 +17,8 @@ SYNOPSIS
*COMMANDS* :=
{ **show** | **list** | **tree** | **attach** | **detach** | **help** }
MAP COMMANDS
=============
CGROUP COMMANDS
===============
| **bpftool** **cgroup { show | list }** *CGROUP*
| **bpftool** **cgroup tree** [*CGROUP_ROOT*]

View file

@ -16,8 +16,8 @@ SYNOPSIS
*COMMANDS* := { **probe** | **help** }
MAP COMMANDS
=============
FEATURE COMMANDS
================
| **bpftool** **feature probe** [*COMPONENT*] [**macros** [**prefix** *PREFIX*]]
| **bpftool** **feature help**

View file

@ -18,7 +18,7 @@ SYNOPSIS
{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load**
| **loadall** | **help** }
MAP COMMANDS
PROG COMMANDS
=============
| **bpftool** **prog { show | list }** [*PROG*]

File diff suppressed because it is too large Load diff

View file

@ -55,33 +55,44 @@ struct btf_ext_header {
__u32 line_info_len;
};
typedef int (*btf_print_fn_t)(const char *, ...)
__attribute__((format(printf, 1, 2)));
LIBBPF_API void btf__free(struct btf *btf);
LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
const char *type_name);
LIBBPF_API __u32 btf__get_nr_types(const struct btf *btf);
LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf,
__u32 id);
LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id);
LIBBPF_API int btf__fd(const struct btf *btf);
LIBBPF_API void btf__get_strings(const struct btf *btf, const char **strings,
__u32 *str_len);
LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
__u32 expected_key_size,
__u32 expected_value_size,
__u32 *key_type_id, __u32 *value_type_id);
struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
void btf_ext__free(struct btf_ext *btf_ext);
int btf_ext__reloc_func_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **func_info, __u32 *func_info_len);
int btf_ext__reloc_line_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **line_info, __u32 *cnt);
__u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
__u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
LIBBPF_API struct btf_ext *btf_ext__new(__u8 *data, __u32 size);
LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext);
LIBBPF_API int btf_ext__reloc_func_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **func_info, __u32 *cnt);
LIBBPF_API int btf_ext__reloc_line_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **line_info, __u32 *cnt);
LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
struct btf_dedup_opts {
bool dont_resolve_fwds;
};
LIBBPF_API int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
const struct btf_dedup_opts *opts);
#ifdef __cplusplus
} /* extern "C" */

View file

@ -42,6 +42,7 @@
#include "bpf.h"
#include "btf.h"
#include "str_error.h"
#include "libbpf_util.h"
#ifndef EM_BPF
#define EM_BPF 247
@ -53,39 +54,33 @@
#define __printf(a, b) __attribute__((format(printf, a, b)))
__printf(1, 2)
static int __base_pr(const char *format, ...)
static int __base_pr(enum libbpf_print_level level, const char *format,
va_list args)
{
va_list args;
int err;
if (level == LIBBPF_DEBUG)
return 0;
va_start(args, format);
err = vfprintf(stderr, format, args);
va_end(args);
return err;
return vfprintf(stderr, format, args);
}
static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
static __printf(1, 2) libbpf_print_fn_t __pr_debug;
static libbpf_print_fn_t __libbpf_pr = __base_pr;
#define __pr(func, fmt, ...) \
do { \
if ((func)) \
(func)("libbpf: " fmt, ##__VA_ARGS__); \
} while (0)
#define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__)
#define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__)
void libbpf_set_print(libbpf_print_fn_t warn,
libbpf_print_fn_t info,
libbpf_print_fn_t debug)
void libbpf_set_print(libbpf_print_fn_t fn)
{
__pr_warning = warn;
__pr_info = info;
__pr_debug = debug;
__libbpf_pr = fn;
}
__printf(2, 3)
void libbpf_print(enum libbpf_print_level level, const char *format, ...)
{
va_list args;
if (!__libbpf_pr)
return;
va_start(args, format);
__libbpf_pr(level, format, args);
va_end(args);
}
#define STRERR_BUFSIZE 128
@ -839,8 +834,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
else if (strcmp(name, "maps") == 0)
obj->efile.maps_shndx = idx;
else if (strcmp(name, BTF_ELF_SEC) == 0) {
obj->btf = btf__new(data->d_buf, data->d_size,
__pr_debug);
obj->btf = btf__new(data->d_buf, data->d_size);
if (IS_ERR(obj->btf)) {
pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
BTF_ELF_SEC, PTR_ERR(obj->btf));
@ -915,8 +909,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags)
BTF_EXT_ELF_SEC, BTF_ELF_SEC);
} else {
obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
btf_ext_data->d_size,
__pr_debug);
btf_ext_data->d_size);
if (IS_ERR(obj->btf_ext)) {
pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
BTF_EXT_ELF_SEC,
@ -1057,72 +1050,18 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
{
const struct btf_type *container_type;
const struct btf_member *key, *value;
struct bpf_map_def *def = &map->def;
const size_t max_name = 256;
char container_name[max_name];
__s64 key_size, value_size;
__s32 container_id;
__u32 key_type_id, value_type_id;
int ret;
if (snprintf(container_name, max_name, "____btf_map_%s", map->name) ==
max_name) {
pr_warning("map:%s length of '____btf_map_%s' is too long\n",
map->name, map->name);
return -EINVAL;
}
ret = btf__get_map_kv_tids(btf, map->name, def->key_size,
def->value_size, &key_type_id,
&value_type_id);
if (ret)
return ret;
container_id = btf__find_by_name(btf, container_name);
if (container_id < 0) {
pr_debug("map:%s container_name:%s cannot be found in BTF. Missing BPF_ANNOTATE_KV_PAIR?\n",
map->name, container_name);
return container_id;
}
container_type = btf__type_by_id(btf, container_id);
if (!container_type) {
pr_warning("map:%s cannot find BTF type for container_id:%u\n",
map->name, container_id);
return -EINVAL;
}
if (BTF_INFO_KIND(container_type->info) != BTF_KIND_STRUCT ||
BTF_INFO_VLEN(container_type->info) < 2) {
pr_warning("map:%s container_name:%s is an invalid container struct\n",
map->name, container_name);
return -EINVAL;
}
key = (struct btf_member *)(container_type + 1);
value = key + 1;
key_size = btf__resolve_size(btf, key->type);
if (key_size < 0) {
pr_warning("map:%s invalid BTF key_type_size\n",
map->name);
return key_size;
}
if (def->key_size != key_size) {
pr_warning("map:%s btf_key_type_size:%u != map_def_key_size:%u\n",
map->name, (__u32)key_size, def->key_size);
return -EINVAL;
}
value_size = btf__resolve_size(btf, value->type);
if (value_size < 0) {
pr_warning("map:%s invalid BTF value_type_size\n", map->name);
return value_size;
}
if (def->value_size != value_size) {
pr_warning("map:%s btf_value_type_size:%u != map_def_value_size:%u\n",
map->name, (__u32)value_size, def->value_size);
return -EINVAL;
}
map->btf_key_type_id = key->type;
map->btf_value_type_id = value->type;
map->btf_key_type_id = key_type_id;
map->btf_value_type_id = value_type_id;
return 0;
}

View file

@ -47,17 +47,16 @@ enum libbpf_errno {
LIBBPF_API int libbpf_strerror(int err, char *buf, size_t size);
/*
* __printf is defined in include/linux/compiler-gcc.h. However,
* it would be better if libbpf.h didn't depend on Linux header files.
* So instead of __printf, here we use gcc attribute directly.
*/
typedef int (*libbpf_print_fn_t)(const char *, ...)
__attribute__((format(printf, 1, 2)));
enum libbpf_print_level {
LIBBPF_WARN,
LIBBPF_INFO,
LIBBPF_DEBUG,
};
LIBBPF_API void libbpf_set_print(libbpf_print_fn_t warn,
libbpf_print_fn_t info,
libbpf_print_fn_t debug);
typedef int (*libbpf_print_fn_t)(enum libbpf_print_level level,
const char *, va_list ap);
LIBBPF_API void libbpf_set_print(libbpf_print_fn_t fn);
/* Hide internal to user */
struct bpf_object;

View file

@ -133,4 +133,14 @@ LIBBPF_0.0.2 {
bpf_map_lookup_elem_flags;
bpf_object__find_map_fd_by_name;
bpf_get_link_xdp_id;
btf__dedup;
btf__get_map_kv_tids;
btf__get_nr_types;
btf__get_strings;
btf_ext__free;
btf_ext__func_info_rec_size;
btf_ext__line_info_rec_size;
btf_ext__new;
btf_ext__reloc_func_info;
btf_ext__reloc_line_info;
} LIBBPF_0.0.1;

View file

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* Copyright (c) 2019 Facebook */
#ifndef __LIBBPF_LIBBPF_UTIL_H
#define __LIBBPF_LIBBPF_UTIL_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void libbpf_print(enum libbpf_print_level level,
const char *format, ...)
__attribute__((format(printf, 2, 3)));
#define __pr(level, fmt, ...) \
do { \
libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__); \
} while (0)
#define pr_warning(fmt, ...) __pr(LIBBPF_WARN, fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...) __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
#define pr_debug(fmt, ...) __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View file

@ -8,11 +8,11 @@
int main(int argc, char *argv[])
{
/* libbpf.h */
libbpf_set_print(NULL, NULL, NULL);
libbpf_set_print(NULL);
/* bpf.h */
bpf_prog_get_fd_by_id(0);
/* btf.h */
btf__new(NULL, 0, NULL);
btf__new(NULL, 0);
}

View file

@ -24,22 +24,12 @@
#include "llvm-utils.h"
#include "c++/clang-c.h"
#define DEFINE_PRINT_FN(name, level) \
static int libbpf_##name(const char *fmt, ...) \
{ \
va_list args; \
int ret; \
\
va_start(args, fmt); \
ret = veprintf(level, verbose, pr_fmt(fmt), args);\
va_end(args); \
return ret; \
static int libbpf_perf_print(enum libbpf_print_level level __attribute__((unused)),
const char *fmt, va_list args)
{
return veprintf(1, verbose, pr_fmt(fmt), args);
}
DEFINE_PRINT_FN(warning, 1)
DEFINE_PRINT_FN(info, 1)
DEFINE_PRINT_FN(debug, 1)
struct bpf_prog_priv {
bool is_tp;
char *sys_name;
@ -59,9 +49,7 @@ bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
struct bpf_object *obj;
if (!libbpf_initialized) {
libbpf_set_print(libbpf_warning,
libbpf_info,
libbpf_debug);
libbpf_set_print(libbpf_perf_print);
libbpf_initialized = true;
}
@ -79,9 +67,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
struct bpf_object *obj;
if (!libbpf_initialized) {
libbpf_set_print(libbpf_warning,
libbpf_info,
libbpf_debug);
libbpf_set_print(libbpf_perf_print);
libbpf_initialized = true;
}

View file

@ -30,12 +30,11 @@ def send(sock, s):
serverPort = int(sys.argv[1])
HostName = socket.gethostname()
# create active socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
try:
sock.connect((HostName, serverPort))
sock.connect(('localhost', serverPort))
except socket.error as e:
sys.exit(1)

View file

@ -35,13 +35,10 @@ MAX_PORTS = 2
serverPort = SERVER_PORT
serverSocket = None
HostName = socket.gethostname()
# create passive socket
serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
host = socket.gethostname()
try: serverSocket.bind((host, 0))
try: serverSocket.bind(('localhost', 0))
except socket.error as msg:
print('bind fails: ' + str(msg))

View file

@ -52,18 +52,10 @@ static int count_result(int err)
return err;
}
#define __printf(a, b) __attribute__((format(printf, a, b)))
__printf(1, 2)
static int __base_pr(const char *format, ...)
static int __base_pr(enum libbpf_print_level level __attribute__((unused)),
const char *format, va_list args)
{
va_list args;
int err;
va_start(args, format);
err = vfprintf(stderr, format, args);
va_end(args);
return err;
return vfprintf(stderr, format, args);
}
#define BTF_INFO_ENC(kind, kind_flag, vlen) \
@ -78,12 +70,21 @@ static int __base_pr(const char *format, ...)
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
BTF_INT_ENC(encoding, bits_offset, bits)
#define BTF_FWD_ENC(name, kind_flag) \
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FWD, kind_flag, 0), 0)
#define BTF_ARRAY_ENC(type, index_type, nr_elems) \
(type), (index_type), (nr_elems)
#define BTF_TYPE_ARRAY_ENC(type, index_type, nr_elems) \
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ARRAY, 0, 0), 0), \
BTF_ARRAY_ENC(type, index_type, nr_elems)
#define BTF_STRUCT_ENC(name, nr_elems, sz) \
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, nr_elems), sz)
#define BTF_UNION_ENC(name, nr_elems, sz) \
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_UNION, 0, nr_elems), sz)
#define BTF_MEMBER_ENC(name, type, bits_offset) \
(name), (type), (bits_offset)
#define BTF_ENUM_ENC(name, val) (name), (val)
@ -99,6 +100,12 @@ static int __base_pr(const char *format, ...)
#define BTF_CONST_ENC(type) \
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), type)
#define BTF_VOLATILE_ENC(type) \
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), type)
#define BTF_RESTRICT_ENC(type) \
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_RESTRICT, 0, 0), type)
#define BTF_FUNC_PROTO_ENC(ret_type, nargs) \
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, nargs), ret_type)
@ -111,6 +118,10 @@ static int __base_pr(const char *format, ...)
#define BTF_END_RAW 0xdeadbeef
#define NAME_TBD 0xdeadb33f
#define NAME_NTH(N) (0xffff0000 | N)
#define IS_NAME_NTH(X) ((X & 0xffff0000) == 0xffff0000)
#define GET_NAME_NTH_IDX(X) (X & 0x0000ffff)
#define MAX_NR_RAW_U32 1024
#define BTF_LOG_BUF_SIZE 65535
@ -119,12 +130,14 @@ static struct args {
unsigned int file_test_num;
unsigned int get_info_test_num;
unsigned int info_raw_test_num;
unsigned int dedup_test_num;
bool raw_test;
bool file_test;
bool get_info_test;
bool pprint_test;
bool always_log;
bool info_raw_test;
bool dedup_test;
} args;
static char btf_log_buf[BTF_LOG_BUF_SIZE];
@ -1965,7 +1978,7 @@ static struct btf_raw_test raw_tests[] = {
/* void (*)(int a, unsigned int <bad_name_off>) */
BTF_FUNC_PROTO_ENC(0, 2), /* [3] */
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
BTF_FUNC_PROTO_ARG_ENC(0xffffffff, 2),
BTF_FUNC_PROTO_ARG_ENC(0x0fffffff, 2),
BTF_END_RAW,
},
.str_sec = "\0a",
@ -2835,11 +2848,13 @@ static void *btf_raw_create(const struct btf_header *hdr,
const char **ret_next_str)
{
const char *next_str = str, *end_str = str + str_sec_size;
const char **strs_idx = NULL, **tmp_strs_idx;
int strs_cap = 0, strs_cnt = 0, next_str_idx = 0;
unsigned int size_needed, offset;
struct btf_header *ret_hdr;
int i, type_sec_size;
int i, type_sec_size, err = 0;
uint32_t *ret_types;
void *raw_btf;
void *raw_btf = NULL;
type_sec_size = get_raw_sec_size(raw_types);
if (CHECK(type_sec_size < 0, "Cannot get nr_raw_types"))
@ -2854,17 +2869,44 @@ static void *btf_raw_create(const struct btf_header *hdr,
memcpy(raw_btf, hdr, sizeof(*hdr));
offset = sizeof(*hdr);
/* Index strings */
while ((next_str = get_next_str(next_str, end_str))) {
if (strs_cnt == strs_cap) {
strs_cap += max(16, strs_cap / 2);
tmp_strs_idx = realloc(strs_idx,
sizeof(*strs_idx) * strs_cap);
if (CHECK(!tmp_strs_idx,
"Cannot allocate memory for strs_idx")) {
err = -1;
goto done;
}
strs_idx = tmp_strs_idx;
}
strs_idx[strs_cnt++] = next_str;
next_str += strlen(next_str);
}
/* Copy type section */
ret_types = raw_btf + offset;
for (i = 0; i < type_sec_size / sizeof(raw_types[0]); i++) {
if (raw_types[i] == NAME_TBD) {
next_str = get_next_str(next_str, end_str);
if (CHECK(!next_str, "Error in getting next_str")) {
free(raw_btf);
return NULL;
if (CHECK(next_str_idx == strs_cnt,
"Error in getting next_str #%d",
next_str_idx)) {
err = -1;
goto done;
}
ret_types[i] = next_str - str;
next_str += strlen(next_str);
ret_types[i] = strs_idx[next_str_idx++] - str;
} else if (IS_NAME_NTH(raw_types[i])) {
int idx = GET_NAME_NTH_IDX(raw_types[i]);
if (CHECK(idx <= 0 || idx > strs_cnt,
"Error getting string #%d, strs_cnt:%d",
idx, strs_cnt)) {
err = -1;
goto done;
}
ret_types[i] = strs_idx[idx-1] - str;
} else {
ret_types[i] = raw_types[i];
}
@ -2881,8 +2923,17 @@ static void *btf_raw_create(const struct btf_header *hdr,
*btf_size = size_needed;
if (ret_next_str)
*ret_next_str = next_str;
*ret_next_str =
next_str_idx < strs_cnt ? strs_idx[next_str_idx] : NULL;
done:
if (err) {
if (raw_btf)
free(raw_btf);
if (strs_idx)
free(strs_idx);
return NULL;
}
return raw_btf;
}
@ -5551,20 +5602,450 @@ static int test_info_raw(void)
return err;
}
struct btf_raw_data {
__u32 raw_types[MAX_NR_RAW_U32];
const char *str_sec;
__u32 str_sec_size;
};
struct btf_dedup_test {
const char *descr;
struct btf_raw_data input;
struct btf_raw_data expect;
struct btf_dedup_opts opts;
};
const struct btf_dedup_test dedup_tests[] = {
{
.descr = "dedup: unused strings filtering",
.input = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 32, 4),
BTF_TYPE_INT_ENC(NAME_NTH(5), BTF_INT_SIGNED, 0, 64, 8),
BTF_END_RAW,
},
BTF_STR_SEC("\0unused\0int\0foo\0bar\0long"),
},
.expect = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 64, 8),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0long"),
},
.opts = {
.dont_resolve_fwds = false,
},
},
{
.descr = "dedup: strings deduplication",
.input = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 64, 8),
BTF_TYPE_INT_ENC(NAME_NTH(3), BTF_INT_SIGNED, 0, 32, 4),
BTF_TYPE_INT_ENC(NAME_NTH(4), BTF_INT_SIGNED, 0, 64, 8),
BTF_TYPE_INT_ENC(NAME_NTH(5), BTF_INT_SIGNED, 0, 32, 4),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0long int\0int\0long int\0int"),
},
.expect = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 64, 8),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0long int"),
},
.opts = {
.dont_resolve_fwds = false,
},
},
{
.descr = "dedup: struct example #1",
/*
* struct s {
* struct s *next;
* const int *a;
* int b[16];
* int c;
* }
*/
.input = {
.raw_types = {
/* int */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4), /* [1] */
/* int[16] */
BTF_TYPE_ARRAY_ENC(1, 1, 16), /* [2] */
/* struct s { */
BTF_STRUCT_ENC(NAME_NTH(2), 4, 84), /* [3] */
BTF_MEMBER_ENC(NAME_NTH(3), 4, 0), /* struct s *next; */
BTF_MEMBER_ENC(NAME_NTH(4), 5, 64), /* const int *a; */
BTF_MEMBER_ENC(NAME_NTH(5), 2, 128), /* int b[16]; */
BTF_MEMBER_ENC(NAME_NTH(6), 1, 640), /* int c; */
/* ptr -> [3] struct s */
BTF_PTR_ENC(3), /* [4] */
/* ptr -> [6] const int */
BTF_PTR_ENC(6), /* [5] */
/* const -> [1] int */
BTF_CONST_ENC(1), /* [6] */
/* full copy of the above */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4), /* [7] */
BTF_TYPE_ARRAY_ENC(7, 7, 16), /* [8] */
BTF_STRUCT_ENC(NAME_NTH(2), 4, 84), /* [9] */
BTF_MEMBER_ENC(NAME_NTH(3), 10, 0),
BTF_MEMBER_ENC(NAME_NTH(4), 11, 64),
BTF_MEMBER_ENC(NAME_NTH(5), 8, 128),
BTF_MEMBER_ENC(NAME_NTH(6), 7, 640),
BTF_PTR_ENC(9), /* [10] */
BTF_PTR_ENC(12), /* [11] */
BTF_CONST_ENC(7), /* [12] */
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0s\0next\0a\0b\0c\0"),
},
.expect = {
.raw_types = {
/* int */
BTF_TYPE_INT_ENC(NAME_NTH(4), BTF_INT_SIGNED, 0, 32, 4), /* [1] */
/* int[16] */
BTF_TYPE_ARRAY_ENC(1, 1, 16), /* [2] */
/* struct s { */
BTF_STRUCT_ENC(NAME_NTH(6), 4, 84), /* [3] */
BTF_MEMBER_ENC(NAME_NTH(5), 4, 0), /* struct s *next; */
BTF_MEMBER_ENC(NAME_NTH(1), 5, 64), /* const int *a; */
BTF_MEMBER_ENC(NAME_NTH(2), 2, 128), /* int b[16]; */
BTF_MEMBER_ENC(NAME_NTH(3), 1, 640), /* int c; */
/* ptr -> [3] struct s */
BTF_PTR_ENC(3), /* [4] */
/* ptr -> [6] const int */
BTF_PTR_ENC(6), /* [5] */
/* const -> [1] int */
BTF_CONST_ENC(1), /* [6] */
BTF_END_RAW,
},
BTF_STR_SEC("\0a\0b\0c\0int\0next\0s"),
},
.opts = {
.dont_resolve_fwds = false,
},
},
{
.descr = "dedup: all possible kinds (no duplicates)",
.input = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 8), /* [1] int */
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 2), 4), /* [2] enum */
BTF_ENUM_ENC(NAME_TBD, 0),
BTF_ENUM_ENC(NAME_TBD, 1),
BTF_FWD_ENC(NAME_TBD, 1 /* union kind_flag */), /* [3] fwd */
BTF_TYPE_ARRAY_ENC(2, 1, 7), /* [4] array */
BTF_STRUCT_ENC(NAME_TBD, 1, 4), /* [5] struct */
BTF_MEMBER_ENC(NAME_TBD, 1, 0),
BTF_UNION_ENC(NAME_TBD, 1, 4), /* [6] union */
BTF_MEMBER_ENC(NAME_TBD, 1, 0),
BTF_TYPEDEF_ENC(NAME_TBD, 1), /* [7] typedef */
BTF_PTR_ENC(0), /* [8] ptr */
BTF_CONST_ENC(8), /* [9] const */
BTF_VOLATILE_ENC(8), /* [10] volatile */
BTF_RESTRICT_ENC(8), /* [11] restrict */
BTF_FUNC_PROTO_ENC(1, 2), /* [12] func_proto */
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 8),
BTF_FUNC_ENC(NAME_TBD, 12), /* [13] func */
BTF_END_RAW,
},
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M"),
},
.expect = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 8), /* [1] int */
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 2), 4), /* [2] enum */
BTF_ENUM_ENC(NAME_TBD, 0),
BTF_ENUM_ENC(NAME_TBD, 1),
BTF_FWD_ENC(NAME_TBD, 1 /* union kind_flag */), /* [3] fwd */
BTF_TYPE_ARRAY_ENC(2, 1, 7), /* [4] array */
BTF_STRUCT_ENC(NAME_TBD, 1, 4), /* [5] struct */
BTF_MEMBER_ENC(NAME_TBD, 1, 0),
BTF_UNION_ENC(NAME_TBD, 1, 4), /* [6] union */
BTF_MEMBER_ENC(NAME_TBD, 1, 0),
BTF_TYPEDEF_ENC(NAME_TBD, 1), /* [7] typedef */
BTF_PTR_ENC(0), /* [8] ptr */
BTF_CONST_ENC(8), /* [9] const */
BTF_VOLATILE_ENC(8), /* [10] volatile */
BTF_RESTRICT_ENC(8), /* [11] restrict */
BTF_FUNC_PROTO_ENC(1, 2), /* [12] func_proto */
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 8),
BTF_FUNC_ENC(NAME_TBD, 12), /* [13] func */
BTF_END_RAW,
},
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M"),
},
.opts = {
.dont_resolve_fwds = false,
},
},
{
.descr = "dedup: no int duplicates",
.input = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 8),
/* different name */
BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 32, 8),
/* different encoding */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_CHAR, 0, 32, 8),
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_BOOL, 0, 32, 8),
/* different bit offset */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 8, 32, 8),
/* different bit size */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 27, 8),
/* different byte size */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0some other int"),
},
.expect = {
.raw_types = {
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 8),
/* different name */
BTF_TYPE_INT_ENC(NAME_NTH(2), BTF_INT_SIGNED, 0, 32, 8),
/* different encoding */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_CHAR, 0, 32, 8),
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_BOOL, 0, 32, 8),
/* different bit offset */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 8, 32, 8),
/* different bit size */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 27, 8),
/* different byte size */
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
BTF_END_RAW,
},
BTF_STR_SEC("\0int\0some other int"),
},
.opts = {
.dont_resolve_fwds = false,
},
},
};
static int btf_type_size(const struct btf_type *t)
{
int base_size = sizeof(struct btf_type);
__u16 vlen = BTF_INFO_VLEN(t->info);
__u16 kind = BTF_INFO_KIND(t->info);
switch (kind) {
case BTF_KIND_FWD:
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
case BTF_KIND_PTR:
case BTF_KIND_TYPEDEF:
case BTF_KIND_FUNC:
return base_size;
case BTF_KIND_INT:
return base_size + sizeof(__u32);
case BTF_KIND_ENUM:
return base_size + vlen * sizeof(struct btf_enum);
case BTF_KIND_ARRAY:
return base_size + sizeof(struct btf_array);
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
return base_size + vlen * sizeof(struct btf_member);
case BTF_KIND_FUNC_PROTO:
return base_size + vlen * sizeof(struct btf_param);
default:
fprintf(stderr, "Unsupported BTF_KIND:%u\n", kind);
return -EINVAL;
}
}
static void dump_btf_strings(const char *strs, __u32 len)
{
const char *cur = strs;
int i = 0;
while (cur < strs + len) {
fprintf(stderr, "string #%d: '%s'\n", i, cur);
cur += strlen(cur) + 1;
i++;
}
}
static int do_test_dedup(unsigned int test_num)
{
const struct btf_dedup_test *test = &dedup_tests[test_num - 1];
int err = 0, i;
__u32 test_nr_types, expect_nr_types, test_str_len, expect_str_len;
void *raw_btf;
unsigned int raw_btf_size;
struct btf *test_btf = NULL, *expect_btf = NULL;
const char *ret_test_next_str, *ret_expect_next_str;
const char *test_strs, *expect_strs;
const char *test_str_cur, *test_str_end;
const char *expect_str_cur, *expect_str_end;
fprintf(stderr, "BTF dedup test[%u] (%s):", test_num, test->descr);
raw_btf = btf_raw_create(&hdr_tmpl, test->input.raw_types,
test->input.str_sec, test->input.str_sec_size,
&raw_btf_size, &ret_test_next_str);
if (!raw_btf)
return -1;
test_btf = btf__new((__u8 *)raw_btf, raw_btf_size);
free(raw_btf);
if (CHECK(IS_ERR(test_btf), "invalid test_btf errno:%ld",
PTR_ERR(test_btf))) {
err = -1;
goto done;
}
raw_btf = btf_raw_create(&hdr_tmpl, test->expect.raw_types,
test->expect.str_sec,
test->expect.str_sec_size,
&raw_btf_size, &ret_expect_next_str);
if (!raw_btf)
return -1;
expect_btf = btf__new((__u8 *)raw_btf, raw_btf_size);
free(raw_btf);
if (CHECK(IS_ERR(expect_btf), "invalid expect_btf errno:%ld",
PTR_ERR(expect_btf))) {
err = -1;
goto done;
}
err = btf__dedup(test_btf, NULL, &test->opts);
if (CHECK(err, "btf_dedup failed errno:%d", err)) {
err = -1;
goto done;
}
btf__get_strings(test_btf, &test_strs, &test_str_len);
btf__get_strings(expect_btf, &expect_strs, &expect_str_len);
if (CHECK(test_str_len != expect_str_len,
"test_str_len:%u != expect_str_len:%u",
test_str_len, expect_str_len)) {
fprintf(stderr, "\ntest strings:\n");
dump_btf_strings(test_strs, test_str_len);
fprintf(stderr, "\nexpected strings:\n");
dump_btf_strings(expect_strs, expect_str_len);
err = -1;
goto done;
}
test_str_cur = test_strs;
test_str_end = test_strs + test_str_len;
expect_str_cur = expect_strs;
expect_str_end = expect_strs + expect_str_len;
while (test_str_cur < test_str_end && expect_str_cur < expect_str_end) {
size_t test_len, expect_len;
test_len = strlen(test_str_cur);
expect_len = strlen(expect_str_cur);
if (CHECK(test_len != expect_len,
"test_len:%zu != expect_len:%zu "
"(test_str:%s, expect_str:%s)",
test_len, expect_len, test_str_cur, expect_str_cur)) {
err = -1;
goto done;
}
if (CHECK(strcmp(test_str_cur, expect_str_cur),
"test_str:%s != expect_str:%s",
test_str_cur, expect_str_cur)) {
err = -1;
goto done;
}
test_str_cur += test_len + 1;
expect_str_cur += expect_len + 1;
}
if (CHECK(test_str_cur != test_str_end,
"test_str_cur:%p != test_str_end:%p",
test_str_cur, test_str_end)) {
err = -1;
goto done;
}
test_nr_types = btf__get_nr_types(test_btf);
expect_nr_types = btf__get_nr_types(expect_btf);
if (CHECK(test_nr_types != expect_nr_types,
"test_nr_types:%u != expect_nr_types:%u",
test_nr_types, expect_nr_types)) {
err = -1;
goto done;
}
for (i = 1; i <= test_nr_types; i++) {
const struct btf_type *test_type, *expect_type;
int test_size, expect_size;
test_type = btf__type_by_id(test_btf, i);
expect_type = btf__type_by_id(expect_btf, i);
test_size = btf_type_size(test_type);
expect_size = btf_type_size(expect_type);
if (CHECK(test_size != expect_size,
"type #%d: test_size:%d != expect_size:%u",
i, test_size, expect_size)) {
err = -1;
goto done;
}
if (CHECK(memcmp((void *)test_type,
(void *)expect_type,
test_size),
"type #%d: contents differ", i)) {
err = -1;
goto done;
}
}
done:
if (!err)
fprintf(stderr, "OK");
if (!IS_ERR(test_btf))
btf__free(test_btf);
if (!IS_ERR(expect_btf))
btf__free(expect_btf);
return err;
}
static int test_dedup(void)
{
unsigned int i;
int err = 0;
if (args.dedup_test_num)
return count_result(do_test_dedup(args.dedup_test_num));
for (i = 1; i <= ARRAY_SIZE(dedup_tests); i++)
err |= count_result(do_test_dedup(i));
return err;
}
static void usage(const char *cmd)
{
fprintf(stderr, "Usage: %s [-l] [[-r btf_raw_test_num (1 - %zu)] |\n"
"\t[-g btf_get_info_test_num (1 - %zu)] |\n"
"\t[-f btf_file_test_num (1 - %zu)] |\n"
"\t[-k btf_prog_info_raw_test_num (1 - %zu)] |\n"
"\t[-p (pretty print test)]]\n",
"\t[-p (pretty print test)] |\n"
"\t[-d btf_dedup_test_num (1 - %zu)]]\n",
cmd, ARRAY_SIZE(raw_tests), ARRAY_SIZE(get_info_tests),
ARRAY_SIZE(file_tests), ARRAY_SIZE(info_raw_tests));
ARRAY_SIZE(file_tests), ARRAY_SIZE(info_raw_tests),
ARRAY_SIZE(dedup_tests));
}
static int parse_args(int argc, char **argv)
{
const char *optstr = "lpk:f:r:g:";
const char *optstr = "hlpk:f:r:g:d:";
int opt;
while ((opt = getopt(argc, argv, optstr)) != -1) {
@ -5591,12 +6072,16 @@ static int parse_args(int argc, char **argv)
args.info_raw_test_num = atoi(optarg);
args.info_raw_test = true;
break;
case 'd':
args.dedup_test_num = atoi(optarg);
args.dedup_test = true;
break;
case 'h':
usage(argv[0]);
exit(0);
default:
usage(argv[0]);
return -1;
usage(argv[0]);
return -1;
}
}
@ -5632,6 +6117,14 @@ static int parse_args(int argc, char **argv)
return -1;
}
if (args.dedup_test_num &&
(args.dedup_test_num < 1 ||
args.dedup_test_num > ARRAY_SIZE(dedup_tests))) {
fprintf(stderr, "BTF dedup test number must be [1 - %zu]\n",
ARRAY_SIZE(dedup_tests));
return -1;
}
return 0;
}
@ -5650,7 +6143,7 @@ int main(int argc, char **argv)
return err;
if (args.always_log)
libbpf_set_print(__base_pr, __base_pr, __base_pr);
libbpf_set_print(__base_pr);
if (args.raw_test)
err |= test_raw();
@ -5667,14 +6160,18 @@ int main(int argc, char **argv)
if (args.info_raw_test)
err |= test_info_raw();
if (args.dedup_test)
err |= test_dedup();
if (args.raw_test || args.get_info_test || args.file_test ||
args.pprint_test || args.info_raw_test)
args.pprint_test || args.info_raw_test || args.dedup_test)
goto done;
err |= test_raw();
err |= test_get_info();
err |= test_file();
err |= test_info_raw();
err |= test_dedup();
done:
print_summary();

View file

@ -34,23 +34,16 @@ static void usage(char *argv[])
printf("\n");
}
#define DEFINE_PRINT_FN(name, enabled) \
static int libbpf_##name(const char *fmt, ...) \
{ \
va_list args; \
int ret; \
\
va_start(args, fmt); \
if (enabled) { \
fprintf(stderr, "[" #name "] "); \
ret = vfprintf(stderr, fmt, args); \
} \
va_end(args); \
return ret; \
static bool debug = 0;
static int libbpf_debug_print(enum libbpf_print_level level,
const char *fmt, va_list args)
{
if (level == LIBBPF_DEBUG && !debug)
return 0;
fprintf(stderr, "[%d] ", level);
return vfprintf(stderr, fmt, args);
}
DEFINE_PRINT_FN(warning, 1)
DEFINE_PRINT_FN(info, 1)
DEFINE_PRINT_FN(debug, 1)
#define EXIT_FAIL_LIBBPF EXIT_FAILURE
#define EXIT_FAIL_OPTION 2
@ -120,15 +113,14 @@ int main(int argc, char **argv)
int longindex = 0;
int opt;
libbpf_set_print(libbpf_warning, libbpf_info, NULL);
libbpf_set_print(libbpf_debug_print);
/* Parse commands line args */
while ((opt = getopt_long(argc, argv, "hDq",
long_options, &longindex)) != -1) {
switch (opt) {
case 'D':
libbpf_set_print(libbpf_warning, libbpf_info,
libbpf_debug);
debug = 1;
break;
case 'q': /* Use in scripting mode */
verbose = 0;

View file

@ -45,7 +45,7 @@ static int map_flags;
} \
})
static void test_hashmap(int task, void *data)
static void test_hashmap(unsigned int task, void *data)
{
long long key, next_key, first_key, value;
int fd;
@ -135,7 +135,7 @@ static void test_hashmap(int task, void *data)
close(fd);
}
static void test_hashmap_sizes(int task, void *data)
static void test_hashmap_sizes(unsigned int task, void *data)
{
int fd, i, j;
@ -155,7 +155,7 @@ static void test_hashmap_sizes(int task, void *data)
}
}
static void test_hashmap_percpu(int task, void *data)
static void test_hashmap_percpu(unsigned int task, void *data)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
BPF_DECLARE_PERCPU(long, value);
@ -282,7 +282,7 @@ static int helper_fill_hashmap(int max_entries)
return fd;
}
static void test_hashmap_walk(int task, void *data)
static void test_hashmap_walk(unsigned int task, void *data)
{
int fd, i, max_entries = 1000;
long long key, value, next_key;
@ -353,7 +353,7 @@ static void test_hashmap_zero_seed(void)
close(second);
}
static void test_arraymap(int task, void *data)
static void test_arraymap(unsigned int task, void *data)
{
int key, next_key, fd;
long long value;
@ -408,7 +408,7 @@ static void test_arraymap(int task, void *data)
close(fd);
}
static void test_arraymap_percpu(int task, void *data)
static void test_arraymap_percpu(unsigned int task, void *data)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
BPF_DECLARE_PERCPU(long, values);
@ -504,7 +504,7 @@ static void test_arraymap_percpu_many_keys(void)
close(fd);
}
static void test_devmap(int task, void *data)
static void test_devmap(unsigned int task, void *data)
{
int fd;
__u32 key, value;
@ -519,7 +519,7 @@ static void test_devmap(int task, void *data)
close(fd);
}
static void test_queuemap(int task, void *data)
static void test_queuemap(unsigned int task, void *data)
{
const int MAP_SIZE = 32;
__u32 vals[MAP_SIZE + MAP_SIZE/2], val;
@ -577,7 +577,7 @@ static void test_queuemap(int task, void *data)
close(fd);
}
static void test_stackmap(int task, void *data)
static void test_stackmap(unsigned int task, void *data)
{
const int MAP_SIZE = 32;
__u32 vals[MAP_SIZE + MAP_SIZE/2], val;
@ -642,7 +642,7 @@ static void test_stackmap(int task, void *data)
#define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o"
#define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o"
#define SOCKMAP_TCP_MSG_PROG "./sockmap_tcp_msg_prog.o"
static void test_sockmap(int tasks, void *data)
static void test_sockmap(unsigned int tasks, void *data)
{
struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_msg, *bpf_map_break;
int map_fd_msg = 0, map_fd_rx = 0, map_fd_tx = 0, map_fd_break;
@ -1268,10 +1268,11 @@ static void test_map_large(void)
}
#define run_parallel(N, FN, DATA) \
printf("Fork %d tasks to '" #FN "'\n", N); \
printf("Fork %u tasks to '" #FN "'\n", N); \
__run_parallel(N, FN, DATA)
static void __run_parallel(int tasks, void (*fn)(int task, void *data),
static void __run_parallel(unsigned int tasks,
void (*fn)(unsigned int task, void *data),
void *data)
{
pid_t pid[tasks];
@ -1312,7 +1313,7 @@ static void test_map_stress(void)
#define DO_UPDATE 1
#define DO_DELETE 0
static void test_update_delete(int fn, void *data)
static void test_update_delete(unsigned int fn, void *data)
{
int do_update = ((int *)data)[1];
int fd = ((int *)data)[0];

View file

@ -23,6 +23,7 @@ import string
import struct
import subprocess
import time
import traceback
logfile = None
log_level = 1
@ -78,7 +79,9 @@ def fail(cond, msg):
if not cond:
return
print("FAIL: " + msg)
log("FAIL: " + msg, "", level=1)
tb = "".join(traceback.extract_stack().format())
print(tb)
log("FAIL: " + msg, tb, level=1)
os.sys.exit(1)
def start_test(msg):
@ -589,6 +592,15 @@ def check_verifier_log(output, reference):
return
fail(True, "Missing or incorrect message from netdevsim in verifier log")
def check_multi_basic(two_xdps):
fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
fail("prog" in two_xdps, "Base program reported in multi program mode")
fail(len(two_xdps["attached"]) != 2,
"Wrong attached program count with two programs")
fail(two_xdps["attached"][0]["prog"]["id"] ==
two_xdps["attached"][1]["prog"]["id"],
"Offloaded and other programs have the same id")
def test_spurios_extack(sim, obj, skip_hw, needle):
res = sim.cls_bpf_add_filter(obj, prio=1, handle=1, skip_hw=skip_hw,
include_stderr=True)
@ -600,6 +612,67 @@ def test_spurios_extack(sim, obj, skip_hw, needle):
include_stderr=True)
check_no_extack(res, needle)
def test_multi_prog(sim, obj, modename, modeid):
start_test("Test multi-attachment XDP - %s + offload..." %
(modename or "default", ))
sim.set_xdp(obj, "offload")
xdp = sim.ip_link_show(xdp=True)["xdp"]
offloaded = sim.dfs_read("bpf_offloaded_id")
fail("prog" not in xdp, "Base program not reported in single program mode")
fail(len(xdp["attached"]) != 1,
"Wrong attached program count with one program")
sim.set_xdp(obj, modename)
two_xdps = sim.ip_link_show(xdp=True)["xdp"]
fail(xdp["attached"][0] not in two_xdps["attached"],
"Offload program not reported after other activated")
check_multi_basic(two_xdps)
offloaded2 = sim.dfs_read("bpf_offloaded_id")
fail(offloaded != offloaded2,
"Offload ID changed after loading other program")
start_test("Test multi-attachment XDP - replace...")
ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
fail(ret == 0, "Replaced one of programs without -force")
check_extack(err, "XDP program already attached.", args)
if modename == "" or modename == "drv":
othermode = "" if modename == "drv" else "drv"
start_test("Test multi-attachment XDP - detach...")
ret, _, err = sim.unset_xdp(othermode, force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode")
check_extack(err, "program loaded with different flags.", args)
sim.unset_xdp("offload")
xdp = sim.ip_link_show(xdp=True)["xdp"]
offloaded = sim.dfs_read("bpf_offloaded_id")
fail(xdp["mode"] != modeid, "Bad mode reported after multiple programs")
fail("prog" not in xdp,
"Base program not reported after multi program mode")
fail(xdp["attached"][0] not in two_xdps["attached"],
"Offload program not reported after other activated")
fail(len(xdp["attached"]) != 1,
"Wrong attached program count with remaining programs")
fail(offloaded != "0", "Offload ID reported with only other program left")
start_test("Test multi-attachment XDP - reattach...")
sim.set_xdp(obj, "offload")
two_xdps = sim.ip_link_show(xdp=True)["xdp"]
fail(xdp["attached"][0] not in two_xdps["attached"],
"Other program not reported after offload activated")
check_multi_basic(two_xdps)
start_test("Test multi-attachment XDP - device remove...")
sim.remove()
sim = NetdevSim()
sim.set_ethtool_tc_offloads(True)
return sim
# Parse command line
parser = argparse.ArgumentParser()
@ -842,7 +915,9 @@ try:
ret, _, err = sim.set_xdp(obj, "generic", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Replaced XDP program with a program in different mode")
fail(err.count("File exists") != 1, "Replaced driver XDP with generic")
check_extack(err,
"native and generic XDP can't be active at the same time.",
args)
ret, _, err = sim.set_xdp(obj, "", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Replaced XDP program with a program in different mode")
@ -931,59 +1006,9 @@ try:
rm(pin_file)
bpftool_prog_list_wait(expected=0)
start_test("Test multi-attachment XDP - attach...")
sim.set_xdp(obj, "offload")
xdp = sim.ip_link_show(xdp=True)["xdp"]
offloaded = sim.dfs_read("bpf_offloaded_id")
fail("prog" not in xdp, "Base program not reported in single program mode")
fail(len(ipl["xdp"]["attached"]) != 1,
"Wrong attached program count with one program")
sim.set_xdp(obj, "")
two_xdps = sim.ip_link_show(xdp=True)["xdp"]
offloaded2 = sim.dfs_read("bpf_offloaded_id")
fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
fail("prog" in two_xdps, "Base program reported in multi program mode")
fail(xdp["attached"][0] not in two_xdps["attached"],
"Offload program not reported after driver activated")
fail(len(two_xdps["attached"]) != 2,
"Wrong attached program count with two programs")
fail(two_xdps["attached"][0]["prog"]["id"] ==
two_xdps["attached"][1]["prog"]["id"],
"offloaded and drv programs have the same id")
fail(offloaded != offloaded2,
"offload ID changed after loading driver program")
start_test("Test multi-attachment XDP - replace...")
ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
fail(err.count("busy") != 1, "Replaced one of programs without -force")
start_test("Test multi-attachment XDP - detach...")
ret, _, err = sim.unset_xdp("drv", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode")
check_extack(err, "program loaded with different flags.", args)
sim.unset_xdp("offload")
xdp = sim.ip_link_show(xdp=True)["xdp"]
offloaded = sim.dfs_read("bpf_offloaded_id")
fail(xdp["mode"] != 1, "Bad mode reported after multiple programs")
fail("prog" not in xdp,
"Base program not reported after multi program mode")
fail(xdp["attached"][0] not in two_xdps["attached"],
"Offload program not reported after driver activated")
fail(len(ipl["xdp"]["attached"]) != 1,
"Wrong attached program count with remaining programs")
fail(offloaded != "0", "offload ID reported with only driver program left")
start_test("Test multi-attachment XDP - device remove...")
sim.set_xdp(obj, "offload")
sim.remove()
sim = NetdevSim()
sim.set_ethtool_tc_offloads(True)
sim = test_multi_prog(sim, obj, "", 1)
sim = test_multi_prog(sim, obj, "drv", 1)
sim = test_multi_prog(sim, obj, "generic", 2)
start_test("Test mixing of TC and XDP...")
sim.tc_add_ingress()

View file

@ -10,6 +10,7 @@
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <linux/types.h>
@ -1783,6 +1784,15 @@ static void test_task_fd_query_tp(void)
"sys_enter_read");
}
static int libbpf_debug_print(enum libbpf_print_level level,
const char *format, va_list args)
{
if (level == LIBBPF_DEBUG)
return 0;
return vfprintf(stderr, format, args);
}
static void test_reference_tracking()
{
const char *file = "./test_sk_lookup_kern.o";
@ -1809,9 +1819,9 @@ static void test_reference_tracking()
/* Expect verifier failure if test name has 'fail' */
if (strstr(title, "fail") != NULL) {
libbpf_set_print(NULL, NULL, NULL);
libbpf_set_print(NULL);
err = !bpf_program__load(prog, "GPL", 0);
libbpf_set_print(printf, printf, NULL);
libbpf_set_print(libbpf_debug_print);
} else {
err = bpf_program__load(prog, "GPL", 0);
}

View file

@ -100,6 +100,7 @@
.errstr = "invalid bpf_context access",
.result = REJECT,
.prog_type = BPF_PROG_TYPE_SK_MSG,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"invalid read past end of SK_MSG",

View file

@ -687,6 +687,7 @@
},
.errstr = "invalid bpf_context access",
.result = REJECT,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"check skb->hash half load not permitted, unaligned 3",

View file

@ -27,6 +27,7 @@
.data64 = { 1ULL << 63 | 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jset32: BPF_X",
@ -58,6 +59,7 @@
.data64 = { 1ULL << 63 | 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jset32: min/max deduction",
@ -93,6 +95,7 @@
.data64 = { -1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jeq32: BPF_X",
@ -119,6 +122,7 @@
.data64 = { 1ULL << 63 | 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jeq32: min/max deduction",
@ -154,6 +158,7 @@
.data64 = { -1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jne32: BPF_X",
@ -180,6 +185,7 @@
.data64 = { 1ULL << 63 | 2, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jne32: min/max deduction",
@ -218,6 +224,7 @@
.data64 = { 0, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jge32: BPF_X",
@ -244,6 +251,7 @@
.data64 = { (UINT_MAX - 1) | 2ULL << 32, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jge32: min/max deduction",
@ -284,6 +292,7 @@
.data64 = { 0, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jgt32: BPF_X",
@ -310,6 +319,7 @@
.data64 = { (UINT_MAX - 1) | 2ULL << 32, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jgt32: min/max deduction",
@ -350,6 +360,7 @@
.data64 = { INT_MAX, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jle32: BPF_X",
@ -376,6 +387,7 @@
.data64 = { UINT_MAX, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jle32: min/max deduction",
@ -416,6 +428,7 @@
.data64 = { INT_MAX - 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jlt32: BPF_X",
@ -442,6 +455,7 @@
.data64 = { (INT_MAX - 1) | 3ULL << 32, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jlt32: min/max deduction",
@ -482,6 +496,7 @@
.data64 = { -2, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jsge32: BPF_X",
@ -508,6 +523,7 @@
.data64 = { -2, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jsge32: min/max deduction",
@ -548,6 +564,7 @@
.data64 = { 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jsgt32: BPF_X",
@ -574,6 +591,7 @@
.data64 = { 0x7fffffff, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jsgt32: min/max deduction",
@ -614,6 +632,7 @@
.data64 = { 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jsle32: BPF_X",
@ -640,6 +659,7 @@
.data64 = { 0x7fffffff | 2ULL << 32, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jsle32: min/max deduction",
@ -680,6 +700,7 @@
.data64 = { 1, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jslt32: BPF_X",
@ -706,6 +727,7 @@
.data64 = { 0x7fffffff | 2ULL << 32, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jslt32: min/max deduction",

View file

@ -53,6 +53,7 @@
.data64 = { ~0ULL, }
},
},
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jset: sign-extend",
@ -70,6 +71,7 @@
.result = ACCEPT,
.retval = 2,
.data = { 1, 0, 0, 0, 0, 0, 0, 1, },
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"jset: known const compare",

View file

@ -46,6 +46,7 @@
.errstr_unpriv = "attempt to corrupt spilled",
.errstr = "R0 invalid mem access 'inv",
.result = REJECT,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"check corrupted spill/fill, LSB",

View file

@ -83,6 +83,7 @@
.result_unpriv = REJECT,
.errstr_unpriv = "",
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"spin_lock: test4 direct ld/st",
@ -112,6 +113,7 @@
.result_unpriv = REJECT,
.errstr_unpriv = "",
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"spin_lock: test5 call within a locked region",

View file

@ -512,6 +512,7 @@
.fixup_map_array_48b = { 3 },
.result = ACCEPT,
.retval = 0xabcdef12,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"map access: unknown scalar += value_ptr, 3",
@ -537,6 +538,7 @@
.result_unpriv = REJECT,
.errstr_unpriv = "R0 pointer arithmetic of map value goes out of range",
.retval = 0xabcdef12,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"map access: unknown scalar += value_ptr, 4",
@ -559,6 +561,7 @@
.result = REJECT,
.errstr = "R1 max value is outside of the array range",
.errstr_unpriv = "R1 pointer arithmetic of map value goes out of range",
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"map access: value_ptr += unknown scalar, 1",
@ -598,6 +601,7 @@
.fixup_map_array_48b = { 3 },
.result = ACCEPT,
.retval = 0xabcdef12,
.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
},
{
"map access: value_ptr += unknown scalar, 3",