Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller:
1) Support IPV6 RA Captive Portal Identifier, from Maciej Żenczykowski.
2) Use bio_vec in the networking instead of custom skb_frag_t, from
Matthew Wilcox.
3) Make use of xmit_more in r8169 driver, from Heiner Kallweit.
4) Add devmap_hash to xdp, from Toke Høiland-Jørgensen.
5) Support all variants of 5750X bnxt_en chips, from Michael Chan.
6) More RTNL avoidance work in the core and mlx5 driver, from Vlad
Buslov.
7) Add TCP syn cookies bpf helper, from Petar Penkov.
8) Add 'nettest' to selftests and use it, from David Ahern.
9) Add extack support to drop_monitor, add packet alert mode and
support for HW drops, from Ido Schimmel.
10) Add VLAN offload to stmmac, from Jose Abreu.
11) Lots of devm_platform_ioremap_resource() conversions, from
YueHaibing.
12) Add IONIC driver, from Shannon Nelson.
13) Several kTLS cleanups, from Jakub Kicinski.
* git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1930 commits)
mlxsw: spectrum_buffers: Add the ability to query the CPU port's shared buffer
mlxsw: spectrum: Register CPU port with devlink
mlxsw: spectrum_buffers: Prevent changing CPU port's configuration
net: ena: fix incorrect update of intr_delay_resolution
net: ena: fix retrieval of nonadaptive interrupt moderation intervals
net: ena: fix update of interrupt moderation register
net: ena: remove all old adaptive rx interrupt moderation code from ena_com
net: ena: remove ena_restore_ethtool_params() and relevant fields
net: ena: remove old adaptive interrupt moderation code from ena_netdev
net: ena: remove code duplication in ena_com_update_nonadaptive_moderation_interval _*()
net: ena: enable the interrupt_moderation in driver_supported_features
net: ena: reimplement set/get_coalesce()
net: ena: switch to dim algorithm for rx adaptive interrupt moderation
net: ena: add intr_moder_rx_interval to struct ena_com_dev and use it
net: phy: adin: implement Energy Detect Powerdown mode via phy-tunable
ethtool: implement Energy Detect Powerdown support via phy-tunable
xen-netfront: do not assume sk_buff_head list is empty in error handling
s390/ctcm: Delete unnecessary checks before the macro call “dev_kfree_skb”
net: ena: don't wake up tx queue when down
drop_monitor: Better sanitize notified packets
...
This commit is contained in:
4
tools/testing/selftests/bpf/.gitignore
vendored
4
tools/testing/selftests/bpf/.gitignore
vendored
@@ -39,7 +39,3 @@ libbpf.so.*
|
||||
test_hashmap
|
||||
test_btf_dump
|
||||
xdping
|
||||
test_sockopt
|
||||
test_sockopt_sk
|
||||
test_sockopt_multi
|
||||
test_tcp_rtt
|
||||
|
||||
@@ -17,6 +17,7 @@ LLC ?= llc
|
||||
LLVM_OBJCOPY ?= llvm-objcopy
|
||||
LLVM_READELF ?= llvm-readelf
|
||||
BTF_PAHOLE ?= pahole
|
||||
BPF_GCC ?= $(shell command -v bpf-gcc;)
|
||||
CFLAGS += -g -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include \
|
||||
-Dbpf_prog_load=bpf_prog_test_load \
|
||||
-Dbpf_load_program=bpf_test_load_program
|
||||
@@ -28,8 +29,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
|
||||
test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
|
||||
test_cgroup_storage test_select_reuseport test_section_names \
|
||||
test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
|
||||
test_btf_dump test_cgroup_attach xdping test_sockopt test_sockopt_sk \
|
||||
test_sockopt_multi test_tcp_rtt
|
||||
test_btf_dump test_cgroup_attach xdping
|
||||
|
||||
BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
|
||||
TEST_GEN_FILES = $(BPF_OBJ_FILES)
|
||||
@@ -47,6 +47,10 @@ ifneq ($(SUBREG_CODEGEN),)
|
||||
TEST_GEN_FILES += $(patsubst %.o,alu32/%.o, $(BPF_OBJ_FILES))
|
||||
endif
|
||||
|
||||
ifneq ($(BPF_GCC),)
|
||||
TEST_GEN_FILES += $(patsubst %.o,bpf_gcc/%.o, $(BPF_OBJ_FILES))
|
||||
endif
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_PROGS := test_kmod.sh \
|
||||
test_libbpf.sh \
|
||||
@@ -66,7 +70,8 @@ TEST_PROGS := test_kmod.sh \
|
||||
test_tcp_check_syncookie.sh \
|
||||
test_tc_tunnel.sh \
|
||||
test_tc_edt.sh \
|
||||
test_xdping.sh
|
||||
test_xdping.sh \
|
||||
test_bpftool_build.sh
|
||||
|
||||
TEST_PROGS_EXTENDED := with_addr.sh \
|
||||
with_tunnels.sh \
|
||||
@@ -105,17 +110,13 @@ $(OUTPUT)/test_socket_cookie: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sockmap: cgroup_helpers.c
|
||||
$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
|
||||
$(OUTPUT)/test_tcpnotify_user: cgroup_helpers.c trace_helpers.c
|
||||
$(OUTPUT)/test_progs: trace_helpers.c
|
||||
$(OUTPUT)/test_progs: cgroup_helpers.c trace_helpers.c
|
||||
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
|
||||
$(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
|
||||
$(OUTPUT)/test_netcnt: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sock_fields: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sysctl: cgroup_helpers.c
|
||||
$(OUTPUT)/test_cgroup_attach: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sockopt: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sockopt_sk: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sockopt_multi: cgroup_helpers.c
|
||||
$(OUTPUT)/test_tcp_rtt: cgroup_helpers.c
|
||||
|
||||
.PHONY: force
|
||||
|
||||
@@ -141,16 +142,19 @@ endif
|
||||
#
|
||||
# Use '-idirafter': Don't interfere with include mechanics except where the
|
||||
# build would have failed anyways.
|
||||
CLANG_SYS_INCLUDES := $(shell $(CLANG) -v -E - </dev/null 2>&1 \
|
||||
define get_sys_includes
|
||||
$(shell $(1) -v -E - </dev/null 2>&1 \
|
||||
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
|
||||
endef
|
||||
CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
|
||||
BPF_CFLAGS = -I. -I./include/uapi -I../../../include/uapi \
|
||||
-I$(OUTPUT)/../usr/include -D__TARGET_ARCH_$(SRCARCH)
|
||||
|
||||
CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \
|
||||
$(CLANG_SYS_INCLUDES) \
|
||||
-Wno-compare-distinct-pointer-types \
|
||||
-D__TARGET_ARCH_$(SRCARCH)
|
||||
CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
|
||||
-Wno-compare-distinct-pointer-types
|
||||
|
||||
$(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline
|
||||
$(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline
|
||||
$(OUTPUT)/test_l4lb_noinline.o: BPF_CFLAGS += -fno-inline
|
||||
$(OUTPUT)/test_xdp_noinline.o: BPF_CFLAGS += -fno-inline
|
||||
|
||||
$(OUTPUT)/test_queue_map.o: test_queue_stack_map.h
|
||||
$(OUTPUT)/test_stack_map.o: test_queue_stack_map.h
|
||||
@@ -167,12 +171,12 @@ BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
|
||||
/bin/rm -f ./llvm_btf_verify.o)
|
||||
|
||||
ifneq ($(BTF_LLVM_PROBE),)
|
||||
CLANG_FLAGS += -g
|
||||
BPF_CFLAGS += -g
|
||||
else
|
||||
ifneq ($(BTF_LLC_PROBE),)
|
||||
ifneq ($(BTF_PAHOLE_PROBE),)
|
||||
ifneq ($(BTF_OBJCOPY_PROBE),)
|
||||
CLANG_FLAGS += -g
|
||||
BPF_CFLAGS += -g
|
||||
LLC_FLAGS += -mattr=dwarfris
|
||||
DWARF2BTF = y
|
||||
endif
|
||||
@@ -198,7 +202,7 @@ $(ALU32_BUILD_DIR)/test_progs_32: test_progs.c $(OUTPUT)/libbpf.a\
|
||||
| $(ALU32_BUILD_DIR)
|
||||
$(CC) $(TEST_PROGS_CFLAGS) $(CFLAGS) \
|
||||
-o $(ALU32_BUILD_DIR)/test_progs_32 \
|
||||
test_progs.c test_stub.c trace_helpers.c prog_tests/*.c \
|
||||
test_progs.c test_stub.c cgroup_helpers.c trace_helpers.c prog_tests/*.c \
|
||||
$(OUTPUT)/libbpf.a $(LDLIBS)
|
||||
|
||||
$(ALU32_BUILD_DIR)/test_progs_32: $(PROG_TESTS_H)
|
||||
@@ -206,8 +210,8 @@ $(ALU32_BUILD_DIR)/test_progs_32: prog_tests/*.c
|
||||
|
||||
$(ALU32_BUILD_DIR)/%.o: progs/%.c $(ALU32_BUILD_DIR)/test_progs_32 \
|
||||
| $(ALU32_BUILD_DIR)
|
||||
($(CLANG) $(CLANG_FLAGS) -O2 -target bpf -emit-llvm -c $< -o - || \
|
||||
echo "clang failed") | \
|
||||
($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -target bpf -emit-llvm \
|
||||
-c $< -o - || echo "clang failed") | \
|
||||
$(LLC) -march=bpf -mattr=+alu32 -mcpu=$(CPU) $(LLC_FLAGS) \
|
||||
-filetype=obj -o $@
|
||||
ifeq ($(DWARF2BTF),y)
|
||||
@@ -215,10 +219,37 @@ ifeq ($(DWARF2BTF),y)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(BPF_GCC),)
|
||||
GCC_SYS_INCLUDES = $(call get_sys_includes,gcc)
|
||||
IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
|
||||
grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
|
||||
ifeq ($(IS_LITTLE_ENDIAN),)
|
||||
MENDIAN=-mbig-endian
|
||||
else
|
||||
MENDIAN=-mlittle-endian
|
||||
endif
|
||||
BPF_GCC_CFLAGS = $(GCC_SYS_INCLUDES) $(MENDIAN)
|
||||
BPF_GCC_BUILD_DIR = $(OUTPUT)/bpf_gcc
|
||||
TEST_CUSTOM_PROGS += $(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc
|
||||
$(BPF_GCC_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(BPF_GCC_BUILD_DIR)/urandom_read: $(OUTPUT)/urandom_read | $(BPF_GCC_BUILD_DIR)
|
||||
cp $< $@
|
||||
|
||||
$(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc: $(OUTPUT)/test_progs \
|
||||
| $(BPF_GCC_BUILD_DIR)
|
||||
cp $< $@
|
||||
|
||||
$(BPF_GCC_BUILD_DIR)/%.o: progs/%.c $(BPF_GCC_BUILD_DIR)/test_progs_bpf_gcc \
|
||||
| $(BPF_GCC_BUILD_DIR)
|
||||
$(BPF_GCC) $(BPF_CFLAGS) $(BPF_GCC_CFLAGS) -O2 -c $< -o $@
|
||||
endif
|
||||
|
||||
# Have one program compiled without "-target bpf" to test whether libbpf loads
|
||||
# it successfully
|
||||
$(OUTPUT)/test_xdp.o: progs/test_xdp.c
|
||||
($(CLANG) $(CLANG_FLAGS) -O2 -emit-llvm -c $< -o - || \
|
||||
($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -emit-llvm -c $< -o - || \
|
||||
echo "clang failed") | \
|
||||
$(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
|
||||
ifeq ($(DWARF2BTF),y)
|
||||
@@ -226,8 +257,8 @@ ifeq ($(DWARF2BTF),y)
|
||||
endif
|
||||
|
||||
$(OUTPUT)/%.o: progs/%.c
|
||||
($(CLANG) $(CLANG_FLAGS) -O2 -target bpf -emit-llvm -c $< -o - || \
|
||||
echo "clang failed") | \
|
||||
($(CLANG) $(BPF_CFLAGS) $(CLANG_CFLAGS) -O2 -target bpf -emit-llvm \
|
||||
-c $< -o - || echo "clang failed") | \
|
||||
$(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
|
||||
ifeq ($(DWARF2BTF),y)
|
||||
$(BTF_PAHOLE) -J $@
|
||||
@@ -240,18 +271,12 @@ PROG_TESTS_H := $(PROG_TESTS_DIR)/tests.h
|
||||
PROG_TESTS_FILES := $(wildcard prog_tests/*.c)
|
||||
test_progs.c: $(PROG_TESTS_H)
|
||||
$(OUTPUT)/test_progs: CFLAGS += $(TEST_PROGS_CFLAGS)
|
||||
$(OUTPUT)/test_progs: test_progs.c $(PROG_TESTS_H) $(PROG_TESTS_FILES)
|
||||
$(OUTPUT)/test_progs: test_progs.c $(PROG_TESTS_FILES) | $(PROG_TESTS_H)
|
||||
$(PROG_TESTS_H): $(PROG_TESTS_FILES) | $(PROG_TESTS_DIR)
|
||||
$(shell ( cd prog_tests/; \
|
||||
echo '/* Generated header, do not edit */'; \
|
||||
echo '#ifdef DECLARE'; \
|
||||
ls *.c 2> /dev/null | \
|
||||
sed -e 's@\([^\.]*\)\.c@extern void test_\1(void);@'; \
|
||||
echo '#endif'; \
|
||||
echo '#ifdef CALL'; \
|
||||
ls *.c 2> /dev/null | \
|
||||
sed -e 's@\([^\.]*\)\.c@test_\1();@'; \
|
||||
echo '#endif' \
|
||||
sed -e 's@\([^\.]*\)\.c@DEFINE_TEST(\1)@'; \
|
||||
) > $(PROG_TESTS_H))
|
||||
|
||||
MAP_TESTS_DIR = $(OUTPUT)/map_tests
|
||||
@@ -261,7 +286,7 @@ MAP_TESTS_H := $(MAP_TESTS_DIR)/tests.h
|
||||
MAP_TESTS_FILES := $(wildcard map_tests/*.c)
|
||||
test_maps.c: $(MAP_TESTS_H)
|
||||
$(OUTPUT)/test_maps: CFLAGS += $(TEST_MAPS_CFLAGS)
|
||||
$(OUTPUT)/test_maps: test_maps.c $(MAP_TESTS_H) $(MAP_TESTS_FILES)
|
||||
$(OUTPUT)/test_maps: test_maps.c $(MAP_TESTS_FILES) | $(MAP_TESTS_H)
|
||||
$(MAP_TESTS_H): $(MAP_TESTS_FILES) | $(MAP_TESTS_DIR)
|
||||
$(shell ( cd map_tests/; \
|
||||
echo '/* Generated header, do not edit */'; \
|
||||
@@ -282,7 +307,7 @@ VERIFIER_TESTS_H := $(VERIFIER_TESTS_DIR)/tests.h
|
||||
VERIFIER_TEST_FILES := $(wildcard verifier/*.c)
|
||||
test_verifier.c: $(VERIFIER_TESTS_H)
|
||||
$(OUTPUT)/test_verifier: CFLAGS += $(TEST_VERIFIER_CFLAGS)
|
||||
$(OUTPUT)/test_verifier: test_verifier.c $(VERIFIER_TESTS_H)
|
||||
$(OUTPUT)/test_verifier: test_verifier.c | $(VERIFIER_TEST_FILES) $(VERIFIER_TESTS_H)
|
||||
$(VERIFIER_TESTS_H): $(VERIFIER_TEST_FILES) | $(VERIFIER_TESTS_DIR)
|
||||
$(shell ( cd verifier/; \
|
||||
echo '/* Generated header, do not edit */'; \
|
||||
@@ -292,6 +317,6 @@ $(VERIFIER_TESTS_H): $(VERIFIER_TEST_FILES) | $(VERIFIER_TESTS_DIR)
|
||||
echo '#endif' \
|
||||
) > $(VERIFIER_TESTS_H))
|
||||
|
||||
EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) \
|
||||
EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(ALU32_BUILD_DIR) $(BPF_GCC_BUILD_DIR) \
|
||||
$(VERIFIER_TESTS_H) $(PROG_TESTS_H) $(MAP_TESTS_H) \
|
||||
feature
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_ENDIAN__
|
||||
#define __BPF_ENDIAN__
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
# define __bpf_htonl(x) __builtin_bswap32(x)
|
||||
# define __bpf_constant_ntohl(x) ___constant_swab32(x)
|
||||
# define __bpf_constant_htonl(x) ___constant_swab32(x)
|
||||
# define __bpf_be64_to_cpu(x) __builtin_bswap64(x)
|
||||
# define __bpf_cpu_to_be64(x) __builtin_bswap64(x)
|
||||
# define __bpf_constant_be64_to_cpu(x) ___constant_swab64(x)
|
||||
# define __bpf_constant_cpu_to_be64(x) ___constant_swab64(x)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define __bpf_ntohs(x) (x)
|
||||
# define __bpf_htons(x) (x)
|
||||
@@ -38,6 +42,10 @@
|
||||
# define __bpf_htonl(x) (x)
|
||||
# define __bpf_constant_ntohl(x) (x)
|
||||
# define __bpf_constant_htonl(x) (x)
|
||||
# define __bpf_be64_to_cpu(x) (x)
|
||||
# define __bpf_cpu_to_be64(x) (x)
|
||||
# define __bpf_constant_be64_to_cpu(x) (x)
|
||||
# define __bpf_constant_cpu_to_be64(x) (x)
|
||||
#else
|
||||
# error "Fix your compiler's __BYTE_ORDER__?!"
|
||||
#endif
|
||||
@@ -54,5 +62,11 @@
|
||||
#define bpf_ntohl(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_ntohl(x) : __bpf_ntohl(x))
|
||||
#define bpf_cpu_to_be64(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x))
|
||||
#define bpf_be64_to_cpu(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x))
|
||||
|
||||
#endif /* __BPF_ENDIAN__ */
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __BPF_HELPERS_H
|
||||
#define __BPF_HELPERS_H
|
||||
|
||||
/* helper macro to place programs, maps, license in
|
||||
* different sections in elf_bpf file. Section names
|
||||
* are interpreted by elf_bpf loader
|
||||
*/
|
||||
#define SEC(NAME) __attribute__((section(NAME), used))
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_HELPERS__
|
||||
#define __BPF_HELPERS__
|
||||
|
||||
#define __uint(name, val) int (*name)[val]
|
||||
#define __type(name, val) val *name
|
||||
@@ -19,6 +13,14 @@
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#ifdef __clang__
|
||||
|
||||
/* helper macro to place programs, maps, license in
|
||||
* different sections in elf_bpf file. Section names
|
||||
* are interpreted by elf_bpf loader
|
||||
*/
|
||||
#define SEC(NAME) __attribute__((section(NAME), used))
|
||||
|
||||
/* helper functions called from eBPF programs written in C */
|
||||
static void *(*bpf_map_lookup_elem)(void *map, const void *key) =
|
||||
(void *) BPF_FUNC_map_lookup_elem;
|
||||
@@ -228,6 +230,9 @@ static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk,
|
||||
static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) =
|
||||
(void *)BPF_FUNC_sk_storage_delete;
|
||||
static int (*bpf_send_signal)(unsigned sig) = (void *)BPF_FUNC_send_signal;
|
||||
static long long (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *ip,
|
||||
int ip_len, void *tcp, int tcp_len) =
|
||||
(void *) BPF_FUNC_tcp_gen_syncookie;
|
||||
|
||||
/* llvm builtin functions that eBPF C program may use to
|
||||
* emit BPF_LD_ABS and BPF_LD_IND instructions
|
||||
@@ -253,6 +258,12 @@ struct bpf_map_def {
|
||||
unsigned int numa_node;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <bpf-helpers.h>
|
||||
|
||||
#endif
|
||||
|
||||
#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \
|
||||
struct ____btf_map_##name { \
|
||||
type_key key; \
|
||||
@@ -501,4 +512,24 @@ struct pt_regs;
|
||||
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
|
||||
#endif
|
||||
|
||||
/*
|
||||
* BPF_CORE_READ abstracts away bpf_probe_read() call and captures offset
|
||||
* relocation for source address using __builtin_preserve_access_index()
|
||||
* built-in, provided by Clang.
|
||||
*
|
||||
* __builtin_preserve_access_index() takes as an argument an expression of
|
||||
* taking an address of a field within struct/union. It makes compiler emit
|
||||
* a relocation, which records BTF type ID describing root struct/union and an
|
||||
* accessor string which describes exact embedded field that was used to take
|
||||
* an address. See detailed description of this relocation format and
|
||||
* semantics in comments to struct bpf_offset_reloc in libbpf_internal.h.
|
||||
*
|
||||
* This relocation allows libbpf to adjust BPF instruction to use correct
|
||||
* actual field offset, based on target kernel BTF type that matches original
|
||||
* (local) BTF, used to record relocation.
|
||||
*/
|
||||
#define BPF_CORE_READ(dst, src) \
|
||||
bpf_probe_read((dst), sizeof(*(src)), \
|
||||
__builtin_preserve_access_index(src))
|
||||
|
||||
#endif
|
||||
|
||||
@@ -48,16 +48,17 @@ void test_bpf_obj_id(void)
|
||||
/* test_obj_id.o is a dumb prog. It should never fail
|
||||
* to load.
|
||||
*/
|
||||
if (err)
|
||||
error_cnt++;
|
||||
assert(!err);
|
||||
if (CHECK_FAIL(err))
|
||||
continue;
|
||||
|
||||
/* Insert a magic value to the map */
|
||||
map_fds[i] = bpf_find_map(__func__, objs[i], "test_map_id");
|
||||
assert(map_fds[i] >= 0);
|
||||
if (CHECK_FAIL(map_fds[i] < 0))
|
||||
goto done;
|
||||
err = bpf_map_update_elem(map_fds[i], &array_key,
|
||||
&array_magic_value, 0);
|
||||
assert(!err);
|
||||
if (CHECK_FAIL(err))
|
||||
goto done;
|
||||
|
||||
/* Check getting map info */
|
||||
info_len = sizeof(struct bpf_map_info) * 2;
|
||||
@@ -96,9 +97,11 @@ void test_bpf_obj_id(void)
|
||||
prog_infos[i].map_ids = ptr_to_u64(map_ids + i);
|
||||
prog_infos[i].nr_map_ids = 2;
|
||||
err = clock_gettime(CLOCK_REALTIME, &real_time_ts);
|
||||
assert(!err);
|
||||
if (CHECK_FAIL(err))
|
||||
goto done;
|
||||
err = clock_gettime(CLOCK_BOOTTIME, &boot_time_ts);
|
||||
assert(!err);
|
||||
if (CHECK_FAIL(err))
|
||||
goto done;
|
||||
err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i],
|
||||
&info_len);
|
||||
load_time = (real_time_ts.tv_sec - boot_time_ts.tv_sec)
|
||||
@@ -106,8 +109,8 @@ void test_bpf_obj_id(void)
|
||||
if (CHECK(err ||
|
||||
prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
|
||||
info_len != sizeof(struct bpf_prog_info) ||
|
||||
(jit_enabled && !prog_infos[i].jited_prog_len) ||
|
||||
(jit_enabled &&
|
||||
(env.jit_enabled && !prog_infos[i].jited_prog_len) ||
|
||||
(env.jit_enabled &&
|
||||
!memcmp(jited_insns, zeros, sizeof(zeros))) ||
|
||||
!prog_infos[i].xlated_prog_len ||
|
||||
!memcmp(xlated_insns, zeros, sizeof(zeros)) ||
|
||||
@@ -121,7 +124,7 @@ void test_bpf_obj_id(void)
|
||||
err, errno, i,
|
||||
prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
info_len, sizeof(struct bpf_prog_info),
|
||||
jit_enabled,
|
||||
env.jit_enabled,
|
||||
prog_infos[i].jited_prog_len,
|
||||
prog_infos[i].xlated_prog_len,
|
||||
!!memcmp(jited_insns, zeros, sizeof(zeros)),
|
||||
@@ -224,7 +227,8 @@ void test_bpf_obj_id(void)
|
||||
nr_id_found++;
|
||||
|
||||
err = bpf_map_lookup_elem(map_fd, &array_key, &array_value);
|
||||
assert(!err);
|
||||
if (CHECK_FAIL(err))
|
||||
goto done;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len);
|
||||
CHECK(err || info_len != sizeof(struct bpf_map_info) ||
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
static int libbpf_debug_print(enum libbpf_print_level level,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
if (level != LIBBPF_DEBUG)
|
||||
return vfprintf(stderr, format, args);
|
||||
if (level != LIBBPF_DEBUG) {
|
||||
vprintf(format, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strstr(format, "verifier log"))
|
||||
return 0;
|
||||
return vfprintf(stderr, "%s", args);
|
||||
vprintf("%s", args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_load(const char *file, enum bpf_prog_type type)
|
||||
@@ -25,19 +28,28 @@ static int check_load(const char *file, enum bpf_prog_type type)
|
||||
attr.prog_flags = BPF_F_TEST_RND_HI32;
|
||||
err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
|
||||
bpf_object__close(obj);
|
||||
if (err)
|
||||
error_cnt++;
|
||||
return err;
|
||||
}
|
||||
|
||||
struct scale_test_def {
|
||||
const char *file;
|
||||
enum bpf_prog_type attach_type;
|
||||
bool fails;
|
||||
};
|
||||
|
||||
void test_bpf_verif_scale(void)
|
||||
{
|
||||
const char *sched_cls[] = {
|
||||
"./test_verif_scale1.o", "./test_verif_scale2.o", "./test_verif_scale3.o",
|
||||
};
|
||||
const char *raw_tp[] = {
|
||||
struct scale_test_def tests[] = {
|
||||
{ "loop3.o", BPF_PROG_TYPE_RAW_TRACEPOINT, true /* fails */ },
|
||||
|
||||
{ "test_verif_scale1.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
{ "test_verif_scale2.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
{ "test_verif_scale3.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
|
||||
/* full unroll by llvm */
|
||||
"./pyperf50.o", "./pyperf100.o", "./pyperf180.o",
|
||||
{ "pyperf50.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
{ "pyperf100.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
{ "pyperf180.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
|
||||
/* partial unroll. llvm will unroll loop ~150 times.
|
||||
* C loop count -> 600.
|
||||
@@ -45,7 +57,7 @@ void test_bpf_verif_scale(void)
|
||||
* 16k insns in loop body.
|
||||
* Total of 5 such loops. Total program size ~82k insns.
|
||||
*/
|
||||
"./pyperf600.o",
|
||||
{ "pyperf600.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
|
||||
/* no unroll at all.
|
||||
* C loop count -> 600.
|
||||
@@ -53,48 +65,47 @@ void test_bpf_verif_scale(void)
|
||||
* ~110 insns in loop body.
|
||||
* Total of 5 such loops. Total program size ~1500 insns.
|
||||
*/
|
||||
"./pyperf600_nounroll.o",
|
||||
{ "pyperf600_nounroll.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
|
||||
"./loop1.o", "./loop2.o",
|
||||
{ "loop1.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
{ "loop2.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
{ "loop4.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
{ "loop5.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
|
||||
/* partial unroll. 19k insn in a loop.
|
||||
* Total program size 20.8k insn.
|
||||
* ~350k processed_insns
|
||||
*/
|
||||
"./strobemeta.o",
|
||||
{ "strobemeta.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
|
||||
/* no unroll, tiny loops */
|
||||
"./strobemeta_nounroll1.o",
|
||||
"./strobemeta_nounroll2.o",
|
||||
};
|
||||
const char *cg_sysctl[] = {
|
||||
"./test_sysctl_loop1.o", "./test_sysctl_loop2.o",
|
||||
{ "strobemeta_nounroll1.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
{ "strobemeta_nounroll2.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
|
||||
{ "test_sysctl_loop1.o", BPF_PROG_TYPE_CGROUP_SYSCTL },
|
||||
{ "test_sysctl_loop2.o", BPF_PROG_TYPE_CGROUP_SYSCTL },
|
||||
|
||||
{ "test_xdp_loop.o", BPF_PROG_TYPE_XDP },
|
||||
{ "test_seg6_loop.o", BPF_PROG_TYPE_LWT_SEG6LOCAL },
|
||||
};
|
||||
libbpf_print_fn_t old_print_fn = NULL;
|
||||
int err, i;
|
||||
|
||||
if (verifier_stats)
|
||||
libbpf_set_print(libbpf_debug_print);
|
||||
|
||||
err = check_load("./loop3.o", BPF_PROG_TYPE_RAW_TRACEPOINT);
|
||||
printf("test_scale:loop3:%s\n", err ? (error_cnt--, "OK") : "FAIL");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cls); i++) {
|
||||
err = check_load(sched_cls[i], BPF_PROG_TYPE_SCHED_CLS);
|
||||
printf("test_scale:%s:%s\n", sched_cls[i], err ? "FAIL" : "OK");
|
||||
if (env.verifier_stats) {
|
||||
test__force_log();
|
||||
old_print_fn = libbpf_set_print(libbpf_debug_print);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(raw_tp); i++) {
|
||||
err = check_load(raw_tp[i], BPF_PROG_TYPE_RAW_TRACEPOINT);
|
||||
printf("test_scale:%s:%s\n", raw_tp[i], err ? "FAIL" : "OK");
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
const struct scale_test_def *test = &tests[i];
|
||||
|
||||
if (!test__start_subtest(test->file))
|
||||
continue;
|
||||
|
||||
err = check_load(test->file, test->attach_type);
|
||||
CHECK_FAIL(err && !test->fails);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cg_sysctl); i++) {
|
||||
err = check_load(cg_sysctl[i], BPF_PROG_TYPE_CGROUP_SYSCTL);
|
||||
printf("test_scale:%s:%s\n", cg_sysctl[i], err ? "FAIL" : "OK");
|
||||
}
|
||||
err = check_load("./test_xdp_loop.o", BPF_PROG_TYPE_XDP);
|
||||
printf("test_scale:test_xdp_loop:%s\n", err ? "FAIL" : "OK");
|
||||
|
||||
err = check_load("./test_seg6_loop.o", BPF_PROG_TYPE_LWT_SEG6LOCAL);
|
||||
printf("test_scale:test_seg6_loop:%s\n", err ? "FAIL" : "OK");
|
||||
if (env.verifier_stats)
|
||||
libbpf_set_print(old_print_fn);
|
||||
}
|
||||
|
||||
385
tools/testing/selftests/bpf/prog_tests/core_reloc.c
Normal file
385
tools/testing/selftests/bpf/prog_tests/core_reloc.c
Normal file
@@ -0,0 +1,385 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
#include "progs/core_reloc_types.h"
|
||||
|
||||
#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name)
|
||||
|
||||
#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
|
||||
.a = 42, \
|
||||
.b = 0xc001, \
|
||||
.c = 0xbeef, \
|
||||
}
|
||||
|
||||
#define FLAVORS_CASE_COMMON(name) \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_flavors.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o" \
|
||||
|
||||
#define FLAVORS_CASE(name) { \
|
||||
FLAVORS_CASE_COMMON(name), \
|
||||
.input = FLAVORS_DATA(core_reloc_##name), \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = FLAVORS_DATA(core_reloc_flavors), \
|
||||
.output_len = sizeof(struct core_reloc_flavors), \
|
||||
}
|
||||
|
||||
#define FLAVORS_ERR_CASE(name) { \
|
||||
FLAVORS_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
|
||||
.a = { .a = { .a = 42 } }, \
|
||||
.b = { .b = { .b = 0xc001 } }, \
|
||||
}
|
||||
|
||||
#define NESTING_CASE_COMMON(name) \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_nesting.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o"
|
||||
|
||||
#define NESTING_CASE(name) { \
|
||||
NESTING_CASE_COMMON(name), \
|
||||
.input = NESTING_DATA(core_reloc_##name), \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = NESTING_DATA(core_reloc_nesting), \
|
||||
.output_len = sizeof(struct core_reloc_nesting) \
|
||||
}
|
||||
|
||||
#define NESTING_ERR_CASE(name) { \
|
||||
NESTING_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
|
||||
.a = { [2] = 1 }, \
|
||||
.b = { [1] = { [2] = { [3] = 2 } } }, \
|
||||
.c = { [1] = { .c = 3 } }, \
|
||||
.d = { [0] = { [0] = { .d = 4 } } }, \
|
||||
}
|
||||
|
||||
#define ARRAYS_CASE_COMMON(name) \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_arrays.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o"
|
||||
|
||||
#define ARRAYS_CASE(name) { \
|
||||
ARRAYS_CASE_COMMON(name), \
|
||||
.input = ARRAYS_DATA(core_reloc_##name), \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \
|
||||
.a2 = 1, \
|
||||
.b123 = 2, \
|
||||
.c1c = 3, \
|
||||
.d00d = 4, \
|
||||
}, \
|
||||
.output_len = sizeof(struct core_reloc_arrays_output) \
|
||||
}
|
||||
|
||||
#define ARRAYS_ERR_CASE(name) { \
|
||||
ARRAYS_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
|
||||
.a = 1, \
|
||||
.b = 2, \
|
||||
.c = 3, \
|
||||
.d = (void *)4, \
|
||||
.f = (void *)5, \
|
||||
}
|
||||
|
||||
#define PRIMITIVES_CASE_COMMON(name) \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_primitives.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o"
|
||||
|
||||
#define PRIMITIVES_CASE(name) { \
|
||||
PRIMITIVES_CASE_COMMON(name), \
|
||||
.input = PRIMITIVES_DATA(core_reloc_##name), \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = PRIMITIVES_DATA(core_reloc_primitives), \
|
||||
.output_len = sizeof(struct core_reloc_primitives), \
|
||||
}
|
||||
|
||||
#define PRIMITIVES_ERR_CASE(name) { \
|
||||
PRIMITIVES_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
#define MODS_CASE(name) { \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_mods.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o", \
|
||||
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \
|
||||
.a = 1, \
|
||||
.b = 2, \
|
||||
.c = (void *)3, \
|
||||
.d = (void *)4, \
|
||||
.e = { [2] = 5 }, \
|
||||
.f = { [1] = 6 }, \
|
||||
.g = { .x = 7 }, \
|
||||
.h = { .y = 8 }, \
|
||||
}, \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \
|
||||
.a = 1, .b = 2, .c = 3, .d = 4, \
|
||||
.e = 5, .f = 6, .g = 7, .h = 8, \
|
||||
}, \
|
||||
.output_len = sizeof(struct core_reloc_mods_output), \
|
||||
}
|
||||
|
||||
#define PTR_AS_ARR_CASE(name) { \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o", \
|
||||
.input = (const char *)&(struct core_reloc_##name []){ \
|
||||
{ .a = 1 }, \
|
||||
{ .a = 2 }, \
|
||||
{ .a = 3 }, \
|
||||
}, \
|
||||
.input_len = 3 * sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \
|
||||
.a = 3, \
|
||||
}, \
|
||||
.output_len = sizeof(struct core_reloc_ptr_as_arr), \
|
||||
}
|
||||
|
||||
#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
|
||||
.u8_field = 1, \
|
||||
.s8_field = 2, \
|
||||
.u16_field = 3, \
|
||||
.s16_field = 4, \
|
||||
.u32_field = 5, \
|
||||
.s32_field = 6, \
|
||||
.u64_field = 7, \
|
||||
.s64_field = 8, \
|
||||
}
|
||||
|
||||
#define INTS_CASE_COMMON(name) \
|
||||
.case_name = #name, \
|
||||
.bpf_obj_file = "test_core_reloc_ints.o", \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o"
|
||||
|
||||
#define INTS_CASE(name) { \
|
||||
INTS_CASE_COMMON(name), \
|
||||
.input = INTS_DATA(core_reloc_##name), \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = INTS_DATA(core_reloc_ints), \
|
||||
.output_len = sizeof(struct core_reloc_ints), \
|
||||
}
|
||||
|
||||
#define INTS_ERR_CASE(name) { \
|
||||
INTS_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
struct core_reloc_test_case {
|
||||
const char *case_name;
|
||||
const char *bpf_obj_file;
|
||||
const char *btf_src_file;
|
||||
const char *input;
|
||||
int input_len;
|
||||
const char *output;
|
||||
int output_len;
|
||||
bool fails;
|
||||
};
|
||||
|
||||
static struct core_reloc_test_case test_cases[] = {
|
||||
/* validate we can find kernel image and use its BTF for relocs */
|
||||
{
|
||||
.case_name = "kernel",
|
||||
.bpf_obj_file = "test_core_reloc_kernel.o",
|
||||
.btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */
|
||||
.input = "",
|
||||
.input_len = 0,
|
||||
.output = "\1", /* true */
|
||||
.output_len = 1,
|
||||
},
|
||||
|
||||
/* validate BPF program can use multiple flavors to match against
|
||||
* single target BTF type
|
||||
*/
|
||||
FLAVORS_CASE(flavors),
|
||||
|
||||
FLAVORS_ERR_CASE(flavors__err_wrong_name),
|
||||
|
||||
/* various struct/enum nesting and resolution scenarios */
|
||||
NESTING_CASE(nesting),
|
||||
NESTING_CASE(nesting___anon_embed),
|
||||
NESTING_CASE(nesting___struct_union_mixup),
|
||||
NESTING_CASE(nesting___extra_nesting),
|
||||
NESTING_CASE(nesting___dup_compat_types),
|
||||
|
||||
NESTING_ERR_CASE(nesting___err_missing_field),
|
||||
NESTING_ERR_CASE(nesting___err_array_field),
|
||||
NESTING_ERR_CASE(nesting___err_missing_container),
|
||||
NESTING_ERR_CASE(nesting___err_nonstruct_container),
|
||||
NESTING_ERR_CASE(nesting___err_array_container),
|
||||
NESTING_ERR_CASE(nesting___err_dup_incompat_types),
|
||||
NESTING_ERR_CASE(nesting___err_partial_match_dups),
|
||||
NESTING_ERR_CASE(nesting___err_too_deep),
|
||||
|
||||
/* various array access relocation scenarios */
|
||||
ARRAYS_CASE(arrays),
|
||||
ARRAYS_CASE(arrays___diff_arr_dim),
|
||||
ARRAYS_CASE(arrays___diff_arr_val_sz),
|
||||
|
||||
ARRAYS_ERR_CASE(arrays___err_too_small),
|
||||
ARRAYS_ERR_CASE(arrays___err_too_shallow),
|
||||
ARRAYS_ERR_CASE(arrays___err_non_array),
|
||||
ARRAYS_ERR_CASE(arrays___err_wrong_val_type1),
|
||||
ARRAYS_ERR_CASE(arrays___err_wrong_val_type2),
|
||||
|
||||
/* enum/ptr/int handling scenarios */
|
||||
PRIMITIVES_CASE(primitives),
|
||||
PRIMITIVES_CASE(primitives___diff_enum_def),
|
||||
PRIMITIVES_CASE(primitives___diff_func_proto),
|
||||
PRIMITIVES_CASE(primitives___diff_ptr_type),
|
||||
|
||||
PRIMITIVES_ERR_CASE(primitives___err_non_enum),
|
||||
PRIMITIVES_ERR_CASE(primitives___err_non_int),
|
||||
PRIMITIVES_ERR_CASE(primitives___err_non_ptr),
|
||||
|
||||
/* const/volatile/restrict and typedefs scenarios */
|
||||
MODS_CASE(mods),
|
||||
MODS_CASE(mods___mod_swap),
|
||||
MODS_CASE(mods___typedefs),
|
||||
|
||||
/* handling "ptr is an array" semantics */
|
||||
PTR_AS_ARR_CASE(ptr_as_arr),
|
||||
PTR_AS_ARR_CASE(ptr_as_arr___diff_sz),
|
||||
|
||||
/* int signedness/sizing/bitfield handling */
|
||||
INTS_CASE(ints),
|
||||
INTS_CASE(ints___bool),
|
||||
INTS_CASE(ints___reverse_sign),
|
||||
|
||||
INTS_ERR_CASE(ints___err_bitfield),
|
||||
INTS_ERR_CASE(ints___err_wrong_sz_8),
|
||||
INTS_ERR_CASE(ints___err_wrong_sz_16),
|
||||
INTS_ERR_CASE(ints___err_wrong_sz_32),
|
||||
INTS_ERR_CASE(ints___err_wrong_sz_64),
|
||||
|
||||
/* validate edge cases of capturing relocations */
|
||||
{
|
||||
.case_name = "misc",
|
||||
.bpf_obj_file = "test_core_reloc_misc.o",
|
||||
.btf_src_file = "btf__core_reloc_misc.o",
|
||||
.input = (const char *)&(struct core_reloc_misc_extensible[]){
|
||||
{ .a = 1 },
|
||||
{ .a = 2 }, /* not read */
|
||||
{ .a = 3 },
|
||||
},
|
||||
.input_len = 4 * sizeof(int),
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) {
|
||||
.a = 1,
|
||||
.b = 1,
|
||||
.c = 0, /* BUG in clang, should be 3 */
|
||||
},
|
||||
.output_len = sizeof(struct core_reloc_misc_output),
|
||||
},
|
||||
};
|
||||
|
||||
struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
};
|
||||
|
||||
void test_core_reloc(void)
|
||||
{
|
||||
const char *probe_name = "raw_tracepoint/sys_enter";
|
||||
struct bpf_object_load_attr load_attr = {};
|
||||
struct core_reloc_test_case *test_case;
|
||||
int err, duration = 0, i, equal;
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_map *data_map;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
const int zero = 0;
|
||||
struct data data;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
|
||||
test_case = &test_cases[i];
|
||||
|
||||
if (!test__start_subtest(test_case->case_name))
|
||||
continue;
|
||||
|
||||
obj = bpf_object__open(test_case->bpf_obj_file);
|
||||
if (CHECK(IS_ERR_OR_NULL(obj), "obj_open",
|
||||
"failed to open '%s': %ld\n",
|
||||
test_case->bpf_obj_file, PTR_ERR(obj)))
|
||||
continue;
|
||||
|
||||
prog = bpf_object__find_program_by_title(obj, probe_name);
|
||||
if (CHECK(!prog, "find_probe",
|
||||
"prog '%s' not found\n", probe_name))
|
||||
goto cleanup;
|
||||
bpf_program__set_type(prog, BPF_PROG_TYPE_RAW_TRACEPOINT);
|
||||
|
||||
load_attr.obj = obj;
|
||||
load_attr.log_level = 0;
|
||||
load_attr.target_btf_path = test_case->btf_src_file;
|
||||
err = bpf_object__load_xattr(&load_attr);
|
||||
if (test_case->fails) {
|
||||
CHECK(!err, "obj_load_fail",
|
||||
"should fail to load prog '%s'\n", probe_name);
|
||||
goto cleanup;
|
||||
} else {
|
||||
if (CHECK(err, "obj_load",
|
||||
"failed to load prog '%s': %d\n",
|
||||
probe_name, err))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
|
||||
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
|
||||
PTR_ERR(link)))
|
||||
goto cleanup;
|
||||
|
||||
data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
|
||||
if (CHECK(!data_map, "find_data_map", "data map not found\n"))
|
||||
goto cleanup;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
memcpy(data.in, test_case->input, test_case->input_len);
|
||||
|
||||
err = bpf_map_update_elem(bpf_map__fd(data_map),
|
||||
&zero, &data, 0);
|
||||
if (CHECK(err, "update_data_map",
|
||||
"failed to update .data map: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
/* trigger test run */
|
||||
usleep(1);
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &data);
|
||||
if (CHECK(err, "get_result",
|
||||
"failed to get output data: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
equal = memcmp(data.out, test_case->output,
|
||||
test_case->output_len) == 0;
|
||||
if (CHECK(!equal, "check_result",
|
||||
"input/output data don't match\n")) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < test_case->input_len; j++) {
|
||||
printf("input byte #%d: 0x%02hhx\n",
|
||||
j, test_case->input[j]);
|
||||
}
|
||||
for (j = 0; j < test_case->output_len; j++) {
|
||||
printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n",
|
||||
j, test_case->output[j], data.out[j]);
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (!IS_ERR_OR_NULL(link)) {
|
||||
bpf_link__destroy(link);
|
||||
link = NULL;
|
||||
}
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,10 @@
|
||||
#include <linux/if_tun.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#ifndef IP_MF
|
||||
#define IP_MF 0x2000
|
||||
#endif
|
||||
|
||||
#define CHECK_FLOW_KEYS(desc, got, expected) \
|
||||
CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \
|
||||
desc, \
|
||||
@@ -16,6 +20,7 @@
|
||||
"is_encap=%u/%u " \
|
||||
"ip_proto=0x%x/0x%x " \
|
||||
"n_proto=0x%x/0x%x " \
|
||||
"flow_label=0x%x/0x%x " \
|
||||
"sport=%u/%u " \
|
||||
"dport=%u/%u\n", \
|
||||
got.nhoff, expected.nhoff, \
|
||||
@@ -26,6 +31,7 @@
|
||||
got.is_encap, expected.is_encap, \
|
||||
got.ip_proto, expected.ip_proto, \
|
||||
got.n_proto, expected.n_proto, \
|
||||
got.flow_label, expected.flow_label, \
|
||||
got.sport, expected.sport, \
|
||||
got.dport, expected.dport)
|
||||
|
||||
@@ -35,6 +41,13 @@ struct ipv4_pkt {
|
||||
struct tcphdr tcp;
|
||||
} __packed;
|
||||
|
||||
struct ipip_pkt {
|
||||
struct ethhdr eth;
|
||||
struct iphdr iph;
|
||||
struct iphdr iph_inner;
|
||||
struct tcphdr tcp;
|
||||
} __packed;
|
||||
|
||||
struct svlan_ipv4_pkt {
|
||||
struct ethhdr eth;
|
||||
__u16 vlan_tci;
|
||||
@@ -49,6 +62,18 @@ struct ipv6_pkt {
|
||||
struct tcphdr tcp;
|
||||
} __packed;
|
||||
|
||||
struct ipv6_frag_pkt {
|
||||
struct ethhdr eth;
|
||||
struct ipv6hdr iph;
|
||||
struct frag_hdr {
|
||||
__u8 nexthdr;
|
||||
__u8 reserved;
|
||||
__be16 frag_off;
|
||||
__be32 identification;
|
||||
} ipf;
|
||||
struct tcphdr tcp;
|
||||
} __packed;
|
||||
|
||||
struct dvlan_ipv6_pkt {
|
||||
struct ethhdr eth;
|
||||
__u16 vlan_tci;
|
||||
@@ -64,10 +89,13 @@ struct test {
|
||||
union {
|
||||
struct ipv4_pkt ipv4;
|
||||
struct svlan_ipv4_pkt svlan_ipv4;
|
||||
struct ipip_pkt ipip;
|
||||
struct ipv6_pkt ipv6;
|
||||
struct ipv6_frag_pkt ipv6_frag;
|
||||
struct dvlan_ipv6_pkt dvlan_ipv6;
|
||||
} pkt;
|
||||
struct bpf_flow_keys keys;
|
||||
__u32 flags;
|
||||
};
|
||||
|
||||
#define VLAN_HLEN 4
|
||||
@@ -81,6 +109,8 @@ struct test tests[] = {
|
||||
.iph.protocol = IPPROTO_TCP,
|
||||
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN,
|
||||
@@ -88,6 +118,8 @@ struct test tests[] = {
|
||||
.addr_proto = ETH_P_IP,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -97,6 +129,8 @@ struct test tests[] = {
|
||||
.iph.nexthdr = IPPROTO_TCP,
|
||||
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN,
|
||||
@@ -104,6 +138,8 @@ struct test tests[] = {
|
||||
.addr_proto = ETH_P_IPV6,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -115,6 +151,8 @@ struct test tests[] = {
|
||||
.iph.protocol = IPPROTO_TCP,
|
||||
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN + VLAN_HLEN,
|
||||
@@ -122,6 +160,8 @@ struct test tests[] = {
|
||||
.addr_proto = ETH_P_IP,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -133,6 +173,8 @@ struct test tests[] = {
|
||||
.iph.nexthdr = IPPROTO_TCP,
|
||||
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN + VLAN_HLEN * 2,
|
||||
@@ -141,8 +183,205 @@ struct test tests[] = {
|
||||
.addr_proto = ETH_P_IPV6,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "ipv4-frag",
|
||||
.pkt.ipv4 = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.iph.ihl = 5,
|
||||
.iph.protocol = IPPROTO_TCP,
|
||||
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.iph.frag_off = __bpf_constant_htons(IP_MF),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct iphdr),
|
||||
.addr_proto = ETH_P_IP,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.is_frag = true,
|
||||
.is_first_frag = true,
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
|
||||
},
|
||||
{
|
||||
.name = "ipv4-no-frag",
|
||||
.pkt.ipv4 = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.iph.ihl = 5,
|
||||
.iph.protocol = IPPROTO_TCP,
|
||||
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.iph.frag_off = __bpf_constant_htons(IP_MF),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct iphdr),
|
||||
.addr_proto = ETH_P_IP,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.is_frag = true,
|
||||
.is_first_frag = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "ipv6-frag",
|
||||
.pkt.ipv6_frag = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.iph.nexthdr = IPPROTO_FRAGMENT,
|
||||
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.ipf.nexthdr = IPPROTO_TCP,
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
|
||||
sizeof(struct frag_hdr),
|
||||
.addr_proto = ETH_P_IPV6,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.is_frag = true,
|
||||
.is_first_frag = true,
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
|
||||
},
|
||||
{
|
||||
.name = "ipv6-no-frag",
|
||||
.pkt.ipv6_frag = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.iph.nexthdr = IPPROTO_FRAGMENT,
|
||||
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.ipf.nexthdr = IPPROTO_TCP,
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
|
||||
sizeof(struct frag_hdr),
|
||||
.addr_proto = ETH_P_IPV6,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.is_frag = true,
|
||||
.is_first_frag = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "ipv6-flow-label",
|
||||
.pkt.ipv6 = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.iph.nexthdr = IPPROTO_TCP,
|
||||
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.iph.flow_lbl = { 0xb, 0xee, 0xef },
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
|
||||
.addr_proto = ETH_P_IPV6,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
.flow_label = __bpf_constant_htonl(0xbeeef),
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "ipv6-no-flow-label",
|
||||
.pkt.ipv6 = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.iph.nexthdr = IPPROTO_TCP,
|
||||
.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.iph.flow_lbl = { 0xb, 0xee, 0xef },
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
|
||||
.addr_proto = ETH_P_IPV6,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IPV6),
|
||||
.flow_label = __bpf_constant_htonl(0xbeeef),
|
||||
},
|
||||
.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
|
||||
},
|
||||
{
|
||||
.name = "ipip-encap",
|
||||
.pkt.ipip = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.iph.ihl = 5,
|
||||
.iph.protocol = IPPROTO_IPIP,
|
||||
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.iph_inner.ihl = 5,
|
||||
.iph_inner.protocol = IPPROTO_TCP,
|
||||
.iph_inner.tot_len =
|
||||
__bpf_constant_htons(MAGIC_BYTES) -
|
||||
sizeof(struct iphdr),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct iphdr) +
|
||||
sizeof(struct iphdr),
|
||||
.addr_proto = ETH_P_IP,
|
||||
.ip_proto = IPPROTO_TCP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.is_encap = true,
|
||||
.sport = 80,
|
||||
.dport = 8080,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "ipip-no-encap",
|
||||
.pkt.ipip = {
|
||||
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.iph.ihl = 5,
|
||||
.iph.protocol = IPPROTO_IPIP,
|
||||
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
|
||||
.iph_inner.ihl = 5,
|
||||
.iph_inner.protocol = IPPROTO_TCP,
|
||||
.iph_inner.tot_len =
|
||||
__bpf_constant_htons(MAGIC_BYTES) -
|
||||
sizeof(struct iphdr),
|
||||
.tcp.doff = 5,
|
||||
.tcp.source = 80,
|
||||
.tcp.dest = 8080,
|
||||
},
|
||||
.keys = {
|
||||
.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
|
||||
.nhoff = ETH_HLEN,
|
||||
.thoff = ETH_HLEN + sizeof(struct iphdr),
|
||||
.addr_proto = ETH_P_IP,
|
||||
.ip_proto = IPPROTO_IPIP,
|
||||
.n_proto = __bpf_constant_htons(ETH_P_IP),
|
||||
.is_encap = true,
|
||||
},
|
||||
.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
|
||||
},
|
||||
};
|
||||
|
||||
static int create_tap(const char *ifname)
|
||||
@@ -212,10 +451,8 @@ void test_flow_dissector(void)
|
||||
|
||||
err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
|
||||
"jmp_table", "last_dissection", &prog_fd, &keys_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
struct bpf_flow_keys flow_keys;
|
||||
@@ -225,6 +462,13 @@ void test_flow_dissector(void)
|
||||
.data_size_in = sizeof(tests[i].pkt),
|
||||
.data_out = &flow_keys,
|
||||
};
|
||||
static struct bpf_flow_keys ctx = {};
|
||||
|
||||
if (tests[i].flags) {
|
||||
tattr.ctx_in = &ctx;
|
||||
tattr.ctx_size_in = sizeof(ctx);
|
||||
ctx.flags = tests[i].flags;
|
||||
}
|
||||
|
||||
err = bpf_prog_test_run_xattr(&tattr);
|
||||
CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
|
||||
@@ -251,9 +495,20 @@ void test_flow_dissector(void)
|
||||
CHECK(err, "ifup", "err %d errno %d\n", err, errno);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
struct bpf_flow_keys flow_keys = {};
|
||||
/* Keep in sync with 'flags' from eth_get_headlen. */
|
||||
__u32 eth_get_headlen_flags =
|
||||
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
|
||||
struct bpf_prog_test_run_attr tattr = {};
|
||||
__u32 key = 0;
|
||||
struct bpf_flow_keys flow_keys = {};
|
||||
__u32 key = (__u32)(tests[i].keys.sport) << 16 |
|
||||
tests[i].keys.dport;
|
||||
|
||||
/* For skb-less case we can't pass input flags; run
|
||||
* only the tests that have a matching set of flags.
|
||||
*/
|
||||
|
||||
if (tests[i].flags != eth_get_headlen_flags)
|
||||
continue;
|
||||
|
||||
err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
|
||||
CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
|
||||
@@ -263,6 +518,9 @@ void test_flow_dissector(void)
|
||||
|
||||
CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
|
||||
CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
|
||||
|
||||
err = bpf_map_delete_elem(keys_fd, &key);
|
||||
CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
|
||||
}
|
||||
|
||||
bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/socket.h>
|
||||
#include <test_progs.h>
|
||||
|
||||
#define MAX_CNT_RAWTP 10ull
|
||||
#define MAX_STACK_RAWTP 100
|
||||
|
||||
static int duration = 0;
|
||||
|
||||
struct get_stack_trace_t {
|
||||
int pid;
|
||||
int kern_stack_size;
|
||||
@@ -13,7 +20,7 @@ struct get_stack_trace_t {
|
||||
struct bpf_stack_build_id user_stack_buildid[MAX_STACK_RAWTP];
|
||||
};
|
||||
|
||||
static int get_stack_print_output(void *data, int size)
|
||||
static void get_stack_print_output(void *ctx, int cpu, void *data, __u32 size)
|
||||
{
|
||||
bool good_kern_stack = false, good_user_stack = false;
|
||||
const char *nonjit_func = "___bpf_prog_run";
|
||||
@@ -34,7 +41,7 @@ static int get_stack_print_output(void *data, int size)
|
||||
* just assume it is good if the stack is not empty.
|
||||
* This could be improved in the future.
|
||||
*/
|
||||
if (jit_enabled) {
|
||||
if (env.jit_enabled) {
|
||||
found = num_stack > 0;
|
||||
} else {
|
||||
for (i = 0; i < num_stack; i++) {
|
||||
@@ -51,7 +58,7 @@ static int get_stack_print_output(void *data, int size)
|
||||
}
|
||||
} else {
|
||||
num_stack = e->kern_stack_size / sizeof(__u64);
|
||||
if (jit_enabled) {
|
||||
if (env.jit_enabled) {
|
||||
good_kern_stack = num_stack > 0;
|
||||
} else {
|
||||
for (i = 0; i < num_stack; i++) {
|
||||
@@ -65,75 +72,73 @@ static int get_stack_print_output(void *data, int size)
|
||||
if (e->user_stack_size > 0 && e->user_stack_buildid_size > 0)
|
||||
good_user_stack = true;
|
||||
}
|
||||
if (!good_kern_stack || !good_user_stack)
|
||||
return LIBBPF_PERF_EVENT_ERROR;
|
||||
|
||||
if (cnt == MAX_CNT_RAWTP)
|
||||
return LIBBPF_PERF_EVENT_DONE;
|
||||
|
||||
return LIBBPF_PERF_EVENT_CONT;
|
||||
if (!good_kern_stack)
|
||||
CHECK(!good_kern_stack, "kern_stack", "corrupted kernel stack\n");
|
||||
if (!good_user_stack)
|
||||
CHECK(!good_user_stack, "user_stack", "corrupted user stack\n");
|
||||
}
|
||||
|
||||
void test_get_stack_raw_tp(void)
|
||||
{
|
||||
const char *file = "./test_get_stack_rawtp.o";
|
||||
int i, efd, err, prog_fd, pmu_fd, perfmap_fd;
|
||||
struct perf_event_attr attr = {};
|
||||
const char *prog_name = "raw_tracepoint/sys_enter";
|
||||
int i, err, prog_fd, exp_cnt = MAX_CNT_RAWTP;
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
struct perf_buffer *pb = NULL;
|
||||
struct bpf_link *link = NULL;
|
||||
struct timespec tv = {0, 10};
|
||||
__u32 key = 0, duration = 0;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
struct bpf_map *map;
|
||||
cpu_set_t cpu_set;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
||||
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
||||
return;
|
||||
|
||||
efd = bpf_raw_tracepoint_open("sys_enter", prog_fd);
|
||||
if (CHECK(efd < 0, "raw_tp_open", "err %d errno %d\n", efd, errno))
|
||||
prog = bpf_object__find_program_by_title(obj, prog_name);
|
||||
if (CHECK(!prog, "find_probe", "prog '%s' not found\n", prog_name))
|
||||
goto close_prog;
|
||||
|
||||
perfmap_fd = bpf_find_map(__func__, obj, "perfmap");
|
||||
if (CHECK(perfmap_fd < 0, "bpf_find_map", "err %d errno %d\n",
|
||||
perfmap_fd, errno))
|
||||
map = bpf_object__find_map_by_name(obj, "perfmap");
|
||||
if (CHECK(!map, "bpf_find_map", "not found\n"))
|
||||
goto close_prog;
|
||||
|
||||
err = load_kallsyms();
|
||||
if (CHECK(err < 0, "load_kallsyms", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
|
||||
attr.sample_type = PERF_SAMPLE_RAW;
|
||||
attr.type = PERF_TYPE_SOFTWARE;
|
||||
attr.config = PERF_COUNT_SW_BPF_OUTPUT;
|
||||
pmu_fd = syscall(__NR_perf_event_open, &attr, getpid()/*pid*/, -1/*cpu*/,
|
||||
-1/*group_fd*/, 0);
|
||||
if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", pmu_fd,
|
||||
errno))
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(0, &cpu_set);
|
||||
err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
|
||||
if (CHECK(err, "set_affinity", "err %d, errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
|
||||
err = bpf_map_update_elem(perfmap_fd, &key, &pmu_fd, BPF_ANY);
|
||||
if (CHECK(err < 0, "bpf_map_update_elem", "err %d errno %d\n", err,
|
||||
errno))
|
||||
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
|
||||
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
|
||||
goto close_prog;
|
||||
|
||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (CHECK(err < 0, "ioctl PERF_EVENT_IOC_ENABLE", "err %d errno %d\n",
|
||||
err, errno))
|
||||
goto close_prog;
|
||||
|
||||
err = perf_event_mmap(pmu_fd);
|
||||
if (CHECK(err < 0, "perf_event_mmap", "err %d errno %d\n", err, errno))
|
||||
pb_opts.sample_cb = get_stack_print_output;
|
||||
pb = perf_buffer__new(bpf_map__fd(map), 8, &pb_opts);
|
||||
if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb)))
|
||||
goto close_prog;
|
||||
|
||||
/* trigger some syscall action */
|
||||
for (i = 0; i < MAX_CNT_RAWTP; i++)
|
||||
nanosleep(&tv, NULL);
|
||||
|
||||
err = perf_event_poller(pmu_fd, get_stack_print_output);
|
||||
if (CHECK(err < 0, "perf_event_poller", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
while (exp_cnt > 0) {
|
||||
err = perf_buffer__poll(pb, 100);
|
||||
if (err < 0 && CHECK(err < 0, "pb__poll", "err %d\n", err))
|
||||
goto close_prog;
|
||||
exp_cnt -= err;
|
||||
}
|
||||
|
||||
goto close_prog_noerr;
|
||||
close_prog:
|
||||
error_cnt++;
|
||||
close_prog_noerr:
|
||||
if (!IS_ERR_OR_NULL(link))
|
||||
bpf_link__destroy(link);
|
||||
if (!IS_ERR_OR_NULL(pb))
|
||||
perf_buffer__free(pb);
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,8 @@ static void test_global_data_number(struct bpf_object *obj, __u32 duration)
|
||||
uint64_t num;
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "result_number");
|
||||
if (map_fd < 0) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(map_fd < 0))
|
||||
return;
|
||||
}
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
@@ -44,10 +42,8 @@ static void test_global_data_string(struct bpf_object *obj, __u32 duration)
|
||||
char str[32];
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "result_string");
|
||||
if (map_fd < 0) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(map_fd < 0))
|
||||
return;
|
||||
}
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
@@ -81,10 +77,8 @@ static void test_global_data_struct(struct bpf_object *obj, __u32 duration)
|
||||
struct foo val;
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "result_struct");
|
||||
if (map_fd < 0) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(map_fd < 0))
|
||||
return;
|
||||
}
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
@@ -112,16 +106,12 @@ static void test_global_data_rdonly(struct bpf_object *obj, __u32 duration)
|
||||
__u8 *buff;
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, "test_glo.rodata");
|
||||
if (!map || !bpf_map__is_internal(map)) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(!map || !bpf_map__is_internal(map)))
|
||||
return;
|
||||
}
|
||||
|
||||
map_fd = bpf_map__fd(map);
|
||||
if (map_fd < 0) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(map_fd < 0))
|
||||
return;
|
||||
}
|
||||
|
||||
buff = malloc(bpf_map__def(map)->value_size);
|
||||
if (buff)
|
||||
|
||||
@@ -30,10 +30,8 @@ static void test_l4lb(const char *file)
|
||||
u32 *magic = (u32 *)buf;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "vip_map");
|
||||
if (map_fd < 0)
|
||||
@@ -72,10 +70,9 @@ static void test_l4lb(const char *file)
|
||||
bytes += stats[i].bytes;
|
||||
pkts += stats[i].pkts;
|
||||
}
|
||||
if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(bytes != MAGIC_BYTES * NUM_ITER * 2 ||
|
||||
pkts != NUM_ITER * 2))
|
||||
printf("test_l4lb:FAIL:stats %lld %lld\n", bytes, pkts);
|
||||
}
|
||||
out:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ static void *parallel_map_access(void *arg)
|
||||
|
||||
for (i = 0; i < 10000; i++) {
|
||||
err = bpf_map_lookup_elem_flags(map_fd, &key, vars, BPF_F_LOCK);
|
||||
if (err) {
|
||||
if (CHECK_FAIL(err)) {
|
||||
printf("lookup failed\n");
|
||||
error_cnt++;
|
||||
goto out;
|
||||
}
|
||||
if (vars[0] != 0) {
|
||||
if (CHECK_FAIL(vars[0] != 0)) {
|
||||
printf("lookup #%d var[0]=%d\n", i, vars[0]);
|
||||
error_cnt++;
|
||||
goto out;
|
||||
}
|
||||
rnd = vars[1];
|
||||
@@ -24,7 +22,7 @@ static void *parallel_map_access(void *arg)
|
||||
continue;
|
||||
printf("lookup #%d var[1]=%d var[%d]=%d\n",
|
||||
i, rnd, j, vars[j]);
|
||||
error_cnt++;
|
||||
CHECK_FAIL(vars[j] != rnd);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -42,34 +40,36 @@ void test_map_lock(void)
|
||||
void *ret;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
|
||||
if (err) {
|
||||
if (CHECK_FAIL(err)) {
|
||||
printf("test_map_lock:bpf_prog_load errno %d\n", errno);
|
||||
goto close_prog;
|
||||
}
|
||||
map_fd[0] = bpf_find_map(__func__, obj, "hash_map");
|
||||
if (map_fd[0] < 0)
|
||||
if (CHECK_FAIL(map_fd[0] < 0))
|
||||
goto close_prog;
|
||||
map_fd[1] = bpf_find_map(__func__, obj, "array_map");
|
||||
if (map_fd[1] < 0)
|
||||
if (CHECK_FAIL(map_fd[1] < 0))
|
||||
goto close_prog;
|
||||
|
||||
bpf_map_update_elem(map_fd[0], &key, vars, BPF_F_LOCK);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
assert(pthread_create(&thread_id[i], NULL,
|
||||
&spin_lock_thread, &prog_fd) == 0);
|
||||
if (CHECK_FAIL(pthread_create(&thread_id[i], NULL,
|
||||
&spin_lock_thread, &prog_fd)))
|
||||
goto close_prog;
|
||||
for (i = 4; i < 6; i++)
|
||||
assert(pthread_create(&thread_id[i], NULL,
|
||||
¶llel_map_access, &map_fd[i - 4]) == 0);
|
||||
if (CHECK_FAIL(pthread_create(&thread_id[i], NULL,
|
||||
¶llel_map_access,
|
||||
&map_fd[i - 4])))
|
||||
goto close_prog;
|
||||
for (i = 0; i < 4; i++)
|
||||
assert(pthread_join(thread_id[i], &ret) == 0 &&
|
||||
ret == (void *)&prog_fd);
|
||||
if (CHECK_FAIL(pthread_join(thread_id[i], &ret) ||
|
||||
ret != (void *)&prog_fd))
|
||||
goto close_prog;
|
||||
for (i = 4; i < 6; i++)
|
||||
assert(pthread_join(thread_id[i], &ret) == 0 &&
|
||||
ret == (void *)&map_fd[i - 4]);
|
||||
goto close_prog_noerr;
|
||||
if (CHECK_FAIL(pthread_join(thread_id[i], &ret) ||
|
||||
ret != (void *)&map_fd[i - 4]))
|
||||
goto close_prog;
|
||||
close_prog:
|
||||
error_cnt++;
|
||||
close_prog_noerr:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,8 @@ void test_pkt_access(void)
|
||||
int err, prog_fd;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4),
|
||||
NULL, NULL, &retval, &duration);
|
||||
|
||||
@@ -9,10 +9,8 @@ void test_pkt_md_access(void)
|
||||
int err, prog_fd;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4),
|
||||
NULL, NULL, &retval, &duration);
|
||||
|
||||
@@ -27,10 +27,8 @@ static void test_queue_stack_map_by_type(int type)
|
||||
return;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
map_in_fd = bpf_find_map(__func__, obj, "map_in");
|
||||
if (map_in_fd < 0)
|
||||
@@ -43,10 +41,8 @@ static void test_queue_stack_map_by_type(int type)
|
||||
/* Push 32 elements to the input map */
|
||||
for (i = 0; i < MAP_SIZE; i++) {
|
||||
err = bpf_map_update_elem(map_in_fd, NULL, &vals[i], 0);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* The eBPF program pushes iph.saddr in the output map,
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void test_reference_tracking(void)
|
||||
{
|
||||
const char *file = "./test_sk_lookup_kern.o";
|
||||
@@ -19,10 +10,8 @@ void test_reference_tracking(void)
|
||||
int err = 0;
|
||||
|
||||
obj = bpf_object__open(file);
|
||||
if (IS_ERR(obj)) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(IS_ERR(obj)))
|
||||
return;
|
||||
}
|
||||
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
const char *title;
|
||||
@@ -36,9 +25,11 @@ void test_reference_tracking(void)
|
||||
|
||||
/* Expect verifier failure if test name has 'fail' */
|
||||
if (strstr(title, "fail") != NULL) {
|
||||
libbpf_set_print(NULL);
|
||||
libbpf_print_fn_t old_print_fn;
|
||||
|
||||
old_print_fn = libbpf_set_print(NULL);
|
||||
err = !bpf_program__load(prog, "GPL", 0);
|
||||
libbpf_set_print(libbpf_debug_print);
|
||||
libbpf_set_print(old_print_fn);
|
||||
} else {
|
||||
err = bpf_program__load(prog, "GPL", 0);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ static void sigusr1_handler(int signum)
|
||||
sigusr1_received++;
|
||||
}
|
||||
|
||||
static int test_send_signal_common(struct perf_event_attr *attr,
|
||||
static void test_send_signal_common(struct perf_event_attr *attr,
|
||||
int prog_type,
|
||||
const char *test_name)
|
||||
{
|
||||
@@ -23,13 +23,13 @@ static int test_send_signal_common(struct perf_event_attr *attr,
|
||||
|
||||
if (CHECK(pipe(pipe_c2p), test_name,
|
||||
"pipe pipe_c2p error: %s\n", strerror(errno)))
|
||||
goto no_fork_done;
|
||||
return;
|
||||
|
||||
if (CHECK(pipe(pipe_p2c), test_name,
|
||||
"pipe pipe_p2c error: %s\n", strerror(errno))) {
|
||||
close(pipe_c2p[0]);
|
||||
close(pipe_c2p[1]);
|
||||
goto no_fork_done;
|
||||
return;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
@@ -38,7 +38,7 @@ static int test_send_signal_common(struct perf_event_attr *attr,
|
||||
close(pipe_c2p[1]);
|
||||
close(pipe_p2c[0]);
|
||||
close(pipe_p2c[1]);
|
||||
goto no_fork_done;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
@@ -125,7 +125,7 @@ static int test_send_signal_common(struct perf_event_attr *attr,
|
||||
goto disable_pmu;
|
||||
}
|
||||
|
||||
err = CHECK(buf[0] != '2', test_name, "incorrect result\n");
|
||||
CHECK(buf[0] != '2', test_name, "incorrect result\n");
|
||||
|
||||
/* notify child safe to exit */
|
||||
write(pipe_p2c[1], buf, 1);
|
||||
@@ -138,11 +138,9 @@ prog_load_failure:
|
||||
close(pipe_c2p[0]);
|
||||
close(pipe_p2c[1]);
|
||||
wait(NULL);
|
||||
no_fork_done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int test_send_signal_tracepoint(void)
|
||||
static void test_send_signal_tracepoint(void)
|
||||
{
|
||||
const char *id_path = "/sys/kernel/debug/tracing/events/syscalls/sys_enter_nanosleep/id";
|
||||
struct perf_event_attr attr = {
|
||||
@@ -159,21 +157,21 @@ static int test_send_signal_tracepoint(void)
|
||||
if (CHECK(efd < 0, "tracepoint",
|
||||
"open syscalls/sys_enter_nanosleep/id failure: %s\n",
|
||||
strerror(errno)))
|
||||
return -1;
|
||||
return;
|
||||
|
||||
bytes = read(efd, buf, sizeof(buf));
|
||||
close(efd);
|
||||
if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "tracepoint",
|
||||
"read syscalls/sys_enter_nanosleep/id failure: %s\n",
|
||||
strerror(errno)))
|
||||
return -1;
|
||||
return;
|
||||
|
||||
attr.config = strtol(buf, NULL, 0);
|
||||
|
||||
return test_send_signal_common(&attr, BPF_PROG_TYPE_TRACEPOINT, "tracepoint");
|
||||
test_send_signal_common(&attr, BPF_PROG_TYPE_TRACEPOINT, "tracepoint");
|
||||
}
|
||||
|
||||
static int test_send_signal_perf(void)
|
||||
static void test_send_signal_perf(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.sample_period = 1,
|
||||
@@ -181,11 +179,11 @@ static int test_send_signal_perf(void)
|
||||
.config = PERF_COUNT_SW_CPU_CLOCK,
|
||||
};
|
||||
|
||||
return test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT,
|
||||
"perf_sw_event");
|
||||
test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT,
|
||||
"perf_sw_event");
|
||||
}
|
||||
|
||||
static int test_send_signal_nmi(void)
|
||||
static void test_send_signal_nmi(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.sample_freq = 50,
|
||||
@@ -203,27 +201,25 @@ static int test_send_signal_nmi(void)
|
||||
if (pmu_fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n",
|
||||
__func__);
|
||||
return 0;
|
||||
__func__);
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
/* Let the test fail with a more informative message */
|
||||
} else {
|
||||
close(pmu_fd);
|
||||
}
|
||||
|
||||
return test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT,
|
||||
"perf_hw_event");
|
||||
test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT,
|
||||
"perf_hw_event");
|
||||
}
|
||||
|
||||
void test_send_signal(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret |= test_send_signal_tracepoint();
|
||||
ret |= test_send_signal_perf();
|
||||
ret |= test_send_signal_nmi();
|
||||
if (!ret)
|
||||
printf("test_send_signal:OK\n");
|
||||
else
|
||||
printf("test_send_signal:FAIL\n");
|
||||
if (test__start_subtest("send_signal_tracepoint"))
|
||||
test_send_signal_tracepoint();
|
||||
if (test__start_subtest("send_signal_perf"))
|
||||
test_send_signal_perf();
|
||||
if (test__start_subtest("send_signal_nmi"))
|
||||
test_send_signal_nmi();
|
||||
}
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <linux/filter.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "bpf_rlimit.h"
|
||||
#include "bpf_util.h"
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define CG_PATH "/sockopt"
|
||||
|
||||
static char bpf_log_buf[4096];
|
||||
static bool verbose;
|
||||
|
||||
@@ -983,39 +968,18 @@ close_prog_fd:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int args, char **argv)
|
||||
void test_sockopt(void)
|
||||
{
|
||||
int err = EXIT_FAILURE, error_cnt = 0;
|
||||
int cgroup_fd, i;
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto cleanup_obj;
|
||||
|
||||
cgroup_fd = create_and_get_cgroup(CG_PATH);
|
||||
if (cgroup_fd < 0)
|
||||
goto cleanup_cgroup_env;
|
||||
|
||||
if (join_cgroup(CG_PATH))
|
||||
goto cleanup_cgroup;
|
||||
cgroup_fd = test__join_cgroup("/sockopt");
|
||||
if (CHECK_FAIL(cgroup_fd < 0))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
int err = run_test(cgroup_fd, &tests[i]);
|
||||
|
||||
if (err)
|
||||
error_cnt++;
|
||||
|
||||
printf("#%d %s: %s\n", i, err ? "FAIL" : "PASS",
|
||||
tests[i].descr);
|
||||
test__start_subtest(tests[i].descr);
|
||||
CHECK_FAIL(run_test(cgroup_fd, &tests[i]));
|
||||
}
|
||||
|
||||
printf("Summary: %ld PASSED, %d FAILED\n",
|
||||
ARRAY_SIZE(tests) - error_cnt, error_cnt);
|
||||
err = error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
||||
cleanup_cgroup:
|
||||
close(cgroup_fd);
|
||||
cleanup_cgroup_env:
|
||||
cleanup_cgroup_environment();
|
||||
cleanup_obj:
|
||||
return err;
|
||||
}
|
||||
235
tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c
Normal file
235
tools/testing/selftests/bpf/prog_tests/sockopt_inherit.c
Normal file
@@ -0,0 +1,235 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define SOL_CUSTOM 0xdeadbeef
|
||||
#define CUSTOM_INHERIT1 0
|
||||
#define CUSTOM_INHERIT2 1
|
||||
#define CUSTOM_LISTENER 2
|
||||
|
||||
static int connect_to_server(int server_fd)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
int fd;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
log_err("Failed to create client socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) {
|
||||
log_err("Failed to get server addr");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (connect(fd, (const struct sockaddr *)&addr, len) < 0) {
|
||||
log_err("Fail to connect to server");
|
||||
goto out;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int verify_sockopt(int fd, int optname, const char *msg, char expected)
|
||||
{
|
||||
socklen_t optlen = 1;
|
||||
char buf = 0;
|
||||
int err;
|
||||
|
||||
err = getsockopt(fd, SOL_CUSTOM, optname, &buf, &optlen);
|
||||
if (err) {
|
||||
log_err("%s: failed to call getsockopt", msg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s %d: got=0x%x ? expected=0x%x\n", msg, optname, buf, expected);
|
||||
|
||||
if (buf != expected) {
|
||||
log_err("%s: unexpected getsockopt value %d != %d", msg,
|
||||
buf, expected);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pthread_mutex_t server_started_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t server_started = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static void *server_thread(void *arg)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
int fd = *(int *)arg;
|
||||
int client_fd;
|
||||
int err = 0;
|
||||
|
||||
err = listen(fd, 1);
|
||||
|
||||
pthread_mutex_lock(&server_started_mtx);
|
||||
pthread_cond_signal(&server_started);
|
||||
pthread_mutex_unlock(&server_started_mtx);
|
||||
|
||||
if (CHECK_FAIL(err < 0)) {
|
||||
perror("Failed to listed on socket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err += verify_sockopt(fd, CUSTOM_INHERIT1, "listen", 1);
|
||||
err += verify_sockopt(fd, CUSTOM_INHERIT2, "listen", 1);
|
||||
err += verify_sockopt(fd, CUSTOM_LISTENER, "listen", 1);
|
||||
|
||||
client_fd = accept(fd, (struct sockaddr *)&addr, &len);
|
||||
if (CHECK_FAIL(client_fd < 0)) {
|
||||
perror("Failed to accept client");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err += verify_sockopt(client_fd, CUSTOM_INHERIT1, "accept", 1);
|
||||
err += verify_sockopt(client_fd, CUSTOM_INHERIT2, "accept", 1);
|
||||
err += verify_sockopt(client_fd, CUSTOM_LISTENER, "accept", 0);
|
||||
|
||||
close(client_fd);
|
||||
|
||||
return (void *)(long)err;
|
||||
}
|
||||
|
||||
static int start_server(void)
|
||||
{
|
||||
struct sockaddr_in addr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
|
||||
};
|
||||
char buf;
|
||||
int err;
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
log_err("Failed to create server socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = CUSTOM_INHERIT1; i <= CUSTOM_LISTENER; i++) {
|
||||
buf = 0x01;
|
||||
err = setsockopt(fd, SOL_CUSTOM, i, &buf, 1);
|
||||
if (err) {
|
||||
log_err("Failed to call setsockopt(%d)", i);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
log_err("Failed to bind socket");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
|
||||
{
|
||||
enum bpf_attach_type attach_type;
|
||||
enum bpf_prog_type prog_type;
|
||||
struct bpf_program *prog;
|
||||
int err;
|
||||
|
||||
err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
|
||||
if (err) {
|
||||
log_err("Failed to deduct types for %s BPF program", title);
|
||||
return -1;
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_title(obj, title);
|
||||
if (!prog) {
|
||||
log_err("Failed to find %s BPF program", title);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
|
||||
attach_type, 0);
|
||||
if (err) {
|
||||
log_err("Failed to attach %s BPF program", title);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_test(int cgroup_fd)
|
||||
{
|
||||
struct bpf_prog_load_attr attr = {
|
||||
.file = "./sockopt_inherit.o",
|
||||
};
|
||||
int server_fd = -1, client_fd;
|
||||
struct bpf_object *obj;
|
||||
void *server_err;
|
||||
pthread_t tid;
|
||||
int ignored;
|
||||
int err;
|
||||
|
||||
err = bpf_prog_load_xattr(&attr, &obj, &ignored);
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
|
||||
err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt");
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_bpf_object;
|
||||
|
||||
err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt");
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_bpf_object;
|
||||
|
||||
server_fd = start_server();
|
||||
if (CHECK_FAIL(server_fd < 0))
|
||||
goto close_bpf_object;
|
||||
|
||||
if (CHECK_FAIL(pthread_create(&tid, NULL, server_thread,
|
||||
(void *)&server_fd)))
|
||||
goto close_bpf_object;
|
||||
|
||||
pthread_mutex_lock(&server_started_mtx);
|
||||
pthread_cond_wait(&server_started, &server_started_mtx);
|
||||
pthread_mutex_unlock(&server_started_mtx);
|
||||
|
||||
client_fd = connect_to_server(server_fd);
|
||||
if (CHECK_FAIL(client_fd < 0))
|
||||
goto close_server_fd;
|
||||
|
||||
CHECK_FAIL(verify_sockopt(client_fd, CUSTOM_INHERIT1, "connect", 0));
|
||||
CHECK_FAIL(verify_sockopt(client_fd, CUSTOM_INHERIT2, "connect", 0));
|
||||
CHECK_FAIL(verify_sockopt(client_fd, CUSTOM_LISTENER, "connect", 0));
|
||||
|
||||
pthread_join(tid, &server_err);
|
||||
|
||||
err = (int)(long)server_err;
|
||||
CHECK_FAIL(err);
|
||||
|
||||
close(client_fd);
|
||||
|
||||
close_server_fd:
|
||||
close(server_fd);
|
||||
close_bpf_object:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
void test_sockopt_inherit(void)
|
||||
{
|
||||
int cgroup_fd;
|
||||
|
||||
cgroup_fd = test__join_cgroup("/sockopt_inherit");
|
||||
if (CHECK_FAIL(cgroup_fd < 0))
|
||||
return;
|
||||
|
||||
run_test(cgroup_fd);
|
||||
close(cgroup_fd);
|
||||
}
|
||||
@@ -1,19 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <linux/filter.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "bpf_rlimit.h"
|
||||
#include "bpf_util.h"
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
|
||||
@@ -308,7 +294,7 @@ detach:
|
||||
return err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
void test_sockopt_multi(void)
|
||||
{
|
||||
struct bpf_prog_load_attr attr = {
|
||||
.file = "./sockopt_multi.o",
|
||||
@@ -319,56 +305,28 @@ int main(int argc, char **argv)
|
||||
int err = -1;
|
||||
int ignored;
|
||||
|
||||
if (setup_cgroup_environment()) {
|
||||
log_err("Failed to setup cgroup environment\n");
|
||||
cg_parent = test__join_cgroup("/parent");
|
||||
if (CHECK_FAIL(cg_parent < 0))
|
||||
goto out;
|
||||
}
|
||||
|
||||
cg_parent = create_and_get_cgroup("/parent");
|
||||
if (cg_parent < 0) {
|
||||
log_err("Failed to create cgroup /parent\n");
|
||||
cg_child = test__join_cgroup("/parent/child");
|
||||
if (CHECK_FAIL(cg_child < 0))
|
||||
goto out;
|
||||
}
|
||||
|
||||
cg_child = create_and_get_cgroup("/parent/child");
|
||||
if (cg_child < 0) {
|
||||
log_err("Failed to create cgroup /parent/child\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (join_cgroup("/parent/child")) {
|
||||
log_err("Failed to join cgroup /parent/child\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = bpf_prog_load_xattr(&attr, &obj, &ignored);
|
||||
if (err) {
|
||||
log_err("Failed to load BPF object");
|
||||
if (CHECK_FAIL(err))
|
||||
goto out;
|
||||
}
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
log_err("Failed to create socket");
|
||||
if (CHECK_FAIL(sock_fd < 0))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (run_getsockopt_test(obj, cg_parent, cg_child, sock_fd))
|
||||
err = -1;
|
||||
printf("test_sockopt_multi: getsockopt %s\n",
|
||||
err ? "FAILED" : "PASSED");
|
||||
|
||||
if (run_setsockopt_test(obj, cg_parent, cg_child, sock_fd))
|
||||
err = -1;
|
||||
printf("test_sockopt_multi: setsockopt %s\n",
|
||||
err ? "FAILED" : "PASSED");
|
||||
CHECK_FAIL(run_getsockopt_test(obj, cg_parent, cg_child, sock_fd));
|
||||
CHECK_FAIL(run_setsockopt_test(obj, cg_parent, cg_child, sock_fd));
|
||||
|
||||
out:
|
||||
close(sock_fd);
|
||||
bpf_object__close(obj);
|
||||
close(cg_child);
|
||||
close(cg_parent);
|
||||
|
||||
printf("test_sockopt_multi: %s\n", err ? "FAILED" : "PASSED");
|
||||
return err ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,22 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <linux/filter.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "bpf_rlimit.h"
|
||||
#include "bpf_util.h"
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define CG_PATH "/sockopt"
|
||||
|
||||
#define SOL_CUSTOM 0xdeadbeef
|
||||
|
||||
static int getsetsockopt(void)
|
||||
@@ -25,6 +10,7 @@ static int getsetsockopt(void)
|
||||
union {
|
||||
char u8[4];
|
||||
__u32 u32;
|
||||
char cc[16]; /* TCP_CA_NAME_MAX */
|
||||
} buf = {};
|
||||
socklen_t optlen;
|
||||
|
||||
@@ -115,6 +101,29 @@ static int getsetsockopt(void)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* TCP_CONGESTION can extend the string */
|
||||
|
||||
strcpy(buf.cc, "nv");
|
||||
err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv"));
|
||||
if (err) {
|
||||
log_err("Failed to call setsockopt(TCP_CONGESTION)");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
optlen = sizeof(buf.cc);
|
||||
err = getsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, &optlen);
|
||||
if (err) {
|
||||
log_err("Failed to call getsockopt(TCP_CONGESTION)");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (strcmp(buf.cc, "cubic") != 0) {
|
||||
log_err("Unexpected getsockopt(TCP_CONGESTION) %s != %s",
|
||||
buf.cc, "cubic");
|
||||
goto err;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
err:
|
||||
@@ -151,7 +160,7 @@ static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_test(int cgroup_fd)
|
||||
static void run_test(int cgroup_fd)
|
||||
{
|
||||
struct bpf_prog_load_attr attr = {
|
||||
.file = "./sockopt_sk.o",
|
||||
@@ -161,51 +170,31 @@ static int run_test(int cgroup_fd)
|
||||
int err;
|
||||
|
||||
err = bpf_prog_load_xattr(&attr, &obj, &ignored);
|
||||
if (err) {
|
||||
log_err("Failed to load BPF object");
|
||||
return -1;
|
||||
}
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
|
||||
err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt");
|
||||
if (err)
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_bpf_object;
|
||||
|
||||
err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt");
|
||||
if (err)
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_bpf_object;
|
||||
|
||||
err = getsetsockopt();
|
||||
CHECK_FAIL(getsetsockopt());
|
||||
|
||||
close_bpf_object:
|
||||
bpf_object__close(obj);
|
||||
return err;
|
||||
}
|
||||
|
||||
int main(int args, char **argv)
|
||||
void test_sockopt_sk(void)
|
||||
{
|
||||
int cgroup_fd;
|
||||
int err = EXIT_SUCCESS;
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto cleanup_obj;
|
||||
cgroup_fd = test__join_cgroup("/sockopt_sk");
|
||||
if (CHECK_FAIL(cgroup_fd < 0))
|
||||
return;
|
||||
|
||||
cgroup_fd = create_and_get_cgroup(CG_PATH);
|
||||
if (cgroup_fd < 0)
|
||||
goto cleanup_cgroup_env;
|
||||
|
||||
if (join_cgroup(CG_PATH))
|
||||
goto cleanup_cgroup;
|
||||
|
||||
if (run_test(cgroup_fd))
|
||||
err = EXIT_FAILURE;
|
||||
|
||||
printf("test_sockopt_sk: %s\n",
|
||||
err == EXIT_SUCCESS ? "PASSED" : "FAILED");
|
||||
|
||||
cleanup_cgroup:
|
||||
run_test(cgroup_fd);
|
||||
close(cgroup_fd);
|
||||
cleanup_cgroup_env:
|
||||
cleanup_cgroup_environment();
|
||||
cleanup_obj:
|
||||
return err;
|
||||
}
|
||||
@@ -11,19 +11,19 @@ void test_spinlock(void)
|
||||
void *ret;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
|
||||
if (err) {
|
||||
if (CHECK_FAIL(err)) {
|
||||
printf("test_spin_lock:bpf_prog_load errno %d\n", errno);
|
||||
goto close_prog;
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
assert(pthread_create(&thread_id[i], NULL,
|
||||
&spin_lock_thread, &prog_fd) == 0);
|
||||
if (CHECK_FAIL(pthread_create(&thread_id[i], NULL,
|
||||
&spin_lock_thread, &prog_fd)))
|
||||
goto close_prog;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
assert(pthread_join(thread_id[i], &ret) == 0 &&
|
||||
ret == (void *)&prog_fd);
|
||||
goto close_prog_noerr;
|
||||
if (CHECK_FAIL(pthread_join(thread_id[i], &ret) ||
|
||||
ret != (void *)&prog_fd))
|
||||
goto close_prog;
|
||||
close_prog:
|
||||
error_cnt++;
|
||||
close_prog_noerr:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
@@ -51,9 +51,10 @@ retry:
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto disable_pmu;
|
||||
|
||||
assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")
|
||||
== 0);
|
||||
assert(system("./urandom_read") == 0);
|
||||
if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")))
|
||||
goto disable_pmu;
|
||||
if (CHECK_FAIL(system("./urandom_read")))
|
||||
goto disable_pmu;
|
||||
/* disable stack trace collection */
|
||||
key = 0;
|
||||
val = 1;
|
||||
|
||||
@@ -82,9 +82,10 @@ retry:
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto disable_pmu;
|
||||
|
||||
assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")
|
||||
== 0);
|
||||
assert(system("taskset 0x1 ./urandom_read 100000") == 0);
|
||||
if (CHECK_FAIL(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null")))
|
||||
goto disable_pmu;
|
||||
if (CHECK_FAIL(system("taskset 0x1 ./urandom_read 100000")))
|
||||
goto disable_pmu;
|
||||
/* disable stack trace collection */
|
||||
key = 0;
|
||||
val = 1;
|
||||
|
||||
@@ -26,19 +26,19 @@ void test_stacktrace_map(void)
|
||||
|
||||
/* find map fds */
|
||||
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
||||
if (control_map_fd < 0)
|
||||
if (CHECK_FAIL(control_map_fd < 0))
|
||||
goto disable_pmu;
|
||||
|
||||
stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
|
||||
if (stackid_hmap_fd < 0)
|
||||
if (CHECK_FAIL(stackid_hmap_fd < 0))
|
||||
goto disable_pmu;
|
||||
|
||||
stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
|
||||
if (stackmap_fd < 0)
|
||||
if (CHECK_FAIL(stackmap_fd < 0))
|
||||
goto disable_pmu;
|
||||
|
||||
stack_amap_fd = bpf_find_map(__func__, obj, "stack_amap");
|
||||
if (stack_amap_fd < 0)
|
||||
if (CHECK_FAIL(stack_amap_fd < 0))
|
||||
goto disable_pmu;
|
||||
|
||||
/* give some time for bpf program run */
|
||||
@@ -55,23 +55,20 @@ void test_stacktrace_map(void)
|
||||
err = compare_map_keys(stackid_hmap_fd, stackmap_fd);
|
||||
if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap",
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto disable_pmu_noerr;
|
||||
goto disable_pmu;
|
||||
|
||||
err = compare_map_keys(stackmap_fd, stackid_hmap_fd);
|
||||
if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap",
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto disable_pmu_noerr;
|
||||
goto disable_pmu;
|
||||
|
||||
stack_trace_len = PERF_MAX_STACK_DEPTH * sizeof(__u64);
|
||||
err = compare_stack_ips(stackmap_fd, stack_amap_fd, stack_trace_len);
|
||||
if (CHECK(err, "compare_stack_ips stackmap vs. stack_amap",
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto disable_pmu_noerr;
|
||||
goto disable_pmu;
|
||||
|
||||
goto disable_pmu_noerr;
|
||||
disable_pmu:
|
||||
error_cnt++;
|
||||
disable_pmu_noerr:
|
||||
bpf_link__destroy(link);
|
||||
close_prog:
|
||||
bpf_object__close(obj);
|
||||
|
||||
@@ -26,15 +26,15 @@ void test_stacktrace_map_raw_tp(void)
|
||||
|
||||
/* find map fds */
|
||||
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
||||
if (control_map_fd < 0)
|
||||
if (CHECK_FAIL(control_map_fd < 0))
|
||||
goto close_prog;
|
||||
|
||||
stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap");
|
||||
if (stackid_hmap_fd < 0)
|
||||
if (CHECK_FAIL(stackid_hmap_fd < 0))
|
||||
goto close_prog;
|
||||
|
||||
stackmap_fd = bpf_find_map(__func__, obj, "stackmap");
|
||||
if (stackmap_fd < 0)
|
||||
if (CHECK_FAIL(stackmap_fd < 0))
|
||||
goto close_prog;
|
||||
|
||||
/* give some time for bpf program run */
|
||||
@@ -58,10 +58,7 @@ void test_stacktrace_map_raw_tp(void)
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
|
||||
goto close_prog_noerr;
|
||||
close_prog:
|
||||
error_cnt++;
|
||||
close_prog_noerr:
|
||||
if (!IS_ERR_OR_NULL(link))
|
||||
bpf_link__destroy(link);
|
||||
bpf_object__close(obj);
|
||||
|
||||
@@ -70,9 +70,6 @@ void test_task_fd_query_rawtp(void)
|
||||
if (CHECK(!err, "check_results", "fd_type %d len %u\n", fd_type, len))
|
||||
goto close_prog;
|
||||
|
||||
goto close_prog_noerr;
|
||||
close_prog:
|
||||
error_cnt++;
|
||||
close_prog_noerr:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
@@ -62,14 +62,9 @@ static void test_task_fd_query_tp_core(const char *probe_name,
|
||||
fd_type, buf))
|
||||
goto close_pmu;
|
||||
|
||||
close(pmu_fd);
|
||||
goto close_prog_noerr;
|
||||
|
||||
close_pmu:
|
||||
close(pmu_fd);
|
||||
close_prog:
|
||||
error_cnt++;
|
||||
close_prog_noerr:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,8 @@ void test_tcp_estats(void)
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
|
||||
CHECK(err, "", "err %d errno %d\n", err, errno);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <linux/filter.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "bpf_rlimit.h"
|
||||
#include "bpf_util.h"
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define CG_PATH "/tcp_rtt"
|
||||
|
||||
struct tcp_rtt_storage {
|
||||
__u32 invoked;
|
||||
__u32 dsack_dups;
|
||||
@@ -30,8 +14,32 @@ static void send_byte(int fd)
|
||||
{
|
||||
char b = 0x55;
|
||||
|
||||
if (write(fd, &b, sizeof(b)) != 1)
|
||||
error(1, errno, "Failed to send single byte");
|
||||
if (CHECK_FAIL(write(fd, &b, sizeof(b)) != 1))
|
||||
perror("Failed to send single byte");
|
||||
}
|
||||
|
||||
static int wait_for_ack(int fd, int retries)
|
||||
{
|
||||
struct tcp_info info;
|
||||
socklen_t optlen;
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < retries; i++) {
|
||||
optlen = sizeof(info);
|
||||
err = getsockopt(fd, SOL_TCP, TCP_INFO, &info, &optlen);
|
||||
if (err < 0) {
|
||||
log_err("Failed to lookup TCP stats");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (info.tcpi_unacked == 0)
|
||||
return 0;
|
||||
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
log_err("Did not receive ACK");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int verify_sk(int map_fd, int client_fd, const char *msg, __u32 invoked,
|
||||
@@ -41,8 +49,10 @@ static int verify_sk(int map_fd, int client_fd, const char *msg, __u32 invoked,
|
||||
int err = 0;
|
||||
struct tcp_rtt_storage val;
|
||||
|
||||
if (bpf_map_lookup_elem(map_fd, &client_fd, &val) < 0)
|
||||
error(1, errno, "Failed to read socket storage");
|
||||
if (CHECK_FAIL(bpf_map_lookup_elem(map_fd, &client_fd, &val) < 0)) {
|
||||
perror("Failed to read socket storage");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (val.invoked != invoked) {
|
||||
log_err("%s: unexpected bpf_tcp_sock.invoked %d != %d",
|
||||
@@ -149,6 +159,11 @@ static int run_test(int cgroup_fd, int server_fd)
|
||||
/*icsk_retransmits=*/0);
|
||||
|
||||
send_byte(client_fd);
|
||||
if (wait_for_ack(client_fd, 100) < 0) {
|
||||
err = -1;
|
||||
goto close_client_fd;
|
||||
}
|
||||
|
||||
|
||||
err += verify_sk(map_fd, client_fd, "first payload byte",
|
||||
/*invoked=*/2,
|
||||
@@ -157,6 +172,7 @@ static int run_test(int cgroup_fd, int server_fd)
|
||||
/*delivered_ce=*/0,
|
||||
/*icsk_retransmits=*/0);
|
||||
|
||||
close_client_fd:
|
||||
close(client_fd);
|
||||
|
||||
close_bpf_object:
|
||||
@@ -194,61 +210,47 @@ static void *server_thread(void *arg)
|
||||
int fd = *(int *)arg;
|
||||
int client_fd;
|
||||
|
||||
if (listen(fd, 1) < 0)
|
||||
error(1, errno, "Failed to listed on socket");
|
||||
if (CHECK_FAIL(listen(fd, 1)) < 0) {
|
||||
perror("Failed to listed on socket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
client_fd = accept(fd, (struct sockaddr *)&addr, &len);
|
||||
if (client_fd < 0)
|
||||
error(1, errno, "Failed to accept client");
|
||||
if (CHECK_FAIL(client_fd < 0)) {
|
||||
perror("Failed to accept client");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Wait for the next connection (that never arrives)
|
||||
* to keep this thread alive to prevent calling
|
||||
* close() on client_fd.
|
||||
*/
|
||||
if (accept(fd, (struct sockaddr *)&addr, &len) >= 0)
|
||||
error(1, errno, "Unexpected success in second accept");
|
||||
if (CHECK_FAIL(accept(fd, (struct sockaddr *)&addr, &len) >= 0)) {
|
||||
perror("Unexpected success in second accept");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
close(client_fd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int args, char **argv)
|
||||
void test_tcp_rtt(void)
|
||||
{
|
||||
int server_fd, cgroup_fd;
|
||||
int err = EXIT_SUCCESS;
|
||||
pthread_t tid;
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto cleanup_obj;
|
||||
|
||||
cgroup_fd = create_and_get_cgroup(CG_PATH);
|
||||
if (cgroup_fd < 0)
|
||||
goto cleanup_cgroup_env;
|
||||
|
||||
if (join_cgroup(CG_PATH))
|
||||
goto cleanup_cgroup;
|
||||
cgroup_fd = test__join_cgroup("/tcp_rtt");
|
||||
if (CHECK_FAIL(cgroup_fd < 0))
|
||||
return;
|
||||
|
||||
server_fd = start_server();
|
||||
if (server_fd < 0) {
|
||||
err = EXIT_FAILURE;
|
||||
goto cleanup_cgroup;
|
||||
}
|
||||
if (CHECK_FAIL(server_fd < 0))
|
||||
goto close_cgroup_fd;
|
||||
|
||||
pthread_create(&tid, NULL, server_thread, (void *)&server_fd);
|
||||
|
||||
if (run_test(cgroup_fd, server_fd))
|
||||
err = EXIT_FAILURE;
|
||||
|
||||
CHECK_FAIL(run_test(cgroup_fd, server_fd));
|
||||
close(server_fd);
|
||||
|
||||
printf("test_sockopt_sk: %s\n",
|
||||
err == EXIT_SUCCESS ? "PASSED" : "FAILED");
|
||||
|
||||
cleanup_cgroup:
|
||||
close_cgroup_fd:
|
||||
close(cgroup_fd);
|
||||
cleanup_cgroup_env:
|
||||
cleanup_cgroup_environment();
|
||||
cleanup_obj:
|
||||
return err;
|
||||
}
|
||||
@@ -16,10 +16,8 @@ void test_xdp(void)
|
||||
int err, prog_fd, map_fd;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "vip2tnl");
|
||||
if (map_fd < 0)
|
||||
|
||||
@@ -10,10 +10,8 @@ void test_xdp_adjust_tail(void)
|
||||
int err, prog_fd;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
|
||||
buf, &size, &retval, &duration);
|
||||
|
||||
@@ -31,10 +31,8 @@ void test_xdp_noinline(void)
|
||||
u32 *magic = (u32 *)buf;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
}
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "vip_map");
|
||||
if (map_fd < 0)
|
||||
@@ -73,9 +71,10 @@ void test_xdp_noinline(void)
|
||||
bytes += stats[i].bytes;
|
||||
pkts += stats[i].pkts;
|
||||
}
|
||||
if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) {
|
||||
error_cnt++;
|
||||
printf("test_xdp_noinline:FAIL:stats %lld %lld\n", bytes, pkts);
|
||||
if (CHECK_FAIL(bytes != MAGIC_BYTES * NUM_ITER * 2 ||
|
||||
pkts != NUM_ITER * 2)) {
|
||||
printf("test_xdp_noinline:FAIL:stats %lld %lld\n",
|
||||
bytes, pkts);
|
||||
}
|
||||
out:
|
||||
bpf_object__close(obj);
|
||||
|
||||
@@ -65,8 +65,8 @@ struct {
|
||||
} jmp_table SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 1024);
|
||||
__type(key, __u32);
|
||||
__type(value, struct bpf_flow_keys);
|
||||
} last_dissection SEC(".maps");
|
||||
@@ -74,15 +74,20 @@ struct {
|
||||
static __always_inline int export_flow_keys(struct bpf_flow_keys *keys,
|
||||
int ret)
|
||||
{
|
||||
struct bpf_flow_keys *val;
|
||||
__u32 key = 0;
|
||||
__u32 key = (__u32)(keys->sport) << 16 | keys->dport;
|
||||
struct bpf_flow_keys val;
|
||||
|
||||
val = bpf_map_lookup_elem(&last_dissection, &key);
|
||||
if (val)
|
||||
memcpy(val, keys, sizeof(*val));
|
||||
memcpy(&val, keys, sizeof(val));
|
||||
bpf_map_update_elem(&last_dissection, &key, &val, BPF_ANY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define IPV6_FLOWLABEL_MASK __bpf_constant_htonl(0x000FFFFF)
|
||||
static inline __be32 ip6_flowlabel(const struct ipv6hdr *hdr)
|
||||
{
|
||||
return *(__be32 *)hdr & IPV6_FLOWLABEL_MASK;
|
||||
}
|
||||
|
||||
static __always_inline void *bpf_flow_dissect_get_header(struct __sk_buff *skb,
|
||||
__u16 hdr_size,
|
||||
void *buffer)
|
||||
@@ -153,7 +158,6 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
|
||||
struct tcphdr *tcp, _tcp;
|
||||
struct udphdr *udp, _udp;
|
||||
|
||||
keys->ip_proto = proto;
|
||||
switch (proto) {
|
||||
case IPPROTO_ICMP:
|
||||
icmp = bpf_flow_dissect_get_header(skb, sizeof(*icmp), &_icmp);
|
||||
@@ -162,9 +166,15 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
|
||||
return export_flow_keys(keys, BPF_OK);
|
||||
case IPPROTO_IPIP:
|
||||
keys->is_encap = true;
|
||||
if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
|
||||
return export_flow_keys(keys, BPF_OK);
|
||||
|
||||
return parse_eth_proto(skb, bpf_htons(ETH_P_IP));
|
||||
case IPPROTO_IPV6:
|
||||
keys->is_encap = true;
|
||||
if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
|
||||
return export_flow_keys(keys, BPF_OK);
|
||||
|
||||
return parse_eth_proto(skb, bpf_htons(ETH_P_IPV6));
|
||||
case IPPROTO_GRE:
|
||||
gre = bpf_flow_dissect_get_header(skb, sizeof(*gre), &_gre);
|
||||
@@ -184,6 +194,8 @@ static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto)
|
||||
keys->thoff += 4; /* Step over sequence number */
|
||||
|
||||
keys->is_encap = true;
|
||||
if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP)
|
||||
return export_flow_keys(keys, BPF_OK);
|
||||
|
||||
if (gre->proto == bpf_htons(ETH_P_TEB)) {
|
||||
eth = bpf_flow_dissect_get_header(skb, sizeof(*eth),
|
||||
@@ -231,7 +243,6 @@ static __always_inline int parse_ipv6_proto(struct __sk_buff *skb, __u8 nexthdr)
|
||||
{
|
||||
struct bpf_flow_keys *keys = skb->flow_keys;
|
||||
|
||||
keys->ip_proto = nexthdr;
|
||||
switch (nexthdr) {
|
||||
case IPPROTO_HOPOPTS:
|
||||
case IPPROTO_DSTOPTS:
|
||||
@@ -266,6 +277,7 @@ PROG(IP)(struct __sk_buff *skb)
|
||||
keys->addr_proto = ETH_P_IP;
|
||||
keys->ipv4_src = iph->saddr;
|
||||
keys->ipv4_dst = iph->daddr;
|
||||
keys->ip_proto = iph->protocol;
|
||||
|
||||
keys->thoff += iph->ihl << 2;
|
||||
if (data + keys->thoff > data_end)
|
||||
@@ -273,13 +285,20 @@ PROG(IP)(struct __sk_buff *skb)
|
||||
|
||||
if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) {
|
||||
keys->is_frag = true;
|
||||
if (iph->frag_off & bpf_htons(IP_OFFSET))
|
||||
if (iph->frag_off & bpf_htons(IP_OFFSET)) {
|
||||
/* From second fragment on, packets do not have headers
|
||||
* we can parse.
|
||||
*/
|
||||
done = true;
|
||||
else
|
||||
} else {
|
||||
keys->is_first_frag = true;
|
||||
/* No need to parse fragmented packet unless
|
||||
* explicitly asked for.
|
||||
*/
|
||||
if (!(keys->flags &
|
||||
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
@@ -301,6 +320,11 @@ PROG(IPV6)(struct __sk_buff *skb)
|
||||
memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr));
|
||||
|
||||
keys->thoff += sizeof(struct ipv6hdr);
|
||||
keys->ip_proto = ip6h->nexthdr;
|
||||
keys->flow_label = ip6_flowlabel(ip6h);
|
||||
|
||||
if (keys->flags & BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL)
|
||||
return export_flow_keys(keys, BPF_OK);
|
||||
|
||||
return parse_ipv6_proto(skb, ip6h->nexthdr);
|
||||
}
|
||||
@@ -317,7 +341,8 @@ PROG(IPV6OP)(struct __sk_buff *skb)
|
||||
/* hlen is in 8-octets and does not include the first 8 bytes
|
||||
* of the header
|
||||
*/
|
||||
skb->flow_keys->thoff += (1 + ip6h->hdrlen) << 3;
|
||||
keys->thoff += (1 + ip6h->hdrlen) << 3;
|
||||
keys->ip_proto = ip6h->nexthdr;
|
||||
|
||||
return parse_ipv6_proto(skb, ip6h->nexthdr);
|
||||
}
|
||||
@@ -333,9 +358,18 @@ PROG(IPV6FR)(struct __sk_buff *skb)
|
||||
|
||||
keys->thoff += sizeof(*fragh);
|
||||
keys->is_frag = true;
|
||||
if (!(fragh->frag_off & bpf_htons(IP6_OFFSET)))
|
||||
keys->ip_proto = fragh->nexthdr;
|
||||
|
||||
if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) {
|
||||
keys->is_first_frag = true;
|
||||
|
||||
/* No need to parse fragmented packet unless
|
||||
* explicitly asked for.
|
||||
*/
|
||||
if (!(keys->flags & BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG))
|
||||
return export_flow_keys(keys, BPF_OK);
|
||||
}
|
||||
|
||||
return parse_ipv6_proto(skb, fragh->nexthdr);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___diff_arr_dim x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___diff_arr_val_sz x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_non_array x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_too_shallow x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_too_small x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_wrong_val_type1 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_wrong_val_type2 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_flavors x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_flavors__err_wrong_name x) {}
|
||||
3
tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c
Normal file
3
tools/testing/selftests/bpf/progs/btf__core_reloc_ints.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___bool x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_bitfield x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_16 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_32 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_64 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_8 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___reverse_sign x) {}
|
||||
5
tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c
Normal file
5
tools/testing/selftests/bpf/progs/btf__core_reloc_misc.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f1(struct core_reloc_misc___a x) {}
|
||||
void f2(struct core_reloc_misc___b x) {}
|
||||
void f3(struct core_reloc_misc_extensible x) {}
|
||||
3
tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c
Normal file
3
tools/testing/selftests/bpf/progs/btf__core_reloc_mods.c
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_mods x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_mods___mod_swap x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_mods___typedefs x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___anon_embed x) {}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f1(struct core_reloc_nesting___dup_compat_types x) {}
|
||||
void f2(struct core_reloc_nesting___dup_compat_types__2 x) {}
|
||||
void f3(struct core_reloc_nesting___dup_compat_types__3 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_array_container x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_array_field x) {}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f1(struct core_reloc_nesting___err_dup_incompat_types__1 x) {}
|
||||
void f2(struct core_reloc_nesting___err_dup_incompat_types__2 x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_missing_container x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_missing_field x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_nonstruct_container x) {}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f1(struct core_reloc_nesting___err_partial_match_dups__a x) {}
|
||||
void f2(struct core_reloc_nesting___err_partial_match_dups__b x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_too_deep x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___extra_nesting x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___struct_union_mixup x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___diff_enum_def x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___diff_func_proto x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___diff_ptr_type x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___err_non_enum x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___err_non_int x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___err_non_ptr x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ptr_as_arr x) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ptr_as_arr___diff_sz x) {}
|
||||
667
tools/testing/selftests/bpf/progs/core_reloc_types.h
Normal file
667
tools/testing/selftests/bpf/progs/core_reloc_types.h
Normal file
@@ -0,0 +1,667 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* FLAVORS
|
||||
*/
|
||||
struct core_reloc_flavors {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
};
|
||||
|
||||
/* this is not a flavor, as it doesn't have triple underscore */
|
||||
struct core_reloc_flavors__err_wrong_name {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
};
|
||||
|
||||
/*
|
||||
* NESTING
|
||||
*/
|
||||
/* original set up, used to record relocations in BPF program */
|
||||
struct core_reloc_nesting_substruct {
|
||||
int a;
|
||||
};
|
||||
|
||||
union core_reloc_nesting_subunion {
|
||||
int b;
|
||||
};
|
||||
|
||||
struct core_reloc_nesting {
|
||||
union {
|
||||
struct core_reloc_nesting_substruct a;
|
||||
} a;
|
||||
struct {
|
||||
union core_reloc_nesting_subunion b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* inlined anonymous struct/union instead of named structs in original */
|
||||
struct core_reloc_nesting___anon_embed {
|
||||
int __just_for_padding;
|
||||
union {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
union {
|
||||
int b;
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* different mix of nested structs/unions than in original */
|
||||
struct core_reloc_nesting___struct_union_mixup {
|
||||
int __a;
|
||||
struct {
|
||||
int __a;
|
||||
union {
|
||||
char __a;
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
int __b;
|
||||
union {
|
||||
int __b;
|
||||
union {
|
||||
char __b;
|
||||
int b;
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */
|
||||
struct core_reloc_nesting___extra_nesting {
|
||||
int __padding;
|
||||
struct {
|
||||
struct {
|
||||
struct {
|
||||
struct {
|
||||
union {
|
||||
int a;
|
||||
} a;
|
||||
};
|
||||
};
|
||||
} a;
|
||||
int __some_more;
|
||||
struct {
|
||||
union {
|
||||
union {
|
||||
union {
|
||||
struct {
|
||||
int b;
|
||||
};
|
||||
} b;
|
||||
};
|
||||
} b;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* three flavors of same struct with different structure but same layout for
|
||||
* a.a.a and b.b.b, thus successfully resolved and relocatable */
|
||||
struct core_reloc_nesting___dup_compat_types {
|
||||
char __just_for_padding;
|
||||
/* 3 more bytes of padding */
|
||||
struct {
|
||||
struct {
|
||||
int a; /* offset 4 */
|
||||
} a;
|
||||
} a;
|
||||
long long __more_padding;
|
||||
struct {
|
||||
struct {
|
||||
int b; /* offset 16 */
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
struct core_reloc_nesting___dup_compat_types__2 {
|
||||
int __aligned_padding;
|
||||
struct {
|
||||
int __trickier_noop[0];
|
||||
struct {
|
||||
char __some_more_noops[0];
|
||||
int a; /* offset 4 */
|
||||
} a;
|
||||
} a;
|
||||
int __more_padding;
|
||||
struct {
|
||||
struct {
|
||||
struct {
|
||||
int __critical_padding;
|
||||
int b; /* offset 16 */
|
||||
} b;
|
||||
int __does_not_matter;
|
||||
};
|
||||
} b;
|
||||
int __more_irrelevant_stuff;
|
||||
};
|
||||
|
||||
struct core_reloc_nesting___dup_compat_types__3 {
|
||||
char __correct_padding[4];
|
||||
struct {
|
||||
struct {
|
||||
int a; /* offset 4 */
|
||||
} a;
|
||||
} a;
|
||||
/* 8 byte padding due to next struct's alignment */
|
||||
struct {
|
||||
struct {
|
||||
int b;
|
||||
} b;
|
||||
} b __attribute__((aligned(16)));
|
||||
};
|
||||
|
||||
/* b.b.b field is missing */
|
||||
struct core_reloc_nesting___err_missing_field {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
struct {
|
||||
int x;
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* b.b.b field is an array of integers instead of plain int */
|
||||
struct core_reloc_nesting___err_array_field {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
struct {
|
||||
int b[1];
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* middle b container is missing */
|
||||
struct core_reloc_nesting___err_missing_container {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
int x;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* middle b container is referenced through pointer instead of being embedded */
|
||||
struct core_reloc_nesting___err_nonstruct_container {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
struct {
|
||||
int b;
|
||||
} *b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* middle b container is an array of structs instead of plain struct */
|
||||
struct core_reloc_nesting___err_array_container {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
struct {
|
||||
int b;
|
||||
} b[1];
|
||||
} b;
|
||||
};
|
||||
|
||||
/* two flavors of same struct with incompatible layout for b.b.b */
|
||||
struct core_reloc_nesting___err_dup_incompat_types__1 {
|
||||
struct {
|
||||
struct {
|
||||
int a; /* offset 0 */
|
||||
} a;
|
||||
} a;
|
||||
struct {
|
||||
struct {
|
||||
int b; /* offset 4 */
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
struct core_reloc_nesting___err_dup_incompat_types__2 {
|
||||
struct {
|
||||
struct {
|
||||
int a; /* offset 0 */
|
||||
} a;
|
||||
} a;
|
||||
int __extra_padding;
|
||||
struct {
|
||||
struct {
|
||||
int b; /* offset 8 (!) */
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/* two flavors of same struct having one of a.a.a and b.b.b, but not both */
|
||||
struct core_reloc_nesting___err_partial_match_dups__a {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
};
|
||||
|
||||
struct core_reloc_nesting___err_partial_match_dups__b {
|
||||
struct {
|
||||
struct {
|
||||
int b;
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
struct core_reloc_nesting___err_too_deep {
|
||||
struct {
|
||||
struct {
|
||||
int a;
|
||||
} a;
|
||||
} a;
|
||||
/* 65 levels of nestedness for b.b.b */
|
||||
struct {
|
||||
struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
struct { struct { struct { struct { struct {
|
||||
/* this one is one too much */
|
||||
struct {
|
||||
int b;
|
||||
};
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
}; }; }; }; };
|
||||
} b;
|
||||
} b;
|
||||
};
|
||||
|
||||
/*
|
||||
* ARRAYS
|
||||
*/
|
||||
struct core_reloc_arrays_output {
|
||||
int a2;
|
||||
char b123;
|
||||
int c1c;
|
||||
int d00d;
|
||||
};
|
||||
|
||||
struct core_reloc_arrays_substruct {
|
||||
int c;
|
||||
int d;
|
||||
};
|
||||
|
||||
struct core_reloc_arrays {
|
||||
int a[5];
|
||||
char b[2][3][4];
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
/* bigger array dimensions */
|
||||
struct core_reloc_arrays___diff_arr_dim {
|
||||
int a[7];
|
||||
char b[3][4][5];
|
||||
struct core_reloc_arrays_substruct c[4];
|
||||
struct core_reloc_arrays_substruct d[2][3];
|
||||
};
|
||||
|
||||
/* different size of array's value (struct) */
|
||||
struct core_reloc_arrays___diff_arr_val_sz {
|
||||
int a[5];
|
||||
char b[2][3][4];
|
||||
struct {
|
||||
int __padding1;
|
||||
int c;
|
||||
int __padding2;
|
||||
} c[3];
|
||||
struct {
|
||||
int __padding1;
|
||||
int d;
|
||||
int __padding2;
|
||||
} d[1][2];
|
||||
};
|
||||
|
||||
struct core_reloc_arrays___err_too_small {
|
||||
int a[2]; /* this one is too small */
|
||||
char b[2][3][4];
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
struct core_reloc_arrays___err_too_shallow {
|
||||
int a[5];
|
||||
char b[2][3]; /* this one lacks one dimension */
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
struct core_reloc_arrays___err_non_array {
|
||||
int a; /* not an array */
|
||||
char b[2][3][4];
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
struct core_reloc_arrays___err_wrong_val_type1 {
|
||||
char a[5]; /* char instead of int */
|
||||
char b[2][3][4];
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
struct core_reloc_arrays___err_wrong_val_type2 {
|
||||
int a[5];
|
||||
char b[2][3][4];
|
||||
int c[3]; /* value is not a struct */
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
/*
|
||||
* PRIMITIVES
|
||||
*/
|
||||
enum core_reloc_primitives_enum {
|
||||
A = 0,
|
||||
B = 1,
|
||||
};
|
||||
|
||||
struct core_reloc_primitives {
|
||||
char a;
|
||||
int b;
|
||||
enum core_reloc_primitives_enum c;
|
||||
void *d;
|
||||
int (*f)(const char *);
|
||||
};
|
||||
|
||||
struct core_reloc_primitives___diff_enum_def {
|
||||
char a;
|
||||
int b;
|
||||
void *d;
|
||||
int (*f)(const char *);
|
||||
enum {
|
||||
X = 100,
|
||||
Y = 200,
|
||||
} c; /* inline enum def with differing set of values */
|
||||
};
|
||||
|
||||
struct core_reloc_primitives___diff_func_proto {
|
||||
void (*f)(int); /* incompatible function prototype */
|
||||
void *d;
|
||||
enum core_reloc_primitives_enum c;
|
||||
int b;
|
||||
char a;
|
||||
};
|
||||
|
||||
struct core_reloc_primitives___diff_ptr_type {
|
||||
const char * const d; /* different pointee type + modifiers */
|
||||
char a;
|
||||
int b;
|
||||
enum core_reloc_primitives_enum c;
|
||||
int (*f)(const char *);
|
||||
};
|
||||
|
||||
struct core_reloc_primitives___err_non_enum {
|
||||
char a[1];
|
||||
int b;
|
||||
int c; /* int instead of enum */
|
||||
void *d;
|
||||
int (*f)(const char *);
|
||||
};
|
||||
|
||||
struct core_reloc_primitives___err_non_int {
|
||||
char a[1];
|
||||
int *b; /* ptr instead of int */
|
||||
enum core_reloc_primitives_enum c;
|
||||
void *d;
|
||||
int (*f)(const char *);
|
||||
};
|
||||
|
||||
struct core_reloc_primitives___err_non_ptr {
|
||||
char a[1];
|
||||
int b;
|
||||
enum core_reloc_primitives_enum c;
|
||||
int d; /* int instead of ptr */
|
||||
int (*f)(const char *);
|
||||
};
|
||||
|
||||
/*
|
||||
* MODS
|
||||
*/
|
||||
struct core_reloc_mods_output {
|
||||
int a, b, c, d, e, f, g, h;
|
||||
};
|
||||
|
||||
typedef const int int_t;
|
||||
typedef const char *char_ptr_t;
|
||||
typedef const int arr_t[7];
|
||||
|
||||
struct core_reloc_mods_substruct {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
} core_reloc_mods_substruct_t;
|
||||
|
||||
struct core_reloc_mods {
|
||||
int a;
|
||||
int_t b;
|
||||
char *c;
|
||||
char_ptr_t d;
|
||||
int e[3];
|
||||
arr_t f;
|
||||
struct core_reloc_mods_substruct g;
|
||||
core_reloc_mods_substruct_t h;
|
||||
};
|
||||
|
||||
/* a/b, c/d, e/f, and g/h pairs are swapped */
|
||||
struct core_reloc_mods___mod_swap {
|
||||
int b;
|
||||
int_t a;
|
||||
char *d;
|
||||
char_ptr_t c;
|
||||
int f[3];
|
||||
arr_t e;
|
||||
struct {
|
||||
int y;
|
||||
int x;
|
||||
} h;
|
||||
core_reloc_mods_substruct_t g;
|
||||
};
|
||||
|
||||
typedef int int1_t;
|
||||
typedef int1_t int2_t;
|
||||
typedef int2_t int3_t;
|
||||
|
||||
typedef int arr1_t[5];
|
||||
typedef arr1_t arr2_t;
|
||||
typedef arr2_t arr3_t;
|
||||
typedef arr3_t arr4_t;
|
||||
|
||||
typedef const char * const volatile fancy_char_ptr_t;
|
||||
|
||||
typedef core_reloc_mods_substruct_t core_reloc_mods_substruct_tt;
|
||||
|
||||
/* we need more typedefs */
|
||||
struct core_reloc_mods___typedefs {
|
||||
core_reloc_mods_substruct_tt g;
|
||||
core_reloc_mods_substruct_tt h;
|
||||
arr4_t f;
|
||||
arr4_t e;
|
||||
fancy_char_ptr_t d;
|
||||
fancy_char_ptr_t c;
|
||||
int3_t b;
|
||||
int3_t a;
|
||||
};
|
||||
|
||||
/*
|
||||
* PTR_AS_ARR
|
||||
*/
|
||||
struct core_reloc_ptr_as_arr {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct core_reloc_ptr_as_arr___diff_sz {
|
||||
int :32; /* padding */
|
||||
char __some_more_padding;
|
||||
int a;
|
||||
};
|
||||
|
||||
/*
|
||||
* INTS
|
||||
*/
|
||||
struct core_reloc_ints {
|
||||
uint8_t u8_field;
|
||||
int8_t s8_field;
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
uint32_t u32_field;
|
||||
int32_t s32_field;
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
/* signed/unsigned types swap */
|
||||
struct core_reloc_ints___reverse_sign {
|
||||
int8_t u8_field;
|
||||
uint8_t s8_field;
|
||||
int16_t u16_field;
|
||||
uint16_t s16_field;
|
||||
int32_t u32_field;
|
||||
uint32_t s32_field;
|
||||
int64_t u64_field;
|
||||
uint64_t s64_field;
|
||||
};
|
||||
|
||||
struct core_reloc_ints___bool {
|
||||
bool u8_field; /* bool instead of uint8 */
|
||||
int8_t s8_field;
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
uint32_t u32_field;
|
||||
int32_t s32_field;
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
struct core_reloc_ints___err_bitfield {
|
||||
uint8_t u8_field;
|
||||
int8_t s8_field;
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
uint32_t u32_field: 32; /* bitfields are not supported */
|
||||
int32_t s32_field;
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
struct core_reloc_ints___err_wrong_sz_8 {
|
||||
uint16_t u8_field; /* not 8-bit anymore */
|
||||
int16_t s8_field; /* not 8-bit anymore */
|
||||
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
uint32_t u32_field;
|
||||
int32_t s32_field;
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
struct core_reloc_ints___err_wrong_sz_16 {
|
||||
uint8_t u8_field;
|
||||
int8_t s8_field;
|
||||
|
||||
uint32_t u16_field; /* not 16-bit anymore */
|
||||
int32_t s16_field; /* not 16-bit anymore */
|
||||
|
||||
uint32_t u32_field;
|
||||
int32_t s32_field;
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
struct core_reloc_ints___err_wrong_sz_32 {
|
||||
uint8_t u8_field;
|
||||
int8_t s8_field;
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
|
||||
uint64_t u32_field; /* not 32-bit anymore */
|
||||
int64_t s32_field; /* not 32-bit anymore */
|
||||
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
struct core_reloc_ints___err_wrong_sz_64 {
|
||||
uint8_t u8_field;
|
||||
int8_t s8_field;
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
uint32_t u32_field;
|
||||
int32_t s32_field;
|
||||
|
||||
uint32_t u64_field; /* not 64-bit anymore */
|
||||
int32_t s64_field; /* not 64-bit anymore */
|
||||
};
|
||||
|
||||
/*
|
||||
* MISC
|
||||
*/
|
||||
struct core_reloc_misc_output {
|
||||
int a, b, c;
|
||||
};
|
||||
|
||||
struct core_reloc_misc___a {
|
||||
int a1;
|
||||
int a2;
|
||||
};
|
||||
|
||||
struct core_reloc_misc___b {
|
||||
int b1;
|
||||
int b2;
|
||||
};
|
||||
|
||||
/* this one extends core_reloc_misc_extensible struct from BPF prog */
|
||||
struct core_reloc_misc_extensible {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
};
|
||||
18
tools/testing/selftests/bpf/progs/loop4.c
Normal file
18
tools/testing/selftests/bpf/progs/loop4.c
Normal file
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC("socket")
|
||||
int combinations(volatile struct __sk_buff* skb)
|
||||
{
|
||||
int ret = 0, i;
|
||||
|
||||
#pragma nounroll
|
||||
for (i = 0; i < 20; i++)
|
||||
if (skb->len)
|
||||
ret |= 1 << i;
|
||||
return ret;
|
||||
}
|
||||
32
tools/testing/selftests/bpf/progs/loop5.c
Normal file
32
tools/testing/selftests/bpf/progs/loop5.c
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
#define barrier() __asm__ __volatile__("": : :"memory")
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC("socket")
|
||||
int while_true(volatile struct __sk_buff* skb)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
if (skb->len)
|
||||
i += 3;
|
||||
else
|
||||
i += 7;
|
||||
if (i == 9)
|
||||
break;
|
||||
barrier();
|
||||
if (i == 10)
|
||||
break;
|
||||
barrier();
|
||||
if (i == 13)
|
||||
break;
|
||||
barrier();
|
||||
if (i == 14)
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
97
tools/testing/selftests/bpf/progs/sockopt_inherit.c
Normal file
97
tools/testing/selftests/bpf/progs/sockopt_inherit.c
Normal file
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
__u32 _version SEC("version") = 1;
|
||||
|
||||
#define SOL_CUSTOM 0xdeadbeef
|
||||
#define CUSTOM_INHERIT1 0
|
||||
#define CUSTOM_INHERIT2 1
|
||||
#define CUSTOM_LISTENER 2
|
||||
|
||||
struct sockopt_inherit {
|
||||
__u8 val;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
|
||||
__type(key, int);
|
||||
__type(value, struct sockopt_inherit);
|
||||
} cloned1_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
|
||||
__type(key, int);
|
||||
__type(value, struct sockopt_inherit);
|
||||
} cloned2_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, struct sockopt_inherit);
|
||||
} listener_only_map SEC(".maps");
|
||||
|
||||
static __inline struct sockopt_inherit *get_storage(struct bpf_sockopt *ctx)
|
||||
{
|
||||
if (ctx->optname == CUSTOM_INHERIT1)
|
||||
return bpf_sk_storage_get(&cloned1_map, ctx->sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
else if (ctx->optname == CUSTOM_INHERIT2)
|
||||
return bpf_sk_storage_get(&cloned2_map, ctx->sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
else
|
||||
return bpf_sk_storage_get(&listener_only_map, ctx->sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
}
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int _getsockopt(struct bpf_sockopt *ctx)
|
||||
{
|
||||
__u8 *optval_end = ctx->optval_end;
|
||||
struct sockopt_inherit *storage;
|
||||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_CUSTOM)
|
||||
return 1; /* only interested in SOL_CUSTOM */
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
||||
storage = get_storage(ctx);
|
||||
if (!storage)
|
||||
return 0; /* EPERM, couldn't get sk storage */
|
||||
|
||||
ctx->retval = 0; /* Reset system call return value to zero */
|
||||
|
||||
optval[0] = storage->val;
|
||||
ctx->optlen = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("cgroup/setsockopt")
|
||||
int _setsockopt(struct bpf_sockopt *ctx)
|
||||
{
|
||||
__u8 *optval_end = ctx->optval_end;
|
||||
struct sockopt_inherit *storage;
|
||||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_CUSTOM)
|
||||
return 1; /* only interested in SOL_CUSTOM */
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
||||
storage = get_storage(ctx);
|
||||
if (!storage)
|
||||
return 0; /* EPERM, couldn't get sk storage */
|
||||
|
||||
storage->val = optval[0];
|
||||
ctx->optlen = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
@@ -42,6 +44,14 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
|
||||
/* Not interested in SOL_TCP:TCP_CONGESTION;
|
||||
* let next BPF program in the cgroup chain or kernel
|
||||
* handle it.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ctx->level != SOL_CUSTOM)
|
||||
return 0; /* EPERM, deny everything except custom level */
|
||||
|
||||
@@ -91,6 +101,18 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
|
||||
/* Always use cubic */
|
||||
|
||||
if (optval + 5 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
||||
memcpy(optval, "cubic", 5);
|
||||
ctx->optlen = 5;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ctx->level != SOL_CUSTOM)
|
||||
return 0; /* EPERM, deny everything except custom level */
|
||||
|
||||
|
||||
55
tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c
Normal file
55
tools/testing/selftests/bpf/progs/test_core_reloc_arrays.c
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_arrays_output {
|
||||
int a2;
|
||||
char b123;
|
||||
int c1c;
|
||||
int d00d;
|
||||
};
|
||||
|
||||
struct core_reloc_arrays_substruct {
|
||||
int c;
|
||||
int d;
|
||||
};
|
||||
|
||||
struct core_reloc_arrays {
|
||||
int a[5];
|
||||
char b[2][3][4];
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_arrays(void *ctx)
|
||||
{
|
||||
struct core_reloc_arrays *in = (void *)&data.in;
|
||||
struct core_reloc_arrays_output *out = (void *)&data.out;
|
||||
|
||||
/* in->a[2] */
|
||||
if (BPF_CORE_READ(&out->a2, &in->a[2]))
|
||||
return 1;
|
||||
/* in->b[1][2][3] */
|
||||
if (BPF_CORE_READ(&out->b123, &in->b[1][2][3]))
|
||||
return 1;
|
||||
/* in->c[1].c */
|
||||
if (BPF_CORE_READ(&out->c1c, &in->c[1].c))
|
||||
return 1;
|
||||
/* in->d[0][0].d */
|
||||
if (BPF_CORE_READ(&out->d00d, &in->d[0][0].d))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
62
tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c
Normal file
62
tools/testing/selftests/bpf/progs/test_core_reloc_flavors.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_flavors {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
};
|
||||
|
||||
/* local flavor with reversed layout */
|
||||
struct core_reloc_flavors___reversed {
|
||||
int c;
|
||||
int b;
|
||||
int a;
|
||||
};
|
||||
|
||||
/* local flavor with nested/overlapping layout */
|
||||
struct core_reloc_flavors___weird {
|
||||
struct {
|
||||
int b;
|
||||
};
|
||||
/* a and c overlap in local flavor, but this should still work
|
||||
* correctly with target original flavor
|
||||
*/
|
||||
union {
|
||||
int a;
|
||||
int c;
|
||||
};
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_flavors(void *ctx)
|
||||
{
|
||||
struct core_reloc_flavors *in_orig = (void *)&data.in;
|
||||
struct core_reloc_flavors___reversed *in_rev = (void *)&data.in;
|
||||
struct core_reloc_flavors___weird *in_weird = (void *)&data.in;
|
||||
struct core_reloc_flavors *out = (void *)&data.out;
|
||||
|
||||
/* read a using weird layout */
|
||||
if (BPF_CORE_READ(&out->a, &in_weird->a))
|
||||
return 1;
|
||||
/* read b using reversed layout */
|
||||
if (BPF_CORE_READ(&out->b, &in_rev->b))
|
||||
return 1;
|
||||
/* read c using original layout */
|
||||
if (BPF_CORE_READ(&out->c, &in_orig->c))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
44
tools/testing/selftests/bpf/progs/test_core_reloc_ints.c
Normal file
44
tools/testing/selftests/bpf/progs/test_core_reloc_ints.c
Normal file
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_ints {
|
||||
uint8_t u8_field;
|
||||
int8_t s8_field;
|
||||
uint16_t u16_field;
|
||||
int16_t s16_field;
|
||||
uint32_t u32_field;
|
||||
int32_t s32_field;
|
||||
uint64_t u64_field;
|
||||
int64_t s64_field;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_ints(void *ctx)
|
||||
{
|
||||
struct core_reloc_ints *in = (void *)&data.in;
|
||||
struct core_reloc_ints *out = (void *)&data.out;
|
||||
|
||||
if (BPF_CORE_READ(&out->u8_field, &in->u8_field) ||
|
||||
BPF_CORE_READ(&out->s8_field, &in->s8_field) ||
|
||||
BPF_CORE_READ(&out->u16_field, &in->u16_field) ||
|
||||
BPF_CORE_READ(&out->s16_field, &in->s16_field) ||
|
||||
BPF_CORE_READ(&out->u32_field, &in->u32_field) ||
|
||||
BPF_CORE_READ(&out->s32_field, &in->s32_field) ||
|
||||
BPF_CORE_READ(&out->u64_field, &in->u64_field) ||
|
||||
BPF_CORE_READ(&out->s64_field, &in->s64_field))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
36
tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
Normal file
36
tools/testing/selftests/bpf/progs/test_core_reloc_kernel.c
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct task_struct {
|
||||
int pid;
|
||||
int tgid;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_kernel(void *ctx)
|
||||
{
|
||||
struct task_struct *task = (void *)bpf_get_current_task();
|
||||
uint64_t pid_tgid = bpf_get_current_pid_tgid();
|
||||
int pid, tgid;
|
||||
|
||||
if (BPF_CORE_READ(&pid, &task->pid) ||
|
||||
BPF_CORE_READ(&tgid, &task->tgid))
|
||||
return 1;
|
||||
|
||||
/* validate pid + tgid matches */
|
||||
data.out[0] = (((uint64_t)pid << 32) | tgid) == pid_tgid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
57
tools/testing/selftests/bpf/progs/test_core_reloc_misc.c
Normal file
57
tools/testing/selftests/bpf/progs/test_core_reloc_misc.c
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_misc_output {
|
||||
int a, b, c;
|
||||
};
|
||||
|
||||
struct core_reloc_misc___a {
|
||||
int a1;
|
||||
int a2;
|
||||
};
|
||||
|
||||
struct core_reloc_misc___b {
|
||||
int b1;
|
||||
int b2;
|
||||
};
|
||||
|
||||
/* fixed two first members, can be extended with new fields */
|
||||
struct core_reloc_misc_extensible {
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_misc(void *ctx)
|
||||
{
|
||||
struct core_reloc_misc___a *in_a = (void *)&data.in;
|
||||
struct core_reloc_misc___b *in_b = (void *)&data.in;
|
||||
struct core_reloc_misc_extensible *in_ext = (void *)&data.in;
|
||||
struct core_reloc_misc_output *out = (void *)&data.out;
|
||||
|
||||
/* record two different relocations with the same accessor string */
|
||||
if (BPF_CORE_READ(&out->a, &in_a->a1) || /* accessor: 0:0 */
|
||||
BPF_CORE_READ(&out->b, &in_b->b1)) /* accessor: 0:0 */
|
||||
return 1;
|
||||
|
||||
/* Validate relocations capture array-only accesses for structs with
|
||||
* fixed header, but with potentially extendable tail. This will read
|
||||
* first 4 bytes of 2nd element of in_ext array of potentially
|
||||
* variably sized struct core_reloc_misc_extensible. */
|
||||
if (BPF_CORE_READ(&out->c, &in_ext[2])) /* accessor: 2 */
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
62
tools/testing/selftests/bpf/progs/test_core_reloc_mods.c
Normal file
62
tools/testing/selftests/bpf/progs/test_core_reloc_mods.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_mods_output {
|
||||
int a, b, c, d, e, f, g, h;
|
||||
};
|
||||
|
||||
typedef const int int_t;
|
||||
typedef const char *char_ptr_t;
|
||||
typedef const int arr_t[7];
|
||||
|
||||
struct core_reloc_mods_substruct {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
} core_reloc_mods_substruct_t;
|
||||
|
||||
struct core_reloc_mods {
|
||||
int a;
|
||||
int_t b;
|
||||
char *c;
|
||||
char_ptr_t d;
|
||||
int e[3];
|
||||
arr_t f;
|
||||
struct core_reloc_mods_substruct g;
|
||||
core_reloc_mods_substruct_t h;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_mods(void *ctx)
|
||||
{
|
||||
struct core_reloc_mods *in = (void *)&data.in;
|
||||
struct core_reloc_mods_output *out = (void *)&data.out;
|
||||
|
||||
if (BPF_CORE_READ(&out->a, &in->a) ||
|
||||
BPF_CORE_READ(&out->b, &in->b) ||
|
||||
BPF_CORE_READ(&out->c, &in->c) ||
|
||||
BPF_CORE_READ(&out->d, &in->d) ||
|
||||
BPF_CORE_READ(&out->e, &in->e[2]) ||
|
||||
BPF_CORE_READ(&out->f, &in->f[1]) ||
|
||||
BPF_CORE_READ(&out->g, &in->g.x) ||
|
||||
BPF_CORE_READ(&out->h, &in->h.y))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
46
tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
Normal file
46
tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_nesting_substruct {
|
||||
int a;
|
||||
};
|
||||
|
||||
union core_reloc_nesting_subunion {
|
||||
int b;
|
||||
};
|
||||
|
||||
/* int a.a.a and b.b.b accesses */
|
||||
struct core_reloc_nesting {
|
||||
union {
|
||||
struct core_reloc_nesting_substruct a;
|
||||
} a;
|
||||
struct {
|
||||
union core_reloc_nesting_subunion b;
|
||||
} b;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_nesting(void *ctx)
|
||||
{
|
||||
struct core_reloc_nesting *in = (void *)&data.in;
|
||||
struct core_reloc_nesting *out = (void *)&data.out;
|
||||
|
||||
if (BPF_CORE_READ(&out->a.a.a, &in->a.a.a))
|
||||
return 1;
|
||||
if (BPF_CORE_READ(&out->b.b.b, &in->b.b.b))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
enum core_reloc_primitives_enum {
|
||||
A = 0,
|
||||
B = 1,
|
||||
};
|
||||
|
||||
struct core_reloc_primitives {
|
||||
char a;
|
||||
int b;
|
||||
enum core_reloc_primitives_enum c;
|
||||
void *d;
|
||||
int (*f)(const char *);
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_primitives(void *ctx)
|
||||
{
|
||||
struct core_reloc_primitives *in = (void *)&data.in;
|
||||
struct core_reloc_primitives *out = (void *)&data.out;
|
||||
|
||||
if (BPF_CORE_READ(&out->a, &in->a) ||
|
||||
BPF_CORE_READ(&out->b, &in->b) ||
|
||||
BPF_CORE_READ(&out->c, &in->c) ||
|
||||
BPF_CORE_READ(&out->d, &in->d) ||
|
||||
BPF_CORE_READ(&out->f, &in->f))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_ptr_as_arr {
|
||||
int a;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_ptr_as_arr(void *ctx)
|
||||
{
|
||||
struct core_reloc_ptr_as_arr *in = (void *)&data.in;
|
||||
struct core_reloc_ptr_as_arr *out = (void *)&data.out;
|
||||
|
||||
if (BPF_CORE_READ(&out->a, &in[2].a))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ struct {
|
||||
__type(value, raw_stack_trace_t);
|
||||
} rawdata_map SEC(".maps");
|
||||
|
||||
SEC("tracepoint/raw_syscalls/sys_enter")
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int bpf_prog1(void *ctx)
|
||||
{
|
||||
int max_len, max_buildid_len, usize, ksize, total_size;
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
|
||||
#define SR6_FLAG_ALERT (1 << 4)
|
||||
|
||||
#define htonll(x) ((bpf_htonl(1)) == 1 ? (x) : ((uint64_t)bpf_htonl((x) & \
|
||||
0xFFFFFFFF) << 32) | bpf_htonl((x) >> 32))
|
||||
#define ntohll(x) ((bpf_ntohl(1)) == 1 ? (x) : ((uint64_t)bpf_ntohl((x) & \
|
||||
0xFFFFFFFF) << 32) | bpf_ntohl((x) >> 32))
|
||||
#define BPF_PACKET_HEADER __attribute__((packed))
|
||||
|
||||
struct ip6_t {
|
||||
@@ -276,8 +272,8 @@ int has_egr_tlv(struct __sk_buff *skb, struct ip6_srh_t *srh)
|
||||
return 0;
|
||||
|
||||
// check if egress TLV value is correct
|
||||
if (ntohll(egr_addr.hi) == 0xfd00000000000000 &&
|
||||
ntohll(egr_addr.lo) == 0x4)
|
||||
if (bpf_be64_to_cpu(egr_addr.hi) == 0xfd00000000000000 &&
|
||||
bpf_be64_to_cpu(egr_addr.lo) == 0x4)
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -308,8 +304,8 @@ int __encap_srh(struct __sk_buff *skb)
|
||||
|
||||
#pragma clang loop unroll(full)
|
||||
for (unsigned long long lo = 0; lo < 4; lo++) {
|
||||
seg->lo = htonll(4 - lo);
|
||||
seg->hi = htonll(hi);
|
||||
seg->lo = bpf_cpu_to_be64(4 - lo);
|
||||
seg->hi = bpf_cpu_to_be64(hi);
|
||||
seg = (struct ip6_addr_t *)((char *)seg + sizeof(*seg));
|
||||
}
|
||||
|
||||
@@ -349,8 +345,8 @@ int __add_egr_x(struct __sk_buff *skb)
|
||||
if (err)
|
||||
return BPF_DROP;
|
||||
|
||||
addr.lo = htonll(lo);
|
||||
addr.hi = htonll(hi);
|
||||
addr.lo = bpf_cpu_to_be64(lo);
|
||||
addr.hi = bpf_cpu_to_be64(hi);
|
||||
err = bpf_lwt_seg6_action(skb, SEG6_LOCAL_ACTION_END_X,
|
||||
(void *)&addr, sizeof(addr));
|
||||
if (err)
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
|
||||
#define SR6_FLAG_ALERT (1 << 4)
|
||||
|
||||
#define htonll(x) ((bpf_htonl(1)) == 1 ? (x) : ((uint64_t)bpf_htonl((x) & \
|
||||
0xFFFFFFFF) << 32) | bpf_htonl((x) >> 32))
|
||||
#define ntohll(x) ((bpf_ntohl(1)) == 1 ? (x) : ((uint64_t)bpf_ntohl((x) & \
|
||||
0xFFFFFFFF) << 32) | bpf_ntohl((x) >> 32))
|
||||
#define BPF_PACKET_HEADER __attribute__((packed))
|
||||
|
||||
struct ip6_t {
|
||||
@@ -251,8 +247,8 @@ int __add_egr_x(struct __sk_buff *skb)
|
||||
if (err)
|
||||
return BPF_DROP;
|
||||
|
||||
addr.lo = htonll(lo);
|
||||
addr.hi = htonll(hi);
|
||||
addr.lo = bpf_cpu_to_be64(lo);
|
||||
addr.hi = bpf_cpu_to_be64(hi);
|
||||
err = bpf_lwt_seg6_action(skb, SEG6_LOCAL_ACTION_END_X,
|
||||
(void *)&addr, sizeof(addr));
|
||||
if (err)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <stdint.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
|
||||
@@ -19,10 +19,29 @@
|
||||
struct bpf_map_def SEC("maps") results = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u64),
|
||||
.max_entries = 1,
|
||||
.value_size = sizeof(__u32),
|
||||
.max_entries = 3,
|
||||
};
|
||||
|
||||
static __always_inline __s64 gen_syncookie(void *data_end, struct bpf_sock *sk,
|
||||
void *iph, __u32 ip_size,
|
||||
struct tcphdr *tcph)
|
||||
{
|
||||
__u32 thlen = tcph->doff * 4;
|
||||
|
||||
if (tcph->syn && !tcph->ack) {
|
||||
// packet should only have an MSS option
|
||||
if (thlen != 24)
|
||||
return 0;
|
||||
|
||||
if ((void *)tcph + thlen > data_end)
|
||||
return 0;
|
||||
|
||||
return bpf_tcp_gen_syncookie(sk, iph, ip_size, tcph, thlen);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __always_inline void check_syncookie(void *ctx, void *data,
|
||||
void *data_end)
|
||||
{
|
||||
@@ -33,8 +52,10 @@ static __always_inline void check_syncookie(void *ctx, void *data,
|
||||
struct ipv6hdr *ipv6h;
|
||||
struct tcphdr *tcph;
|
||||
int ret;
|
||||
__u32 key_mss = 2;
|
||||
__u32 key_gen = 1;
|
||||
__u32 key = 0;
|
||||
__u64 value = 1;
|
||||
__s64 seq_mss;
|
||||
|
||||
ethh = data;
|
||||
if (ethh + 1 > data_end)
|
||||
@@ -66,6 +87,9 @@ static __always_inline void check_syncookie(void *ctx, void *data,
|
||||
if (sk->state != BPF_TCP_LISTEN)
|
||||
goto release;
|
||||
|
||||
seq_mss = gen_syncookie(data_end, sk, ipv4h, sizeof(*ipv4h),
|
||||
tcph);
|
||||
|
||||
ret = bpf_tcp_check_syncookie(sk, ipv4h, sizeof(*ipv4h),
|
||||
tcph, sizeof(*tcph));
|
||||
break;
|
||||
@@ -95,6 +119,9 @@ static __always_inline void check_syncookie(void *ctx, void *data,
|
||||
if (sk->state != BPF_TCP_LISTEN)
|
||||
goto release;
|
||||
|
||||
seq_mss = gen_syncookie(data_end, sk, ipv6h, sizeof(*ipv6h),
|
||||
tcph);
|
||||
|
||||
ret = bpf_tcp_check_syncookie(sk, ipv6h, sizeof(*ipv6h),
|
||||
tcph, sizeof(*tcph));
|
||||
break;
|
||||
@@ -103,8 +130,19 @@ static __always_inline void check_syncookie(void *ctx, void *data,
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
bpf_map_update_elem(&results, &key, &value, 0);
|
||||
if (seq_mss > 0) {
|
||||
__u32 cookie = (__u32)seq_mss;
|
||||
__u32 mss = seq_mss >> 32;
|
||||
|
||||
bpf_map_update_elem(&results, &key_gen, &cookie, 0);
|
||||
bpf_map_update_elem(&results, &key_mss, &mss, 0);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
__u32 cookie = bpf_ntohl(tcph->ack_seq) - 1;
|
||||
|
||||
bpf_map_update_elem(&results, &key, &cookie, 0);
|
||||
}
|
||||
|
||||
release:
|
||||
bpf_sk_release(sk);
|
||||
|
||||
143
tools/testing/selftests/bpf/test_bpftool_build.sh
Executable file
143
tools/testing/selftests/bpf/test_bpftool_build.sh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
ERROR=0
|
||||
TMPDIR=
|
||||
|
||||
# If one build fails, continue but return non-0 on exit.
|
||||
return_value() {
|
||||
if [ -d "$TMPDIR" ] ; then
|
||||
rm -rf -- $TMPDIR
|
||||
fi
|
||||
exit $ERROR
|
||||
}
|
||||
trap return_value EXIT
|
||||
|
||||
case $1 in
|
||||
-h|--help)
|
||||
echo -e "$0 [-j <n>]"
|
||||
echo -e "\tTest the different ways of building bpftool."
|
||||
echo -e ""
|
||||
echo -e "\tOptions:"
|
||||
echo -e "\t\t-j <n>:\tPass -j flag to 'make'."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
J=$*
|
||||
|
||||
# Assume script is located under tools/testing/selftests/bpf/. We want to start
|
||||
# build attempts from the top of kernel repository.
|
||||
SCRIPT_REL_PATH=$(realpath --relative-to=$PWD $0)
|
||||
SCRIPT_REL_DIR=$(dirname $SCRIPT_REL_PATH)
|
||||
KDIR_ROOT_DIR=$(realpath $PWD/$SCRIPT_REL_DIR/../../../../)
|
||||
cd $KDIR_ROOT_DIR
|
||||
|
||||
check() {
|
||||
local dir=$(realpath $1)
|
||||
|
||||
echo -n "binary: "
|
||||
# Returns non-null if file is found (and "false" is run)
|
||||
find $dir -type f -executable -name bpftool -print -exec false {} + && \
|
||||
ERROR=1 && printf "FAILURE: Did not find bpftool\n"
|
||||
}
|
||||
|
||||
make_and_clean() {
|
||||
echo -e "\$PWD: $PWD"
|
||||
echo -e "command: make -s $* >/dev/null"
|
||||
make $J -s $* >/dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
ERROR=1
|
||||
fi
|
||||
if [ $# -ge 1 ] ; then
|
||||
check ${@: -1}
|
||||
else
|
||||
check .
|
||||
fi
|
||||
(
|
||||
if [ $# -ge 1 ] ; then
|
||||
cd ${@: -1}
|
||||
fi
|
||||
make -s clean
|
||||
)
|
||||
echo
|
||||
}
|
||||
|
||||
make_with_tmpdir() {
|
||||
local ARGS
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
if [ $# -ge 2 ] ; then
|
||||
ARGS=${@:1:(($# - 1))}
|
||||
fi
|
||||
echo -e "\$PWD: $PWD"
|
||||
echo -e "command: make -s $ARGS ${@: -1}=$TMPDIR/ >/dev/null"
|
||||
make $J -s $ARGS ${@: -1}=$TMPDIR/ >/dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
ERROR=1
|
||||
fi
|
||||
check $TMPDIR
|
||||
rm -rf -- $TMPDIR
|
||||
echo
|
||||
}
|
||||
|
||||
echo "Trying to build bpftool"
|
||||
echo -e "... through kbuild\n"
|
||||
|
||||
if [ -f ".config" ] ; then
|
||||
make_and_clean tools/bpf
|
||||
|
||||
## $OUTPUT is overwritten in kbuild Makefile, and thus cannot be passed
|
||||
## down from toplevel Makefile to bpftool's Makefile.
|
||||
|
||||
# make_with_tmpdir tools/bpf OUTPUT
|
||||
echo -e "skip: make tools/bpf OUTPUT=<dir> (not supported)\n"
|
||||
|
||||
make_with_tmpdir tools/bpf O
|
||||
else
|
||||
echo -e "skip: make tools/bpf (no .config found)\n"
|
||||
echo -e "skip: make tools/bpf OUTPUT=<dir> (not supported)\n"
|
||||
echo -e "skip: make tools/bpf O=<dir> (no .config found)\n"
|
||||
fi
|
||||
|
||||
echo -e "... from kernel source tree\n"
|
||||
|
||||
make_and_clean -C tools/bpf/bpftool
|
||||
|
||||
make_with_tmpdir -C tools/bpf/bpftool OUTPUT
|
||||
|
||||
make_with_tmpdir -C tools/bpf/bpftool O
|
||||
|
||||
echo -e "... from tools/\n"
|
||||
cd tools/
|
||||
|
||||
make_and_clean bpf
|
||||
|
||||
## In tools/bpf/Makefile, function "descend" is called and passes $(O) and
|
||||
## $(OUTPUT). We would like $(OUTPUT) to have "bpf/bpftool/" appended before
|
||||
## calling bpftool's Makefile, but this is not the case as the "descend"
|
||||
## function focuses on $(O)/$(subdir). However, in the present case, updating
|
||||
## $(O) to have $(OUTPUT) recomputed from it in bpftool's Makefile does not
|
||||
## work, because $(O) is not defined from command line and $(OUTPUT) is not
|
||||
## updated in tools/scripts/Makefile.include.
|
||||
##
|
||||
## Workarounds would require to a) edit "descend" or use an alternative way to
|
||||
## call bpftool's Makefile, b) modify the conditions to update $(OUTPUT) and
|
||||
## other variables in tools/scripts/Makefile.include (at the risk of breaking
|
||||
## the build of other tools), or c) append manually the "bpf/bpftool" suffix to
|
||||
## $(OUTPUT) in bpf's Makefile, which may break if targets for other directories
|
||||
## use "descend" in the future.
|
||||
|
||||
# make_with_tmpdir bpf OUTPUT
|
||||
echo -e "skip: make bpf OUTPUT=<dir> (not supported)\n"
|
||||
|
||||
make_with_tmpdir bpf O
|
||||
|
||||
echo -e "... from bpftool's dir\n"
|
||||
cd bpf/bpftool
|
||||
|
||||
make_and_clean
|
||||
|
||||
make_with_tmpdir OUTPUT
|
||||
|
||||
make_with_tmpdir O
|
||||
@@ -508,6 +508,21 @@ static void test_devmap(unsigned int task, void *data)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void test_devmap_hash(unsigned int task, void *data)
|
||||
{
|
||||
int fd;
|
||||
__u32 key, value;
|
||||
|
||||
fd = bpf_create_map(BPF_MAP_TYPE_DEVMAP_HASH, sizeof(key), sizeof(value),
|
||||
2, 0);
|
||||
if (fd < 0) {
|
||||
printf("Failed to create devmap_hash '%s'!\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void test_queuemap(unsigned int task, void *data)
|
||||
{
|
||||
const int MAP_SIZE = 32;
|
||||
@@ -1684,6 +1699,7 @@ static void run_all_tests(void)
|
||||
test_arraymap_percpu_many_keys();
|
||||
|
||||
test_devmap(0, NULL);
|
||||
test_devmap_hash(0, NULL);
|
||||
test_sockmap(0, NULL);
|
||||
|
||||
test_map_large();
|
||||
|
||||
@@ -1353,7 +1353,7 @@ try:
|
||||
bpftool_prog_list_wait(expected=1)
|
||||
|
||||
ifnameB = bpftool("prog show %s" % (progB))[1]["dev"]["ifname"]
|
||||
fail(ifnameB != simB1['ifname'], "program not bound to originial device")
|
||||
fail(ifnameB != simB1['ifname'], "program not bound to original device")
|
||||
simB1.remove()
|
||||
bpftool_prog_list_wait(expected=1)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user