Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Alexei Starovoitov says: ==================== pull-request: bpf-next 2021-03-09 The following pull-request contains BPF updates for your *net-next* tree. We've added 90 non-merge commits during the last 17 day(s) which contain a total of 114 files changed, 5158 insertions(+), 1288 deletions(-). The main changes are: 1) Faster bpf_redirect_map(), from Björn. 2) skmsg cleanup, from Cong. 3) Support for floating point types in BTF, from Ilya. 4) Documentation for sys_bpf commands, from Joe. 5) Support for sk_lookup in bpf_prog_test_run, form Lorenz. 6) Enable task local storage for tracing programs, from Song. 7) bpf_for_each_map_elem() helper, from Yonghong. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
2
tools/testing/selftests/bpf/.gitignore
vendored
2
tools/testing/selftests/bpf/.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
bpf-helpers*
|
||||
bpf-syscall*
|
||||
test_verifier
|
||||
test_maps
|
||||
test_lru_map
|
||||
|
||||
@@ -68,6 +68,7 @@ TEST_PROGS := test_kmod.sh \
|
||||
test_bpftool_build.sh \
|
||||
test_bpftool.sh \
|
||||
test_bpftool_metadata.sh \
|
||||
test_doc_build.sh \
|
||||
test_xsk.sh
|
||||
|
||||
TEST_PROGS_EXTENDED := with_addr.sh \
|
||||
@@ -103,6 +104,7 @@ override define CLEAN
|
||||
$(call msg,CLEAN)
|
||||
$(Q)$(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN)
|
||||
$(Q)$(MAKE) -C bpf_testmod clean
|
||||
$(Q)$(MAKE) docs-clean
|
||||
endef
|
||||
|
||||
include ../lib.mk
|
||||
@@ -180,6 +182,7 @@ $(OUTPUT)/runqslower: $(BPFOBJ) | $(DEFAULT_BPFTOOL)
|
||||
cp $(SCRATCH_DIR)/runqslower $@
|
||||
|
||||
$(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/test_stub.o $(BPFOBJ)
|
||||
$(TEST_GEN_FILES): docs
|
||||
|
||||
$(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
|
||||
$(OUTPUT)/test_skb_cgroup_id_user: cgroup_helpers.c
|
||||
@@ -200,11 +203,16 @@ $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \
|
||||
CC=$(HOSTCC) LD=$(HOSTLD) \
|
||||
OUTPUT=$(HOST_BUILD_DIR)/bpftool/ \
|
||||
prefix= DESTDIR=$(HOST_SCRATCH_DIR)/ install
|
||||
$(Q)mkdir -p $(BUILD_DIR)/bpftool/Documentation
|
||||
$(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras) \
|
||||
-C $(BPFTOOLDIR)/Documentation \
|
||||
OUTPUT=$(BUILD_DIR)/bpftool/Documentation/ \
|
||||
prefix= DESTDIR=$(SCRATCH_DIR)/ install
|
||||
|
||||
docs:
|
||||
$(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras) \
|
||||
-f Makefile.docs \
|
||||
prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@
|
||||
|
||||
docs-clean:
|
||||
$(Q)$(MAKE) $(submake_extras) \
|
||||
-f Makefile.docs \
|
||||
prefix= OUTPUT=$(OUTPUT)/ DESTDIR=$(OUTPUT)/ $@
|
||||
|
||||
$(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \
|
||||
../../../include/uapi/linux/bpf.h \
|
||||
@@ -382,11 +390,12 @@ $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o: \
|
||||
$$(call msg,EXT-OBJ,$(TRUNNER_BINARY),$$@)
|
||||
$(Q)$$(CC) $$(CFLAGS) -c $$< $$(LDLIBS) -o $$@
|
||||
|
||||
# only copy extra resources if in flavored build
|
||||
# non-flavored in-srctree builds receive special treatment, in particular, we
|
||||
# do not need to copy extra resources (see e.g. test_btf_dump_case())
|
||||
$(TRUNNER_BINARY)-extras: $(TRUNNER_EXTRA_FILES) | $(TRUNNER_OUTPUT)
|
||||
ifneq ($2,)
|
||||
ifneq ($2:$(OUTPUT),:$(shell pwd))
|
||||
$$(call msg,EXT-COPY,$(TRUNNER_BINARY),$(TRUNNER_EXTRA_FILES))
|
||||
$(Q)cp -a $$^ $(TRUNNER_OUTPUT)/
|
||||
$(Q)rsync -aq $$^ $(TRUNNER_OUTPUT)/
|
||||
endif
|
||||
|
||||
$(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \
|
||||
@@ -476,3 +485,5 @@ EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
|
||||
prog_tests/tests.h map_tests/tests.h verifier/tests.h \
|
||||
feature \
|
||||
$(addprefix $(OUTPUT)/,*.o *.skel.h no_alu32 bpf_gcc bpf_testmod.ko)
|
||||
|
||||
.PHONY: docs docs-clean
|
||||
|
||||
82
tools/testing/selftests/bpf/Makefile.docs
Normal file
82
tools/testing/selftests/bpf/Makefile.docs
Normal file
@@ -0,0 +1,82 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
include ../../../scripts/Makefile.include
|
||||
include ../../../scripts/utilities.mak
|
||||
|
||||
INSTALL ?= install
|
||||
RM ?= rm -f
|
||||
RMDIR ?= rmdir --ignore-fail-on-non-empty
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
prefix ?= /usr/local
|
||||
mandir ?= $(prefix)/man
|
||||
man2dir = $(mandir)/man2
|
||||
man7dir = $(mandir)/man7
|
||||
|
||||
SYSCALL_RST = bpf-syscall.rst
|
||||
MAN2_RST = $(SYSCALL_RST)
|
||||
|
||||
HELPERS_RST = bpf-helpers.rst
|
||||
MAN7_RST = $(HELPERS_RST)
|
||||
|
||||
_DOC_MAN2 = $(patsubst %.rst,%.2,$(MAN2_RST))
|
||||
DOC_MAN2 = $(addprefix $(OUTPUT),$(_DOC_MAN2))
|
||||
|
||||
_DOC_MAN7 = $(patsubst %.rst,%.7,$(MAN7_RST))
|
||||
DOC_MAN7 = $(addprefix $(OUTPUT),$(_DOC_MAN7))
|
||||
|
||||
DOCTARGETS := helpers syscall
|
||||
|
||||
docs: $(DOCTARGETS)
|
||||
syscall: man2
|
||||
helpers: man7
|
||||
man2: $(DOC_MAN2)
|
||||
man7: $(DOC_MAN7)
|
||||
|
||||
RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null)
|
||||
|
||||
# Configure make rules for the man page bpf-$1.$2.
|
||||
# $1 - target for scripts/bpf_doc.py
|
||||
# $2 - man page section to generate the troff file
|
||||
define DOCS_RULES =
|
||||
$(OUTPUT)bpf-$1.rst: ../../../../include/uapi/linux/bpf.h
|
||||
$$(QUIET_GEN)../../../../scripts/bpf_doc.py $1 \
|
||||
--filename $$< > $$@
|
||||
|
||||
$(OUTPUT)%.$2: $(OUTPUT)%.rst
|
||||
ifndef RST2MAN_DEP
|
||||
$$(error "rst2man not found, but required to generate man pages")
|
||||
endif
|
||||
$$(QUIET_GEN)rst2man $$< > $$@
|
||||
|
||||
docs-clean-$1:
|
||||
$$(call QUIET_CLEAN, eBPF_$1-manpage)
|
||||
$(Q)$(RM) $$(DOC_MAN$2) $(OUTPUT)bpf-$1.rst
|
||||
|
||||
docs-install-$1: docs
|
||||
$$(call QUIET_INSTALL, eBPF_$1-manpage)
|
||||
$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$$(man$2dir)
|
||||
$(Q)$(INSTALL) -m 644 $$(DOC_MAN$2) $(DESTDIR)$$(man$2dir)
|
||||
|
||||
docs-uninstall-$1:
|
||||
$$(call QUIET_UNINST, eBPF_$1-manpage)
|
||||
$(Q)$(RM) $$(addprefix $(DESTDIR)$$(man$2dir)/,$$(_DOC_MAN$2))
|
||||
$(Q)$(RMDIR) $(DESTDIR)$$(man$2dir)
|
||||
|
||||
.PHONY: $1 docs-clean-$1 docs-install-$1 docs-uninstall-$1
|
||||
endef
|
||||
|
||||
# Create the make targets to generate manual pages by name and section
|
||||
$(eval $(call DOCS_RULES,helpers,7))
|
||||
$(eval $(call DOCS_RULES,syscall,2))
|
||||
|
||||
docs-clean: $(foreach doctarget,$(DOCTARGETS), docs-clean-$(doctarget))
|
||||
docs-install: $(foreach doctarget,$(DOCTARGETS), docs-install-$(doctarget))
|
||||
docs-uninstall: $(foreach doctarget,$(DOCTARGETS), docs-uninstall-$(doctarget))
|
||||
|
||||
.PHONY: docs docs-clean docs-install docs-uninstall man2 man7
|
||||
@@ -111,6 +111,45 @@ available in 10.0.1. The patch is available in llvm 11.0.0 trunk.
|
||||
|
||||
__ https://reviews.llvm.org/D78466
|
||||
|
||||
bpf_verif_scale/loop6.o test failure with Clang 12
|
||||
==================================================
|
||||
|
||||
With Clang 12, the following bpf_verif_scale test failed:
|
||||
* ``bpf_verif_scale/loop6.o``
|
||||
|
||||
The verifier output looks like
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
R1 type=ctx expected=fp
|
||||
The sequence of 8193 jumps is too complex.
|
||||
|
||||
The reason is compiler generating the following code
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
; for (i = 0; (i < VIRTIO_MAX_SGS) && (i < num); i++) {
|
||||
14: 16 05 40 00 00 00 00 00 if w5 == 0 goto +64 <LBB0_6>
|
||||
15: bc 51 00 00 00 00 00 00 w1 = w5
|
||||
16: 04 01 00 00 ff ff ff ff w1 += -1
|
||||
17: 67 05 00 00 20 00 00 00 r5 <<= 32
|
||||
18: 77 05 00 00 20 00 00 00 r5 >>= 32
|
||||
19: a6 01 01 00 05 00 00 00 if w1 < 5 goto +1 <LBB0_4>
|
||||
20: b7 05 00 00 06 00 00 00 r5 = 6
|
||||
00000000000000a8 <LBB0_4>:
|
||||
21: b7 02 00 00 00 00 00 00 r2 = 0
|
||||
22: b7 01 00 00 00 00 00 00 r1 = 0
|
||||
; for (i = 0; (i < VIRTIO_MAX_SGS) && (i < num); i++) {
|
||||
23: 7b 1a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r1
|
||||
24: 7b 5a c0 ff 00 00 00 00 *(u64 *)(r10 - 64) = r5
|
||||
|
||||
Note that insn #15 has w1 = w5 and w1 is refined later but
|
||||
r5(w5) is eventually saved on stack at insn #24 for later use.
|
||||
This cause later verifier failure. The bug has been `fixed`__ in
|
||||
Clang 13.
|
||||
|
||||
__ https://reviews.llvm.org/D97479
|
||||
|
||||
BPF CO-RE-based tests and Clang version
|
||||
=======================================
|
||||
|
||||
@@ -131,3 +170,12 @@ failures:
|
||||
.. _2: https://reviews.llvm.org/D85174
|
||||
.. _3: https://reviews.llvm.org/D83878
|
||||
.. _4: https://reviews.llvm.org/D83242
|
||||
|
||||
Floating-point tests and Clang version
|
||||
======================================
|
||||
|
||||
Certain selftests, e.g. core_reloc, require support for the floating-point
|
||||
types, which was introduced in `Clang 13`__. The older Clang versions will
|
||||
either crash when compiling these tests, or generate an incorrect BTF.
|
||||
|
||||
__ https://reviews.llvm.org/D83289
|
||||
|
||||
@@ -23,6 +23,7 @@ static const char * const btf_kind_str_mapping[] = {
|
||||
[BTF_KIND_FUNC_PROTO] = "FUNC_PROTO",
|
||||
[BTF_KIND_VAR] = "VAR",
|
||||
[BTF_KIND_DATASEC] = "DATASEC",
|
||||
[BTF_KIND_FLOAT] = "FLOAT",
|
||||
};
|
||||
|
||||
static const char *btf_kind_str(__u16 kind)
|
||||
@@ -173,6 +174,9 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BTF_KIND_FLOAT:
|
||||
fprintf(out, " size=%u", t->size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,44 @@
|
||||
#include <test_progs.h>
|
||||
#include "test_attach_probe.skel.h"
|
||||
|
||||
#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
|
||||
|
||||
#define OP_RT_RA_MASK 0xffff0000UL
|
||||
#define LIS_R2 0x3c400000UL
|
||||
#define ADDIS_R2_R12 0x3c4c0000UL
|
||||
#define ADDI_R2_R2 0x38420000UL
|
||||
|
||||
static ssize_t get_offset(ssize_t addr, ssize_t base)
|
||||
{
|
||||
u32 *insn = (u32 *) addr;
|
||||
|
||||
/*
|
||||
* A PPC64 ABIv2 function may have a local and a global entry
|
||||
* point. We need to use the local entry point when patching
|
||||
* functions, so identify and step over the global entry point
|
||||
* sequence.
|
||||
*
|
||||
* The global entry point sequence is always of the form:
|
||||
*
|
||||
* addis r2,r12,XXXX
|
||||
* addi r2,r2,XXXX
|
||||
*
|
||||
* A linker optimisation may convert the addis to lis:
|
||||
*
|
||||
* lis r2,XXXX
|
||||
* addi r2,r2,XXXX
|
||||
*/
|
||||
if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
|
||||
((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
|
||||
((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
|
||||
return (ssize_t)(insn + 2) - base;
|
||||
else
|
||||
return addr - base;
|
||||
}
|
||||
#else
|
||||
#define get_offset(addr, base) (addr - base)
|
||||
#endif
|
||||
|
||||
ssize_t get_base_addr() {
|
||||
size_t start, offset;
|
||||
char buf[256];
|
||||
@@ -36,7 +74,7 @@ void test_attach_probe(void)
|
||||
if (CHECK(base_addr < 0, "get_base_addr",
|
||||
"failed to find base addr: %zd", base_addr))
|
||||
return;
|
||||
uprobe_offset = (size_t)&get_base_addr - base_addr;
|
||||
uprobe_offset = get_offset((size_t)&get_base_addr, base_addr);
|
||||
|
||||
skel = test_attach_probe__open_and_load();
|
||||
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
|
||||
|
||||
@@ -76,6 +76,7 @@ void test_bpf_verif_scale(void)
|
||||
{ "loop2.o", BPF_PROG_TYPE_RAW_TRACEPOINT },
|
||||
{ "loop4.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
{ "loop5.o", BPF_PROG_TYPE_SCHED_CLS },
|
||||
{ "loop6.o", BPF_PROG_TYPE_KPROBE },
|
||||
|
||||
/* partial unroll. 19k insn in a loop.
|
||||
* Total program size 20.8k insn.
|
||||
|
||||
@@ -1903,7 +1903,7 @@ static struct btf_raw_test raw_tests[] = {
|
||||
.raw_types = {
|
||||
/* int */ /* [1] */
|
||||
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
|
||||
BTF_TYPE_ENC(0, 0x10000000, 4),
|
||||
BTF_TYPE_ENC(0, 0x20000000, 4),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
.str_sec = "",
|
||||
@@ -3531,6 +3531,136 @@ static struct btf_raw_test raw_tests[] = {
|
||||
.max_entries = 1,
|
||||
},
|
||||
|
||||
{
|
||||
.descr = "float test #1, well-formed",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* [1] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 2), /* [2] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 4), /* [3] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 8), /* [4] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 12), /* [5] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 16), /* [6] */
|
||||
BTF_STRUCT_ENC(NAME_TBD, 5, 48), /* [7] */
|
||||
BTF_MEMBER_ENC(NAME_TBD, 2, 0),
|
||||
BTF_MEMBER_ENC(NAME_TBD, 3, 32),
|
||||
BTF_MEMBER_ENC(NAME_TBD, 4, 64),
|
||||
BTF_MEMBER_ENC(NAME_TBD, 5, 128),
|
||||
BTF_MEMBER_ENC(NAME_TBD, 6, 256),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0_Float16\0float\0double\0_Float80\0long_double"
|
||||
"\0floats\0a\0b\0c\0d\0e"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "float_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 48,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 7,
|
||||
.max_entries = 1,
|
||||
},
|
||||
{
|
||||
.descr = "float test #2, invalid vlen",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* [1] */
|
||||
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FLOAT, 0, 1), 4),
|
||||
/* [2] */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0float"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "float_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 4,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 2,
|
||||
.max_entries = 1,
|
||||
.btf_load_err = true,
|
||||
.err_str = "vlen != 0",
|
||||
},
|
||||
{
|
||||
.descr = "float test #3, invalid kind_flag",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* [1] */
|
||||
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FLOAT, 1, 0), 4),
|
||||
/* [2] */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0float"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "float_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 4,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 2,
|
||||
.max_entries = 1,
|
||||
.btf_load_err = true,
|
||||
.err_str = "Invalid btf_info kind_flag",
|
||||
},
|
||||
{
|
||||
.descr = "float test #4, member does not fit",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* [1] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 4), /* [2] */
|
||||
BTF_STRUCT_ENC(NAME_TBD, 1, 2), /* [3] */
|
||||
BTF_MEMBER_ENC(NAME_TBD, 2, 0),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0float\0floats\0x"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "float_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 4,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 3,
|
||||
.max_entries = 1,
|
||||
.btf_load_err = true,
|
||||
.err_str = "Member exceeds struct_size",
|
||||
},
|
||||
{
|
||||
.descr = "float test #5, member is not properly aligned",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* [1] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 4), /* [2] */
|
||||
BTF_STRUCT_ENC(NAME_TBD, 1, 8), /* [3] */
|
||||
BTF_MEMBER_ENC(NAME_TBD, 2, 8),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0float\0floats\0x"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "float_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 4,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 3,
|
||||
.max_entries = 1,
|
||||
.btf_load_err = true,
|
||||
.err_str = "Member is not properly aligned",
|
||||
},
|
||||
{
|
||||
.descr = "float test #6, invalid size",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* [1] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 6), /* [2] */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0float"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "float_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 6,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 2,
|
||||
.max_entries = 1,
|
||||
.btf_load_err = true,
|
||||
.err_str = "Invalid type_size",
|
||||
},
|
||||
|
||||
}; /* struct btf_raw_test raw_tests[] */
|
||||
|
||||
static const char *get_next_str(const char *start, const char *end)
|
||||
@@ -6281,11 +6411,12 @@ const struct btf_dedup_test dedup_tests[] = {
|
||||
/* int[16] */
|
||||
BTF_TYPE_ARRAY_ENC(1, 1, 16), /* [2] */
|
||||
/* struct s { */
|
||||
BTF_STRUCT_ENC(NAME_NTH(2), 4, 84), /* [3] */
|
||||
BTF_STRUCT_ENC(NAME_NTH(2), 5, 88), /* [3] */
|
||||
BTF_MEMBER_ENC(NAME_NTH(3), 4, 0), /* struct s *next; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(4), 5, 64), /* const int *a; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(5), 2, 128), /* int b[16]; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(6), 1, 640), /* int c; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(8), 13, 672), /* float d; */
|
||||
/* ptr -> [3] struct s */
|
||||
BTF_PTR_ENC(3), /* [4] */
|
||||
/* ptr -> [6] const int */
|
||||
@@ -6296,39 +6427,43 @@ const struct btf_dedup_test dedup_tests[] = {
|
||||
/* full copy of the above */
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4), /* [7] */
|
||||
BTF_TYPE_ARRAY_ENC(7, 7, 16), /* [8] */
|
||||
BTF_STRUCT_ENC(NAME_NTH(2), 4, 84), /* [9] */
|
||||
BTF_STRUCT_ENC(NAME_NTH(2), 5, 88), /* [9] */
|
||||
BTF_MEMBER_ENC(NAME_NTH(3), 10, 0),
|
||||
BTF_MEMBER_ENC(NAME_NTH(4), 11, 64),
|
||||
BTF_MEMBER_ENC(NAME_NTH(5), 8, 128),
|
||||
BTF_MEMBER_ENC(NAME_NTH(6), 7, 640),
|
||||
BTF_MEMBER_ENC(NAME_NTH(8), 13, 672),
|
||||
BTF_PTR_ENC(9), /* [10] */
|
||||
BTF_PTR_ENC(12), /* [11] */
|
||||
BTF_CONST_ENC(7), /* [12] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(7), 4), /* [13] */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0s\0next\0a\0b\0c\0"),
|
||||
BTF_STR_SEC("\0int\0s\0next\0a\0b\0c\0float\0d"),
|
||||
},
|
||||
.expect = {
|
||||
.raw_types = {
|
||||
/* int */
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(4), BTF_INT_SIGNED, 0, 32, 4), /* [1] */
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(5), BTF_INT_SIGNED, 0, 32, 4), /* [1] */
|
||||
/* int[16] */
|
||||
BTF_TYPE_ARRAY_ENC(1, 1, 16), /* [2] */
|
||||
/* struct s { */
|
||||
BTF_STRUCT_ENC(NAME_NTH(6), 4, 84), /* [3] */
|
||||
BTF_MEMBER_ENC(NAME_NTH(5), 4, 0), /* struct s *next; */
|
||||
BTF_STRUCT_ENC(NAME_NTH(8), 5, 88), /* [3] */
|
||||
BTF_MEMBER_ENC(NAME_NTH(7), 4, 0), /* struct s *next; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(1), 5, 64), /* const int *a; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(2), 2, 128), /* int b[16]; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(3), 1, 640), /* int c; */
|
||||
BTF_MEMBER_ENC(NAME_NTH(4), 7, 672), /* float d; */
|
||||
/* ptr -> [3] struct s */
|
||||
BTF_PTR_ENC(3), /* [4] */
|
||||
/* ptr -> [6] const int */
|
||||
BTF_PTR_ENC(6), /* [5] */
|
||||
/* const -> [1] int */
|
||||
BTF_CONST_ENC(1), /* [6] */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(7), 4), /* [7] */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0a\0b\0c\0int\0next\0s"),
|
||||
BTF_STR_SEC("\0a\0b\0c\0d\0int\0float\0next\0s"),
|
||||
},
|
||||
.opts = {
|
||||
.dont_resolve_fwds = false,
|
||||
@@ -6449,9 +6584,10 @@ const struct btf_dedup_test dedup_tests[] = {
|
||||
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
|
||||
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 8),
|
||||
BTF_FUNC_ENC(NAME_TBD, 12), /* [13] func */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 2), /* [14] float */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M"),
|
||||
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N"),
|
||||
},
|
||||
.expect = {
|
||||
.raw_types = {
|
||||
@@ -6474,16 +6610,17 @@ const struct btf_dedup_test dedup_tests[] = {
|
||||
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
|
||||
BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 8),
|
||||
BTF_FUNC_ENC(NAME_TBD, 12), /* [13] func */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_TBD, 2), /* [14] float */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M"),
|
||||
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N"),
|
||||
},
|
||||
.opts = {
|
||||
.dont_resolve_fwds = false,
|
||||
},
|
||||
},
|
||||
{
|
||||
.descr = "dedup: no int duplicates",
|
||||
.descr = "dedup: no int/float duplicates",
|
||||
.input = {
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 8),
|
||||
@@ -6498,9 +6635,15 @@ const struct btf_dedup_test dedup_tests[] = {
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 27, 8),
|
||||
/* different byte size */
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* all allowed sizes */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 2),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 4),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 8),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 12),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 16),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0some other int"),
|
||||
BTF_STR_SEC("\0int\0some other int\0float"),
|
||||
},
|
||||
.expect = {
|
||||
.raw_types = {
|
||||
@@ -6516,9 +6659,15 @@ const struct btf_dedup_test dedup_tests[] = {
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 27, 8),
|
||||
/* different byte size */
|
||||
BTF_TYPE_INT_ENC(NAME_NTH(1), BTF_INT_SIGNED, 0, 32, 4),
|
||||
/* all allowed sizes */
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 2),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 4),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 8),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 12),
|
||||
BTF_TYPE_FLOAT_ENC(NAME_NTH(3), 16),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0int\0some other int"),
|
||||
BTF_STR_SEC("\0int\0some other int\0float"),
|
||||
},
|
||||
.opts = {
|
||||
.dont_resolve_fwds = false,
|
||||
@@ -6630,6 +6779,7 @@ static int btf_type_size(const struct btf_type *t)
|
||||
case BTF_KIND_PTR:
|
||||
case BTF_KIND_TYPEDEF:
|
||||
case BTF_KIND_FUNC:
|
||||
case BTF_KIND_FLOAT:
|
||||
return base_size;
|
||||
case BTF_KIND_INT:
|
||||
return base_size + sizeof(__u32);
|
||||
|
||||
@@ -266,6 +266,7 @@ static int duration = 0;
|
||||
.arr_elem_sz = sizeof(((type *)0)->arr_field[0]), \
|
||||
.ptr_sz = 8, /* always 8-byte pointer for BPF */ \
|
||||
.enum_sz = sizeof(((type *)0)->enum_field), \
|
||||
.float_sz = sizeof(((type *)0)->float_field), \
|
||||
}
|
||||
|
||||
#define SIZE_CASE(name) { \
|
||||
|
||||
130
tools/testing/selftests/bpf/prog_tests/for_each.c
Normal file
130
tools/testing/selftests/bpf/prog_tests/for_each.c
Normal file
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
#include "for_each_hash_map_elem.skel.h"
|
||||
#include "for_each_array_map_elem.skel.h"
|
||||
|
||||
static unsigned int duration;
|
||||
|
||||
static void test_hash_map(void)
|
||||
{
|
||||
int i, err, hashmap_fd, max_entries, percpu_map_fd;
|
||||
struct for_each_hash_map_elem *skel;
|
||||
__u64 *percpu_valbuf = NULL;
|
||||
__u32 key, num_cpus, retval;
|
||||
__u64 val;
|
||||
|
||||
skel = for_each_hash_map_elem__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "for_each_hash_map_elem__open_and_load"))
|
||||
return;
|
||||
|
||||
hashmap_fd = bpf_map__fd(skel->maps.hashmap);
|
||||
max_entries = bpf_map__max_entries(skel->maps.hashmap);
|
||||
for (i = 0; i < max_entries; i++) {
|
||||
key = i;
|
||||
val = i + 1;
|
||||
err = bpf_map_update_elem(hashmap_fd, &key, &val, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "map_update"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
num_cpus = bpf_num_possible_cpus();
|
||||
percpu_map_fd = bpf_map__fd(skel->maps.percpu_map);
|
||||
percpu_valbuf = malloc(sizeof(__u64) * num_cpus);
|
||||
if (!ASSERT_OK_PTR(percpu_valbuf, "percpu_valbuf"))
|
||||
goto out;
|
||||
|
||||
key = 1;
|
||||
for (i = 0; i < num_cpus; i++)
|
||||
percpu_valbuf[i] = i + 1;
|
||||
err = bpf_map_update_elem(percpu_map_fd, &key, percpu_valbuf, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "percpu_map_update"))
|
||||
goto out;
|
||||
|
||||
err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_pkt_access),
|
||||
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
|
||||
&retval, &duration);
|
||||
if (CHECK(err || retval, "ipv4", "err %d errno %d retval %d\n",
|
||||
err, errno, retval))
|
||||
goto out;
|
||||
|
||||
ASSERT_EQ(skel->bss->hashmap_output, 4, "hashmap_output");
|
||||
ASSERT_EQ(skel->bss->hashmap_elems, max_entries, "hashmap_elems");
|
||||
|
||||
key = 1;
|
||||
err = bpf_map_lookup_elem(hashmap_fd, &key, &val);
|
||||
ASSERT_ERR(err, "hashmap_lookup");
|
||||
|
||||
ASSERT_EQ(skel->bss->percpu_called, 1, "percpu_called");
|
||||
ASSERT_LT(skel->bss->cpu, num_cpus, "num_cpus");
|
||||
ASSERT_EQ(skel->bss->percpu_map_elems, 1, "percpu_map_elems");
|
||||
ASSERT_EQ(skel->bss->percpu_key, 1, "percpu_key");
|
||||
ASSERT_EQ(skel->bss->percpu_val, skel->bss->cpu + 1, "percpu_val");
|
||||
ASSERT_EQ(skel->bss->percpu_output, 100, "percpu_output");
|
||||
out:
|
||||
free(percpu_valbuf);
|
||||
for_each_hash_map_elem__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_array_map(void)
|
||||
{
|
||||
__u32 key, num_cpus, max_entries, retval;
|
||||
int i, arraymap_fd, percpu_map_fd, err;
|
||||
struct for_each_array_map_elem *skel;
|
||||
__u64 *percpu_valbuf = NULL;
|
||||
__u64 val, expected_total;
|
||||
|
||||
skel = for_each_array_map_elem__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "for_each_array_map_elem__open_and_load"))
|
||||
return;
|
||||
|
||||
arraymap_fd = bpf_map__fd(skel->maps.arraymap);
|
||||
expected_total = 0;
|
||||
max_entries = bpf_map__max_entries(skel->maps.arraymap);
|
||||
for (i = 0; i < max_entries; i++) {
|
||||
key = i;
|
||||
val = i + 1;
|
||||
/* skip the last iteration for expected total */
|
||||
if (i != max_entries - 1)
|
||||
expected_total += val;
|
||||
err = bpf_map_update_elem(arraymap_fd, &key, &val, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "map_update"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
num_cpus = bpf_num_possible_cpus();
|
||||
percpu_map_fd = bpf_map__fd(skel->maps.percpu_map);
|
||||
percpu_valbuf = malloc(sizeof(__u64) * num_cpus);
|
||||
if (!ASSERT_OK_PTR(percpu_valbuf, "percpu_valbuf"))
|
||||
goto out;
|
||||
|
||||
key = 0;
|
||||
for (i = 0; i < num_cpus; i++)
|
||||
percpu_valbuf[i] = i + 1;
|
||||
err = bpf_map_update_elem(percpu_map_fd, &key, percpu_valbuf, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "percpu_map_update"))
|
||||
goto out;
|
||||
|
||||
err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_pkt_access),
|
||||
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
|
||||
&retval, &duration);
|
||||
if (CHECK(err || retval, "ipv4", "err %d errno %d retval %d\n",
|
||||
err, errno, retval))
|
||||
goto out;
|
||||
|
||||
ASSERT_EQ(skel->bss->arraymap_output, expected_total, "array_output");
|
||||
ASSERT_EQ(skel->bss->cpu + 1, skel->bss->percpu_val, "percpu_val");
|
||||
|
||||
out:
|
||||
free(percpu_valbuf);
|
||||
for_each_array_map_elem__destroy(skel);
|
||||
}
|
||||
|
||||
void test_for_each(void)
|
||||
{
|
||||
if (test__start_subtest("hash_map"))
|
||||
test_hash_map();
|
||||
if (test__start_subtest("array_map"))
|
||||
test_array_map();
|
||||
}
|
||||
@@ -2,12 +2,31 @@
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
|
||||
#include "test_pkt_access.skel.h"
|
||||
|
||||
static const __u32 duration;
|
||||
|
||||
static void check_run_cnt(int prog_fd, __u64 run_cnt)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
int err;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
|
||||
if (CHECK(err, "get_prog_info", "failed to get bpf_prog_info for fd %d\n", prog_fd))
|
||||
return;
|
||||
|
||||
CHECK(run_cnt != info.run_cnt, "run_cnt",
|
||||
"incorrect number of repetitions, want %llu have %llu\n", run_cnt, info.run_cnt);
|
||||
}
|
||||
|
||||
void test_prog_run_xattr(void)
|
||||
{
|
||||
const char *file = "./test_pkt_access.o";
|
||||
struct bpf_object *obj;
|
||||
char buf[10];
|
||||
int err;
|
||||
struct test_pkt_access *skel;
|
||||
int err, stats_fd = -1;
|
||||
char buf[10] = {};
|
||||
__u64 run_cnt = 0;
|
||||
|
||||
struct bpf_prog_test_run_attr tattr = {
|
||||
.repeat = 1,
|
||||
.data_in = &pkt_v4,
|
||||
@@ -16,12 +35,15 @@ void test_prog_run_xattr(void)
|
||||
.data_size_out = 5,
|
||||
};
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj,
|
||||
&tattr.prog_fd);
|
||||
if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno))
|
||||
stats_fd = bpf_enable_stats(BPF_STATS_RUN_TIME);
|
||||
if (CHECK_ATTR(stats_fd < 0, "enable_stats", "failed %d\n", errno))
|
||||
return;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
skel = test_pkt_access__open_and_load();
|
||||
if (CHECK_ATTR(!skel, "open_and_load", "failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
tattr.prog_fd = bpf_program__fd(skel->progs.test_pkt_access);
|
||||
|
||||
err = bpf_prog_test_run_xattr(&tattr);
|
||||
CHECK_ATTR(err != -1 || errno != ENOSPC || tattr.retval, "run",
|
||||
@@ -34,8 +56,12 @@ void test_prog_run_xattr(void)
|
||||
CHECK_ATTR(buf[5] != 0, "overflow",
|
||||
"BPF_PROG_TEST_RUN ignored size hint\n");
|
||||
|
||||
run_cnt += tattr.repeat;
|
||||
check_run_cnt(tattr.prog_fd, run_cnt);
|
||||
|
||||
tattr.data_out = NULL;
|
||||
tattr.data_size_out = 0;
|
||||
tattr.repeat = 2;
|
||||
errno = 0;
|
||||
|
||||
err = bpf_prog_test_run_xattr(&tattr);
|
||||
@@ -46,5 +72,12 @@ void test_prog_run_xattr(void)
|
||||
err = bpf_prog_test_run_xattr(&tattr);
|
||||
CHECK_ATTR(err != -EINVAL, "run_wrong_size_out", "err %d\n", err);
|
||||
|
||||
bpf_object__close(obj);
|
||||
run_cnt += tattr.repeat;
|
||||
check_run_cnt(tattr.prog_fd, run_cnt);
|
||||
|
||||
cleanup:
|
||||
if (skel)
|
||||
test_pkt_access__destroy(skel);
|
||||
if (stats_fd != -1)
|
||||
close(stats_fd);
|
||||
}
|
||||
|
||||
@@ -241,6 +241,48 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __u64 socket_cookie(int fd)
|
||||
{
|
||||
__u64 cookie;
|
||||
socklen_t cookie_len = sizeof(cookie);
|
||||
|
||||
if (CHECK(getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0,
|
||||
"getsockopt(SO_COOKIE)", "%s\n", strerror(errno)))
|
||||
return 0;
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static int fill_sk_lookup_ctx(struct bpf_sk_lookup *ctx, const char *local_ip, __u16 local_port,
|
||||
const char *remote_ip, __u16 remote_port)
|
||||
{
|
||||
void *local, *remote;
|
||||
int err;
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->local_port = local_port;
|
||||
ctx->remote_port = htons(remote_port);
|
||||
|
||||
if (is_ipv6(local_ip)) {
|
||||
ctx->family = AF_INET6;
|
||||
local = &ctx->local_ip6[0];
|
||||
remote = &ctx->remote_ip6[0];
|
||||
} else {
|
||||
ctx->family = AF_INET;
|
||||
local = &ctx->local_ip4;
|
||||
remote = &ctx->remote_ip4;
|
||||
}
|
||||
|
||||
err = inet_pton(ctx->family, local_ip, local);
|
||||
if (CHECK(err != 1, "inet_pton", "local_ip failed\n"))
|
||||
return 1;
|
||||
|
||||
err = inet_pton(ctx->family, remote_ip, remote);
|
||||
if (CHECK(err != 1, "inet_pton", "remote_ip failed\n"))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_byte(int fd)
|
||||
{
|
||||
ssize_t n;
|
||||
@@ -1009,18 +1051,27 @@ static void test_drop_on_reuseport(struct test_sk_lookup *skel)
|
||||
|
||||
static void run_sk_assign(struct test_sk_lookup *skel,
|
||||
struct bpf_program *lookup_prog,
|
||||
const char *listen_ip, const char *connect_ip)
|
||||
const char *remote_ip, const char *local_ip)
|
||||
{
|
||||
int client_fd, peer_fd, server_fds[MAX_SERVERS] = { -1 };
|
||||
struct bpf_link *lookup_link;
|
||||
int server_fds[MAX_SERVERS] = { -1 };
|
||||
struct bpf_sk_lookup ctx;
|
||||
__u64 server_cookie;
|
||||
int i, err;
|
||||
|
||||
lookup_link = attach_lookup_prog(lookup_prog);
|
||||
if (!lookup_link)
|
||||
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
|
||||
.ctx_in = &ctx,
|
||||
.ctx_size_in = sizeof(ctx),
|
||||
.ctx_out = &ctx,
|
||||
.ctx_size_out = sizeof(ctx),
|
||||
);
|
||||
|
||||
if (fill_sk_lookup_ctx(&ctx, local_ip, EXT_PORT, remote_ip, INT_PORT))
|
||||
return;
|
||||
|
||||
ctx.protocol = IPPROTO_TCP;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
|
||||
server_fds[i] = make_server(SOCK_STREAM, listen_ip, 0, NULL);
|
||||
server_fds[i] = make_server(SOCK_STREAM, local_ip, 0, NULL);
|
||||
if (server_fds[i] < 0)
|
||||
goto close_servers;
|
||||
|
||||
@@ -1030,23 +1081,25 @@ static void run_sk_assign(struct test_sk_lookup *skel,
|
||||
goto close_servers;
|
||||
}
|
||||
|
||||
client_fd = make_client(SOCK_STREAM, connect_ip, EXT_PORT);
|
||||
if (client_fd < 0)
|
||||
server_cookie = socket_cookie(server_fds[SERVER_B]);
|
||||
if (!server_cookie)
|
||||
return;
|
||||
|
||||
err = bpf_prog_test_run_opts(bpf_program__fd(lookup_prog), &opts);
|
||||
if (CHECK(err, "test_run", "failed with error %d\n", errno))
|
||||
goto close_servers;
|
||||
|
||||
peer_fd = accept(server_fds[SERVER_B], NULL, NULL);
|
||||
if (CHECK(peer_fd < 0, "accept", "failed\n"))
|
||||
goto close_client;
|
||||
if (CHECK(ctx.cookie == 0, "ctx.cookie", "no socket selected\n"))
|
||||
goto close_servers;
|
||||
|
||||
CHECK(ctx.cookie != server_cookie, "ctx.cookie",
|
||||
"selected sk %llu instead of %llu\n", ctx.cookie, server_cookie);
|
||||
|
||||
close(peer_fd);
|
||||
close_client:
|
||||
close(client_fd);
|
||||
close_servers:
|
||||
for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
|
||||
if (server_fds[i] != -1)
|
||||
close(server_fds[i]);
|
||||
}
|
||||
bpf_link__destroy(lookup_link);
|
||||
}
|
||||
|
||||
static void run_sk_assign_v4(struct test_sk_lookup *skel,
|
||||
|
||||
@@ -1014,8 +1014,8 @@ static void test_skb_redir_to_connected(struct test_sockmap_listen *skel,
|
||||
struct bpf_map *inner_map, int family,
|
||||
int sotype)
|
||||
{
|
||||
int verdict = bpf_program__fd(skel->progs.prog_skb_verdict);
|
||||
int parser = bpf_program__fd(skel->progs.prog_skb_parser);
|
||||
int verdict = bpf_program__fd(skel->progs.prog_stream_verdict);
|
||||
int parser = bpf_program__fd(skel->progs.prog_stream_parser);
|
||||
int verdict_map = bpf_map__fd(skel->maps.verdict_map);
|
||||
int sock_map = bpf_map__fd(inner_map);
|
||||
int err;
|
||||
@@ -1125,8 +1125,8 @@ static void test_skb_redir_to_listening(struct test_sockmap_listen *skel,
|
||||
struct bpf_map *inner_map, int family,
|
||||
int sotype)
|
||||
{
|
||||
int verdict = bpf_program__fd(skel->progs.prog_skb_verdict);
|
||||
int parser = bpf_program__fd(skel->progs.prog_skb_parser);
|
||||
int verdict = bpf_program__fd(skel->progs.prog_stream_verdict);
|
||||
int parser = bpf_program__fd(skel->progs.prog_stream_parser);
|
||||
int verdict_map = bpf_map__fd(skel->maps.verdict_map);
|
||||
int sock_map = bpf_map__fd(inner_map);
|
||||
int err;
|
||||
|
||||
92
tools/testing/selftests/bpf/prog_tests/task_local_storage.c
Normal file
92
tools/testing/selftests/bpf/prog_tests/task_local_storage.c
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
|
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h> /* For SYS_xxx definitions */
|
||||
#include <sys/types.h>
|
||||
#include <test_progs.h>
|
||||
#include "task_local_storage.skel.h"
|
||||
#include "task_local_storage_exit_creds.skel.h"
|
||||
#include "task_ls_recursion.skel.h"
|
||||
|
||||
static void test_sys_enter_exit(void)
|
||||
{
|
||||
struct task_local_storage *skel;
|
||||
int err;
|
||||
|
||||
skel = task_local_storage__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
|
||||
return;
|
||||
|
||||
skel->bss->target_pid = syscall(SYS_gettid);
|
||||
|
||||
err = task_local_storage__attach(skel);
|
||||
if (!ASSERT_OK(err, "skel_attach"))
|
||||
goto out;
|
||||
|
||||
syscall(SYS_gettid);
|
||||
syscall(SYS_gettid);
|
||||
|
||||
/* 3x syscalls: 1x attach and 2x gettid */
|
||||
ASSERT_EQ(skel->bss->enter_cnt, 3, "enter_cnt");
|
||||
ASSERT_EQ(skel->bss->exit_cnt, 3, "exit_cnt");
|
||||
ASSERT_EQ(skel->bss->mismatch_cnt, 0, "mismatch_cnt");
|
||||
out:
|
||||
task_local_storage__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_exit_creds(void)
|
||||
{
|
||||
struct task_local_storage_exit_creds *skel;
|
||||
int err;
|
||||
|
||||
skel = task_local_storage_exit_creds__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
|
||||
return;
|
||||
|
||||
err = task_local_storage_exit_creds__attach(skel);
|
||||
if (!ASSERT_OK(err, "skel_attach"))
|
||||
goto out;
|
||||
|
||||
/* trigger at least one exit_creds() */
|
||||
if (CHECK_FAIL(system("ls > /dev/null")))
|
||||
goto out;
|
||||
|
||||
/* sync rcu to make sure exit_creds() is called for "ls" */
|
||||
kern_sync_rcu();
|
||||
ASSERT_EQ(skel->bss->valid_ptr_count, 0, "valid_ptr_count");
|
||||
ASSERT_NEQ(skel->bss->null_ptr_count, 0, "null_ptr_count");
|
||||
out:
|
||||
task_local_storage_exit_creds__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_recursion(void)
|
||||
{
|
||||
struct task_ls_recursion *skel;
|
||||
int err;
|
||||
|
||||
skel = task_ls_recursion__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
|
||||
return;
|
||||
|
||||
err = task_ls_recursion__attach(skel);
|
||||
if (!ASSERT_OK(err, "skel_attach"))
|
||||
goto out;
|
||||
|
||||
/* trigger sys_enter, make sure it does not cause deadlock */
|
||||
syscall(SYS_gettid);
|
||||
|
||||
out:
|
||||
task_ls_recursion__destroy(skel);
|
||||
}
|
||||
|
||||
void test_task_local_storage(void)
|
||||
{
|
||||
if (test__start_subtest("sys_enter_exit"))
|
||||
test_sys_enter_exit();
|
||||
if (test__start_subtest("exit_creds"))
|
||||
test_exit_creds();
|
||||
if (test__start_subtest("recursion"))
|
||||
test_recursion();
|
||||
}
|
||||
@@ -205,6 +205,12 @@ struct struct_with_embedded_stuff {
|
||||
int t[11];
|
||||
};
|
||||
|
||||
struct float_struct {
|
||||
float f;
|
||||
const double *d;
|
||||
volatile long double *ld;
|
||||
};
|
||||
|
||||
struct root_struct {
|
||||
enum e1 _1;
|
||||
enum e2 _2;
|
||||
@@ -219,6 +225,7 @@ struct root_struct {
|
||||
union_fwd_t *_12;
|
||||
union_fwd_ptr_t _13;
|
||||
struct struct_with_embedded_stuff _14;
|
||||
struct float_struct _15;
|
||||
};
|
||||
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
@@ -807,6 +807,7 @@ struct core_reloc_size_output {
|
||||
int arr_elem_sz;
|
||||
int ptr_sz;
|
||||
int enum_sz;
|
||||
int float_sz;
|
||||
};
|
||||
|
||||
struct core_reloc_size {
|
||||
@@ -816,6 +817,7 @@ struct core_reloc_size {
|
||||
int arr_field[4];
|
||||
void *ptr_field;
|
||||
enum { VALUE = 123 } enum_field;
|
||||
float float_field;
|
||||
};
|
||||
|
||||
struct core_reloc_size___diff_sz {
|
||||
@@ -825,6 +827,7 @@ struct core_reloc_size___diff_sz {
|
||||
char arr_field[10];
|
||||
void *ptr_field;
|
||||
enum { OTHER_VALUE = 0xFFFFFFFFFFFFFFFF } enum_field;
|
||||
double float_field;
|
||||
};
|
||||
|
||||
/* Error case of two candidates with the fields (int_field) at the same
|
||||
@@ -839,6 +842,7 @@ struct core_reloc_size___err_ambiguous1 {
|
||||
int arr_field[4];
|
||||
void *ptr_field;
|
||||
enum { VALUE___1 = 123 } enum_field;
|
||||
float float_field;
|
||||
};
|
||||
|
||||
struct core_reloc_size___err_ambiguous2 {
|
||||
@@ -850,6 +854,7 @@ struct core_reloc_size___err_ambiguous2 {
|
||||
int arr_field[4];
|
||||
void *ptr_field;
|
||||
enum { VALUE___2 = 123 } enum_field;
|
||||
float float_field;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
61
tools/testing/selftests/bpf/progs/for_each_array_map_elem.c
Normal file
61
tools/testing/selftests/bpf/progs/for_each_array_map_elem.c
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 3);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} arraymap SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} percpu_map SEC(".maps");
|
||||
|
||||
struct callback_ctx {
|
||||
int output;
|
||||
};
|
||||
|
||||
static __u64
|
||||
check_array_elem(struct bpf_map *map, __u32 *key, __u64 *val,
|
||||
struct callback_ctx *data)
|
||||
{
|
||||
data->output += *val;
|
||||
if (*key == 1)
|
||||
return 1; /* stop the iteration */
|
||||
return 0;
|
||||
}
|
||||
|
||||
__u32 cpu = 0;
|
||||
__u64 percpu_val = 0;
|
||||
|
||||
static __u64
|
||||
check_percpu_elem(struct bpf_map *map, __u32 *key, __u64 *val,
|
||||
struct callback_ctx *data)
|
||||
{
|
||||
cpu = bpf_get_smp_processor_id();
|
||||
percpu_val = *val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 arraymap_output = 0;
|
||||
|
||||
SEC("classifier")
|
||||
int test_pkt_access(struct __sk_buff *skb)
|
||||
{
|
||||
struct callback_ctx data;
|
||||
|
||||
data.output = 0;
|
||||
bpf_for_each_map_elem(&arraymap, check_array_elem, &data, 0);
|
||||
arraymap_output = data.output;
|
||||
|
||||
bpf_for_each_map_elem(&percpu_map, check_percpu_elem, (void *)0, 0);
|
||||
return 0;
|
||||
}
|
||||
95
tools/testing/selftests/bpf/progs/for_each_hash_map_elem.c
Normal file
95
tools/testing/selftests/bpf/progs/for_each_hash_map_elem.c
Normal file
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 3);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} hashmap SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} percpu_map SEC(".maps");
|
||||
|
||||
struct callback_ctx {
|
||||
struct __sk_buff *ctx;
|
||||
int input;
|
||||
int output;
|
||||
};
|
||||
|
||||
static __u64
|
||||
check_hash_elem(struct bpf_map *map, __u32 *key, __u64 *val,
|
||||
struct callback_ctx *data)
|
||||
{
|
||||
struct __sk_buff *skb = data->ctx;
|
||||
__u32 k;
|
||||
__u64 v;
|
||||
|
||||
if (skb) {
|
||||
k = *key;
|
||||
v = *val;
|
||||
if (skb->len == 10000 && k == 10 && v == 10)
|
||||
data->output = 3; /* impossible path */
|
||||
else
|
||||
data->output = 4;
|
||||
} else {
|
||||
data->output = data->input;
|
||||
bpf_map_delete_elem(map, key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__u32 cpu = 0;
|
||||
__u32 percpu_called = 0;
|
||||
__u32 percpu_key = 0;
|
||||
__u64 percpu_val = 0;
|
||||
int percpu_output = 0;
|
||||
|
||||
static __u64
|
||||
check_percpu_elem(struct bpf_map *map, __u32 *key, __u64 *val,
|
||||
struct callback_ctx *unused)
|
||||
{
|
||||
struct callback_ctx data;
|
||||
|
||||
percpu_called++;
|
||||
cpu = bpf_get_smp_processor_id();
|
||||
percpu_key = *key;
|
||||
percpu_val = *val;
|
||||
|
||||
data.ctx = 0;
|
||||
data.input = 100;
|
||||
data.output = 0;
|
||||
bpf_for_each_map_elem(&hashmap, check_hash_elem, &data, 0);
|
||||
percpu_output = data.output;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_output = 0;
|
||||
int hashmap_elems = 0;
|
||||
int percpu_map_elems = 0;
|
||||
|
||||
SEC("classifier")
|
||||
int test_pkt_access(struct __sk_buff *skb)
|
||||
{
|
||||
struct callback_ctx data;
|
||||
|
||||
data.ctx = skb;
|
||||
data.input = 10;
|
||||
data.output = 0;
|
||||
hashmap_elems = bpf_for_each_map_elem(&hashmap, check_hash_elem, &data, 0);
|
||||
hashmap_output = data.output;
|
||||
|
||||
percpu_map_elems = bpf_for_each_map_elem(&percpu_map, check_percpu_elem,
|
||||
(void *)0, 0);
|
||||
return 0;
|
||||
}
|
||||
99
tools/testing/selftests/bpf/progs/loop6.c
Normal file
99
tools/testing/selftests/bpf/progs/loop6.c
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
/* typically virtio scsi has max SGs of 6 */
|
||||
#define VIRTIO_MAX_SGS 6
|
||||
|
||||
/* Verifier will fail with SG_MAX = 128. The failure can be
|
||||
* workarounded with a smaller SG_MAX, e.g. 10.
|
||||
*/
|
||||
#define WORKAROUND
|
||||
#ifdef WORKAROUND
|
||||
#define SG_MAX 10
|
||||
#else
|
||||
/* typically virtio blk has max SEG of 128 */
|
||||
#define SG_MAX 128
|
||||
#endif
|
||||
|
||||
#define SG_CHAIN 0x01UL
|
||||
#define SG_END 0x02UL
|
||||
|
||||
struct scatterlist {
|
||||
unsigned long page_link;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
#define sg_is_chain(sg) ((sg)->page_link & SG_CHAIN)
|
||||
#define sg_is_last(sg) ((sg)->page_link & SG_END)
|
||||
#define sg_chain_ptr(sg) \
|
||||
((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))
|
||||
|
||||
static inline struct scatterlist *__sg_next(struct scatterlist *sgp)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
|
||||
bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
|
||||
if (sg_is_last(&sg))
|
||||
return NULL;
|
||||
|
||||
sgp++;
|
||||
|
||||
bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
|
||||
if (sg_is_chain(&sg))
|
||||
sgp = sg_chain_ptr(&sg);
|
||||
|
||||
return sgp;
|
||||
}
|
||||
|
||||
static inline struct scatterlist *get_sgp(struct scatterlist **sgs, int i)
|
||||
{
|
||||
struct scatterlist *sgp;
|
||||
|
||||
bpf_probe_read_kernel(&sgp, sizeof(sgp), sgs + i);
|
||||
return sgp;
|
||||
}
|
||||
|
||||
int config = 0;
|
||||
int result = 0;
|
||||
|
||||
SEC("kprobe/virtqueue_add_sgs")
|
||||
int BPF_KPROBE(trace_virtqueue_add_sgs, void *unused, struct scatterlist **sgs,
|
||||
unsigned int out_sgs, unsigned int in_sgs)
|
||||
{
|
||||
struct scatterlist *sgp = NULL;
|
||||
__u64 length1 = 0, length2 = 0;
|
||||
unsigned int i, n, len;
|
||||
|
||||
if (config != 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; (i < VIRTIO_MAX_SGS) && (i < out_sgs); i++) {
|
||||
for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
|
||||
sgp = __sg_next(sgp)) {
|
||||
bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
|
||||
length1 += len;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; (i < VIRTIO_MAX_SGS) && (i < in_sgs); i++) {
|
||||
for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
|
||||
sgp = __sg_next(sgp)) {
|
||||
bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
|
||||
length2 += len;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
config = 1;
|
||||
result = length2 - length1;
|
||||
return 0;
|
||||
}
|
||||
64
tools/testing/selftests/bpf/progs/task_local_storage.c
Normal file
64
tools/testing/selftests/bpf/progs/task_local_storage.c
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, long);
|
||||
} enter_id SEC(".maps");
|
||||
|
||||
#define MAGIC_VALUE 0xabcd1234
|
||||
|
||||
pid_t target_pid = 0;
|
||||
int mismatch_cnt = 0;
|
||||
int enter_cnt = 0;
|
||||
int exit_cnt = 0;
|
||||
|
||||
SEC("tp_btf/sys_enter")
|
||||
int BPF_PROG(on_enter, struct pt_regs *regs, long id)
|
||||
{
|
||||
struct task_struct *task;
|
||||
long *ptr;
|
||||
|
||||
task = bpf_get_current_task_btf();
|
||||
if (task->pid != target_pid)
|
||||
return 0;
|
||||
|
||||
ptr = bpf_task_storage_get(&enter_id, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (!ptr)
|
||||
return 0;
|
||||
|
||||
__sync_fetch_and_add(&enter_cnt, 1);
|
||||
*ptr = MAGIC_VALUE + enter_cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/sys_exit")
|
||||
int BPF_PROG(on_exit, struct pt_regs *regs, long id)
|
||||
{
|
||||
struct task_struct *task;
|
||||
long *ptr;
|
||||
|
||||
task = bpf_get_current_task_btf();
|
||||
if (task->pid != target_pid)
|
||||
return 0;
|
||||
|
||||
ptr = bpf_task_storage_get(&enter_id, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (!ptr)
|
||||
return 0;
|
||||
|
||||
__sync_fetch_and_add(&exit_cnt, 1);
|
||||
if (*ptr != MAGIC_VALUE + exit_cnt)
|
||||
__sync_fetch_and_add(&mismatch_cnt, 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, __u64);
|
||||
} task_storage SEC(".maps");
|
||||
|
||||
int valid_ptr_count = 0;
|
||||
int null_ptr_count = 0;
|
||||
|
||||
SEC("fentry/exit_creds")
|
||||
int BPF_PROG(trace_exit_creds, struct task_struct *task)
|
||||
{
|
||||
__u64 *ptr;
|
||||
|
||||
ptr = bpf_task_storage_get(&task_storage, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (ptr)
|
||||
__sync_fetch_and_add(&valid_ptr_count, 1);
|
||||
else
|
||||
__sync_fetch_and_add(&null_ptr_count, 1);
|
||||
return 0;
|
||||
}
|
||||
70
tools/testing/selftests/bpf/progs/task_ls_recursion.c
Normal file
70
tools/testing/selftests/bpf/progs/task_ls_recursion.c
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2021 Facebook */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, long);
|
||||
} map_a SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, long);
|
||||
} map_b SEC(".maps");
|
||||
|
||||
SEC("fentry/bpf_local_storage_lookup")
|
||||
int BPF_PROG(on_lookup)
|
||||
{
|
||||
struct task_struct *task = bpf_get_current_task_btf();
|
||||
|
||||
bpf_task_storage_delete(&map_a, task);
|
||||
bpf_task_storage_delete(&map_b, task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/bpf_local_storage_update")
|
||||
int BPF_PROG(on_update)
|
||||
{
|
||||
struct task_struct *task = bpf_get_current_task_btf();
|
||||
long *ptr;
|
||||
|
||||
ptr = bpf_task_storage_get(&map_a, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (ptr)
|
||||
*ptr += 1;
|
||||
|
||||
ptr = bpf_task_storage_get(&map_b, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (ptr)
|
||||
*ptr += 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/sys_enter")
|
||||
int BPF_PROG(on_enter, struct pt_regs *regs, long id)
|
||||
{
|
||||
struct task_struct *task;
|
||||
long *ptr;
|
||||
|
||||
task = bpf_get_current_task_btf();
|
||||
ptr = bpf_task_storage_get(&map_a, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (ptr)
|
||||
*ptr = 200;
|
||||
|
||||
ptr = bpf_task_storage_get(&map_b, task, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (ptr)
|
||||
*ptr = 100;
|
||||
return 0;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ struct core_reloc_size_output {
|
||||
int arr_elem_sz;
|
||||
int ptr_sz;
|
||||
int enum_sz;
|
||||
int float_sz;
|
||||
};
|
||||
|
||||
struct core_reloc_size {
|
||||
@@ -30,6 +31,7 @@ struct core_reloc_size {
|
||||
int arr_field[4];
|
||||
void *ptr_field;
|
||||
enum { VALUE = 123 } enum_field;
|
||||
float float_field;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
@@ -45,6 +47,7 @@ int test_core_size(void *ctx)
|
||||
out->arr_elem_sz = bpf_core_field_size(in->arr_field[0]);
|
||||
out->ptr_sz = bpf_core_field_size(in->ptr_field);
|
||||
out->enum_sz = bpf_core_field_size(in->enum_field);
|
||||
out->float_sz = bpf_core_field_size(in->float_field);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ static const int PROG_DONE = 1;
|
||||
static const __u32 KEY_SERVER_A = SERVER_A;
|
||||
static const __u32 KEY_SERVER_B = SERVER_B;
|
||||
|
||||
static const __u16 SRC_PORT = bpf_htons(8008);
|
||||
static const __u32 SRC_IP4 = IP4(127, 0, 0, 2);
|
||||
static const __u32 SRC_IP6[] = IP6(0xfd000000, 0x0, 0x0, 0x00000002);
|
||||
|
||||
static const __u16 DST_PORT = 7007; /* Host byte order */
|
||||
static const __u32 DST_IP4 = IP4(127, 0, 0, 1);
|
||||
static const __u32 DST_IP6[] = IP6(0xfd000000, 0x0, 0x0, 0x00000001);
|
||||
@@ -398,11 +402,12 @@ int ctx_narrow_access(struct bpf_sk_lookup *ctx)
|
||||
if (LSW(ctx->protocol, 0) != IPPROTO_TCP)
|
||||
return SK_DROP;
|
||||
|
||||
/* Narrow loads from remote_port field. Expect non-0 value. */
|
||||
if (LSB(ctx->remote_port, 0) == 0 && LSB(ctx->remote_port, 1) == 0 &&
|
||||
LSB(ctx->remote_port, 2) == 0 && LSB(ctx->remote_port, 3) == 0)
|
||||
/* Narrow loads from remote_port field. Expect SRC_PORT. */
|
||||
if (LSB(ctx->remote_port, 0) != ((SRC_PORT >> 0) & 0xff) ||
|
||||
LSB(ctx->remote_port, 1) != ((SRC_PORT >> 8) & 0xff) ||
|
||||
LSB(ctx->remote_port, 2) != 0 || LSB(ctx->remote_port, 3) != 0)
|
||||
return SK_DROP;
|
||||
if (LSW(ctx->remote_port, 0) == 0)
|
||||
if (LSW(ctx->remote_port, 0) != SRC_PORT)
|
||||
return SK_DROP;
|
||||
|
||||
/* Narrow loads from local_port field. Expect DST_PORT. */
|
||||
@@ -415,11 +420,14 @@ int ctx_narrow_access(struct bpf_sk_lookup *ctx)
|
||||
|
||||
/* Narrow loads from IPv4 fields */
|
||||
if (v4) {
|
||||
/* Expect non-0.0.0.0 in remote_ip4 */
|
||||
if (LSB(ctx->remote_ip4, 0) == 0 && LSB(ctx->remote_ip4, 1) == 0 &&
|
||||
LSB(ctx->remote_ip4, 2) == 0 && LSB(ctx->remote_ip4, 3) == 0)
|
||||
/* Expect SRC_IP4 in remote_ip4 */
|
||||
if (LSB(ctx->remote_ip4, 0) != ((SRC_IP4 >> 0) & 0xff) ||
|
||||
LSB(ctx->remote_ip4, 1) != ((SRC_IP4 >> 8) & 0xff) ||
|
||||
LSB(ctx->remote_ip4, 2) != ((SRC_IP4 >> 16) & 0xff) ||
|
||||
LSB(ctx->remote_ip4, 3) != ((SRC_IP4 >> 24) & 0xff))
|
||||
return SK_DROP;
|
||||
if (LSW(ctx->remote_ip4, 0) == 0 && LSW(ctx->remote_ip4, 1) == 0)
|
||||
if (LSW(ctx->remote_ip4, 0) != ((SRC_IP4 >> 0) & 0xffff) ||
|
||||
LSW(ctx->remote_ip4, 1) != ((SRC_IP4 >> 16) & 0xffff))
|
||||
return SK_DROP;
|
||||
|
||||
/* Expect DST_IP4 in local_ip4 */
|
||||
@@ -448,20 +456,32 @@ int ctx_narrow_access(struct bpf_sk_lookup *ctx)
|
||||
|
||||
/* Narrow loads from IPv6 fields */
|
||||
if (!v4) {
|
||||
/* Expect non-:: IP in remote_ip6 */
|
||||
if (LSB(ctx->remote_ip6[0], 0) == 0 && LSB(ctx->remote_ip6[0], 1) == 0 &&
|
||||
LSB(ctx->remote_ip6[0], 2) == 0 && LSB(ctx->remote_ip6[0], 3) == 0 &&
|
||||
LSB(ctx->remote_ip6[1], 0) == 0 && LSB(ctx->remote_ip6[1], 1) == 0 &&
|
||||
LSB(ctx->remote_ip6[1], 2) == 0 && LSB(ctx->remote_ip6[1], 3) == 0 &&
|
||||
LSB(ctx->remote_ip6[2], 0) == 0 && LSB(ctx->remote_ip6[2], 1) == 0 &&
|
||||
LSB(ctx->remote_ip6[2], 2) == 0 && LSB(ctx->remote_ip6[2], 3) == 0 &&
|
||||
LSB(ctx->remote_ip6[3], 0) == 0 && LSB(ctx->remote_ip6[3], 1) == 0 &&
|
||||
LSB(ctx->remote_ip6[3], 2) == 0 && LSB(ctx->remote_ip6[3], 3) == 0)
|
||||
/* Expect SRC_IP6 in remote_ip6 */
|
||||
if (LSB(ctx->remote_ip6[0], 0) != ((SRC_IP6[0] >> 0) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[0], 1) != ((SRC_IP6[0] >> 8) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[0], 2) != ((SRC_IP6[0] >> 16) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[0], 3) != ((SRC_IP6[0] >> 24) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[1], 0) != ((SRC_IP6[1] >> 0) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[1], 1) != ((SRC_IP6[1] >> 8) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[1], 2) != ((SRC_IP6[1] >> 16) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[1], 3) != ((SRC_IP6[1] >> 24) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[2], 0) != ((SRC_IP6[2] >> 0) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[2], 1) != ((SRC_IP6[2] >> 8) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[2], 2) != ((SRC_IP6[2] >> 16) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[2], 3) != ((SRC_IP6[2] >> 24) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[3], 0) != ((SRC_IP6[3] >> 0) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[3], 1) != ((SRC_IP6[3] >> 8) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[3], 2) != ((SRC_IP6[3] >> 16) & 0xff) ||
|
||||
LSB(ctx->remote_ip6[3], 3) != ((SRC_IP6[3] >> 24) & 0xff))
|
||||
return SK_DROP;
|
||||
if (LSW(ctx->remote_ip6[0], 0) == 0 && LSW(ctx->remote_ip6[0], 1) == 0 &&
|
||||
LSW(ctx->remote_ip6[1], 0) == 0 && LSW(ctx->remote_ip6[1], 1) == 0 &&
|
||||
LSW(ctx->remote_ip6[2], 0) == 0 && LSW(ctx->remote_ip6[2], 1) == 0 &&
|
||||
LSW(ctx->remote_ip6[3], 0) == 0 && LSW(ctx->remote_ip6[3], 1) == 0)
|
||||
if (LSW(ctx->remote_ip6[0], 0) != ((SRC_IP6[0] >> 0) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[0], 1) != ((SRC_IP6[0] >> 16) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[1], 0) != ((SRC_IP6[1] >> 0) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[1], 1) != ((SRC_IP6[1] >> 16) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[2], 0) != ((SRC_IP6[2] >> 0) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[2], 1) != ((SRC_IP6[2] >> 16) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[3], 0) != ((SRC_IP6[3] >> 0) & 0xffff) ||
|
||||
LSW(ctx->remote_ip6[3], 1) != ((SRC_IP6[3] >> 16) & 0xffff))
|
||||
return SK_DROP;
|
||||
/* Expect DST_IP6 in local_ip6 */
|
||||
if (LSB(ctx->local_ip6[0], 0) != ((DST_IP6[0] >> 0) & 0xff) ||
|
||||
|
||||
@@ -31,13 +31,13 @@ struct {
|
||||
static volatile bool test_sockmap; /* toggled by user-space */
|
||||
|
||||
SEC("sk_skb/stream_parser")
|
||||
int prog_skb_parser(struct __sk_buff *skb)
|
||||
int prog_stream_parser(struct __sk_buff *skb)
|
||||
{
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
SEC("sk_skb/stream_verdict")
|
||||
int prog_skb_verdict(struct __sk_buff *skb)
|
||||
int prog_stream_verdict(struct __sk_buff *skb)
|
||||
{
|
||||
unsigned int *count;
|
||||
__u32 zero = 0;
|
||||
|
||||
@@ -24,14 +24,29 @@ static const int cfg_port = 8000;
|
||||
|
||||
static const int cfg_udp_src = 20000;
|
||||
|
||||
#define L2_PAD_SZ (sizeof(struct vxlanhdr) + ETH_HLEN)
|
||||
|
||||
#define UDP_PORT 5555
|
||||
#define MPLS_OVER_UDP_PORT 6635
|
||||
#define ETH_OVER_UDP_PORT 7777
|
||||
#define VXLAN_UDP_PORT 8472
|
||||
|
||||
#define EXTPROTO_VXLAN 0x1
|
||||
|
||||
#define VXLAN_N_VID (1u << 24)
|
||||
#define VXLAN_VNI_MASK bpf_htonl((VXLAN_N_VID - 1) << 8)
|
||||
#define VXLAN_FLAGS 0x8
|
||||
#define VXLAN_VNI 1
|
||||
|
||||
/* MPLS label 1000 with S bit (last label) set and ttl of 255. */
|
||||
static const __u32 mpls_label = __bpf_constant_htonl(1000 << 12 |
|
||||
MPLS_LS_S_MASK | 0xff);
|
||||
|
||||
struct vxlanhdr {
|
||||
__be32 vx_flags;
|
||||
__be32 vx_vni;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gre_hdr {
|
||||
__be16 flags;
|
||||
__be16 protocol;
|
||||
@@ -45,13 +60,13 @@ union l4hdr {
|
||||
struct v4hdr {
|
||||
struct iphdr ip;
|
||||
union l4hdr l4hdr;
|
||||
__u8 pad[16]; /* enough space for L2 header */
|
||||
__u8 pad[L2_PAD_SZ]; /* space for L2 header / vxlan header ... */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct v6hdr {
|
||||
struct ipv6hdr ip;
|
||||
union l4hdr l4hdr;
|
||||
__u8 pad[16]; /* enough space for L2 header */
|
||||
__u8 pad[L2_PAD_SZ]; /* space for L2 header / vxlan header ... */
|
||||
} __attribute__((packed));
|
||||
|
||||
static __always_inline void set_ipv4_csum(struct iphdr *iph)
|
||||
@@ -69,14 +84,15 @@ static __always_inline void set_ipv4_csum(struct iphdr *iph)
|
||||
iph->check = ~((csum & 0xffff) + (csum >> 16));
|
||||
}
|
||||
|
||||
static __always_inline int encap_ipv4(struct __sk_buff *skb, __u8 encap_proto,
|
||||
__u16 l2_proto)
|
||||
static __always_inline int __encap_ipv4(struct __sk_buff *skb, __u8 encap_proto,
|
||||
__u16 l2_proto, __u16 ext_proto)
|
||||
{
|
||||
__u16 udp_dst = UDP_PORT;
|
||||
struct iphdr iph_inner;
|
||||
struct v4hdr h_outer;
|
||||
struct tcphdr tcph;
|
||||
int olen, l2_len;
|
||||
__u8 *l2_hdr = NULL;
|
||||
int tcp_off;
|
||||
__u64 flags;
|
||||
|
||||
@@ -141,7 +157,11 @@ static __always_inline int encap_ipv4(struct __sk_buff *skb, __u8 encap_proto,
|
||||
break;
|
||||
case ETH_P_TEB:
|
||||
l2_len = ETH_HLEN;
|
||||
udp_dst = ETH_OVER_UDP_PORT;
|
||||
if (ext_proto & EXTPROTO_VXLAN) {
|
||||
udp_dst = VXLAN_UDP_PORT;
|
||||
l2_len += sizeof(struct vxlanhdr);
|
||||
} else
|
||||
udp_dst = ETH_OVER_UDP_PORT;
|
||||
break;
|
||||
}
|
||||
flags |= BPF_F_ADJ_ROOM_ENCAP_L2(l2_len);
|
||||
@@ -171,14 +191,26 @@ static __always_inline int encap_ipv4(struct __sk_buff *skb, __u8 encap_proto,
|
||||
}
|
||||
|
||||
/* add L2 encap (if specified) */
|
||||
l2_hdr = (__u8 *)&h_outer + olen;
|
||||
switch (l2_proto) {
|
||||
case ETH_P_MPLS_UC:
|
||||
*((__u32 *)((__u8 *)&h_outer + olen)) = mpls_label;
|
||||
*(__u32 *)l2_hdr = mpls_label;
|
||||
break;
|
||||
case ETH_P_TEB:
|
||||
if (bpf_skb_load_bytes(skb, 0, (__u8 *)&h_outer + olen,
|
||||
ETH_HLEN))
|
||||
flags |= BPF_F_ADJ_ROOM_ENCAP_L2_ETH;
|
||||
|
||||
if (ext_proto & EXTPROTO_VXLAN) {
|
||||
struct vxlanhdr *vxlan_hdr = (struct vxlanhdr *)l2_hdr;
|
||||
|
||||
vxlan_hdr->vx_flags = VXLAN_FLAGS;
|
||||
vxlan_hdr->vx_vni = bpf_htonl((VXLAN_VNI & VXLAN_VNI_MASK) << 8);
|
||||
|
||||
l2_hdr += sizeof(struct vxlanhdr);
|
||||
}
|
||||
|
||||
if (bpf_skb_load_bytes(skb, 0, l2_hdr, ETH_HLEN))
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
break;
|
||||
}
|
||||
olen += l2_len;
|
||||
@@ -214,14 +246,21 @@ static __always_inline int encap_ipv4(struct __sk_buff *skb, __u8 encap_proto,
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
static __always_inline int encap_ipv4(struct __sk_buff *skb, __u8 encap_proto,
|
||||
__u16 l2_proto)
|
||||
{
|
||||
return __encap_ipv4(skb, encap_proto, l2_proto, 0);
|
||||
}
|
||||
|
||||
static __always_inline int __encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
__u16 l2_proto, __u16 ext_proto)
|
||||
{
|
||||
__u16 udp_dst = UDP_PORT;
|
||||
struct ipv6hdr iph_inner;
|
||||
struct v6hdr h_outer;
|
||||
struct tcphdr tcph;
|
||||
int olen, l2_len;
|
||||
__u8 *l2_hdr = NULL;
|
||||
__u16 tot_len;
|
||||
__u64 flags;
|
||||
|
||||
@@ -249,7 +288,11 @@ static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
break;
|
||||
case ETH_P_TEB:
|
||||
l2_len = ETH_HLEN;
|
||||
udp_dst = ETH_OVER_UDP_PORT;
|
||||
if (ext_proto & EXTPROTO_VXLAN) {
|
||||
udp_dst = VXLAN_UDP_PORT;
|
||||
l2_len += sizeof(struct vxlanhdr);
|
||||
} else
|
||||
udp_dst = ETH_OVER_UDP_PORT;
|
||||
break;
|
||||
}
|
||||
flags |= BPF_F_ADJ_ROOM_ENCAP_L2(l2_len);
|
||||
@@ -267,7 +310,7 @@ static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
h_outer.l4hdr.udp.source = __bpf_constant_htons(cfg_udp_src);
|
||||
h_outer.l4hdr.udp.dest = bpf_htons(udp_dst);
|
||||
tot_len = bpf_ntohs(iph_inner.payload_len) + sizeof(iph_inner) +
|
||||
sizeof(h_outer.l4hdr.udp);
|
||||
sizeof(h_outer.l4hdr.udp) + l2_len;
|
||||
h_outer.l4hdr.udp.check = 0;
|
||||
h_outer.l4hdr.udp.len = bpf_htons(tot_len);
|
||||
break;
|
||||
@@ -278,13 +321,24 @@ static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
}
|
||||
|
||||
/* add L2 encap (if specified) */
|
||||
l2_hdr = (__u8 *)&h_outer + olen;
|
||||
switch (l2_proto) {
|
||||
case ETH_P_MPLS_UC:
|
||||
*((__u32 *)((__u8 *)&h_outer + olen)) = mpls_label;
|
||||
*(__u32 *)l2_hdr = mpls_label;
|
||||
break;
|
||||
case ETH_P_TEB:
|
||||
if (bpf_skb_load_bytes(skb, 0, (__u8 *)&h_outer + olen,
|
||||
ETH_HLEN))
|
||||
flags |= BPF_F_ADJ_ROOM_ENCAP_L2_ETH;
|
||||
|
||||
if (ext_proto & EXTPROTO_VXLAN) {
|
||||
struct vxlanhdr *vxlan_hdr = (struct vxlanhdr *)l2_hdr;
|
||||
|
||||
vxlan_hdr->vx_flags = VXLAN_FLAGS;
|
||||
vxlan_hdr->vx_vni = bpf_htonl((VXLAN_VNI & VXLAN_VNI_MASK) << 8);
|
||||
|
||||
l2_hdr += sizeof(struct vxlanhdr);
|
||||
}
|
||||
|
||||
if (bpf_skb_load_bytes(skb, 0, l2_hdr, ETH_HLEN))
|
||||
return TC_ACT_SHOT;
|
||||
break;
|
||||
}
|
||||
@@ -309,6 +363,12 @@ static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
static __always_inline int encap_ipv6(struct __sk_buff *skb, __u8 encap_proto,
|
||||
__u16 l2_proto)
|
||||
{
|
||||
return __encap_ipv6(skb, encap_proto, l2_proto, 0);
|
||||
}
|
||||
|
||||
SEC("encap_ipip_none")
|
||||
int __encap_ipip_none(struct __sk_buff *skb)
|
||||
{
|
||||
@@ -372,6 +432,17 @@ int __encap_udp_eth(struct __sk_buff *skb)
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
SEC("encap_vxlan_eth")
|
||||
int __encap_vxlan_eth(struct __sk_buff *skb)
|
||||
{
|
||||
if (skb->protocol == __bpf_constant_htons(ETH_P_IP))
|
||||
return __encap_ipv4(skb, IPPROTO_UDP,
|
||||
ETH_P_TEB,
|
||||
EXTPROTO_VXLAN);
|
||||
else
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
SEC("encap_sit_none")
|
||||
int __encap_sit_none(struct __sk_buff *skb)
|
||||
{
|
||||
@@ -444,6 +515,17 @@ int __encap_ip6udp_eth(struct __sk_buff *skb)
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
SEC("encap_ip6vxlan_eth")
|
||||
int __encap_ip6vxlan_eth(struct __sk_buff *skb)
|
||||
{
|
||||
if (skb->protocol == __bpf_constant_htons(ETH_P_IPV6))
|
||||
return __encap_ipv6(skb, IPPROTO_UDP,
|
||||
ETH_P_TEB,
|
||||
EXTPROTO_VXLAN);
|
||||
else
|
||||
return TC_ACT_OK;
|
||||
}
|
||||
|
||||
static int decap_internal(struct __sk_buff *skb, int off, int len, char proto)
|
||||
{
|
||||
char buf[sizeof(struct v6hdr)];
|
||||
@@ -479,6 +561,9 @@ static int decap_internal(struct __sk_buff *skb, int off, int len, char proto)
|
||||
case ETH_OVER_UDP_PORT:
|
||||
olen += ETH_HLEN;
|
||||
break;
|
||||
case VXLAN_UDP_PORT:
|
||||
olen += ETH_HLEN + sizeof(struct vxlanhdr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -85,23 +85,6 @@ make_with_tmpdir() {
|
||||
echo
|
||||
}
|
||||
|
||||
make_doc_and_clean() {
|
||||
echo -e "\$PWD: $PWD"
|
||||
echo -e "command: make -s $* doc >/dev/null"
|
||||
RST2MAN_OPTS="--exit-status=1" make $J -s $* doc
|
||||
if [ $? -ne 0 ] ; then
|
||||
ERROR=1
|
||||
printf "FAILURE: Errors or warnings when building documentation\n"
|
||||
fi
|
||||
(
|
||||
if [ $# -ge 1 ] ; then
|
||||
cd ${@: -1}
|
||||
fi
|
||||
make -s doc-clean
|
||||
)
|
||||
echo
|
||||
}
|
||||
|
||||
echo "Trying to build bpftool"
|
||||
echo -e "... through kbuild\n"
|
||||
|
||||
@@ -162,7 +145,3 @@ make_and_clean
|
||||
make_with_tmpdir OUTPUT
|
||||
|
||||
make_with_tmpdir O
|
||||
|
||||
echo -e "Checking documentation build\n"
|
||||
# From tools/bpf/bpftool
|
||||
make_doc_and_clean
|
||||
|
||||
@@ -66,4 +66,7 @@
|
||||
#define BTF_FUNC_ENC(name, func_proto) \
|
||||
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), func_proto)
|
||||
|
||||
#define BTF_TYPE_FLOAT_ENC(name, sz) \
|
||||
BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FLOAT, 0, 0), sz)
|
||||
|
||||
#endif /* _TEST_BTF_H */
|
||||
|
||||
13
tools/testing/selftests/bpf/test_doc_build.sh
Executable file
13
tools/testing/selftests/bpf/test_doc_build.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
# 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
|
||||
|
||||
for tgt in docs docs-clean; do
|
||||
make -s -C $PWD/$SCRIPT_REL_DIR $tgt;
|
||||
done
|
||||
@@ -152,6 +152,17 @@ extern int test__join_cgroup(const char *path);
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_LT(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
typeof(actual) ___act = (actual); \
|
||||
typeof(expected) ___exp = (expected); \
|
||||
bool ___ok = ___act < ___exp; \
|
||||
CHECK(!___ok, (name), \
|
||||
"unexpected %s: actual %lld >= expected %lld\n", \
|
||||
(name), (long long)(___act), (long long)(___exp)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_STREQ(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
const char *___act = actual; \
|
||||
|
||||
@@ -732,7 +732,7 @@ static int sendmsg_test(struct sockmap_options *opt)
|
||||
* socket is not a valid test. So in this case lets not
|
||||
* enable kTLS but still run the test.
|
||||
*/
|
||||
if (!txmsg_redir || (txmsg_redir && txmsg_ingress)) {
|
||||
if (!txmsg_redir || txmsg_ingress) {
|
||||
err = sockmap_init_ktls(opt->verbose, rx_fd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -44,8 +44,8 @@ setup() {
|
||||
# clamp route to reserve room for tunnel headers
|
||||
ip -netns "${ns1}" -4 route flush table main
|
||||
ip -netns "${ns1}" -6 route flush table main
|
||||
ip -netns "${ns1}" -4 route add "${ns2_v4}" mtu 1458 dev veth1
|
||||
ip -netns "${ns1}" -6 route add "${ns2_v6}" mtu 1438 dev veth1
|
||||
ip -netns "${ns1}" -4 route add "${ns2_v4}" mtu 1450 dev veth1
|
||||
ip -netns "${ns1}" -6 route add "${ns2_v6}" mtu 1430 dev veth1
|
||||
|
||||
sleep 1
|
||||
|
||||
@@ -105,6 +105,12 @@ if [[ "$#" -eq "0" ]]; then
|
||||
echo "sit"
|
||||
$0 ipv6 sit none 100
|
||||
|
||||
echo "ip4 vxlan"
|
||||
$0 ipv4 vxlan eth 2000
|
||||
|
||||
echo "ip6 vxlan"
|
||||
$0 ipv6 ip6vxlan eth 2000
|
||||
|
||||
for mac in none mpls eth ; do
|
||||
echo "ip gre $mac"
|
||||
$0 ipv4 gre $mac 100
|
||||
@@ -214,6 +220,9 @@ if [[ "$tuntype" =~ "udp" ]]; then
|
||||
targs="encap fou encap-sport auto encap-dport $dport"
|
||||
elif [[ "$tuntype" =~ "gre" && "$mac" == "eth" ]]; then
|
||||
ttype=$gretaptype
|
||||
elif [[ "$tuntype" =~ "vxlan" && "$mac" == "eth" ]]; then
|
||||
ttype="vxlan"
|
||||
targs="id 1 dstport 8472 udp6zerocsumrx"
|
||||
else
|
||||
ttype=$tuntype
|
||||
targs=""
|
||||
@@ -242,7 +251,7 @@ if [[ "$tuntype" == "ip6udp" && "$mac" == "mpls" ]]; then
|
||||
elif [[ "$tuntype" =~ "udp" && "$mac" == "eth" ]]; then
|
||||
# No support for TEB fou tunnel; expect failure.
|
||||
expect_tun_fail=1
|
||||
elif [[ "$tuntype" =~ "gre" && "$mac" == "eth" ]]; then
|
||||
elif [[ "$tuntype" =~ (gre|vxlan) && "$mac" == "eth" ]]; then
|
||||
# Share ethernet address between tunnel/veth2 so L2 decap works.
|
||||
ethaddr=$(ip netns exec "${ns2}" ip link show veth2 | \
|
||||
awk '/ether/ { print $2 }')
|
||||
|
||||
@@ -105,7 +105,7 @@ struct bpf_test {
|
||||
enum bpf_prog_type prog_type;
|
||||
uint8_t flags;
|
||||
void (*fill_helper)(struct bpf_test *self);
|
||||
uint8_t runs;
|
||||
int runs;
|
||||
#define bpf_testdata_struct_t \
|
||||
struct { \
|
||||
uint32_t retval, retval_unpriv; \
|
||||
@@ -1165,7 +1165,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||
|
||||
run_errs = 0;
|
||||
run_successes = 0;
|
||||
if (!alignment_prevented_execution && fd_prog >= 0) {
|
||||
if (!alignment_prevented_execution && fd_prog >= 0 && test->runs >= 0) {
|
||||
uint32_t expected_val;
|
||||
int i;
|
||||
|
||||
|
||||
@@ -71,13 +71,21 @@
|
||||
#
|
||||
# Run (full output without color-coding):
|
||||
# sudo ./test_xsk.sh
|
||||
#
|
||||
# Run with verbose output:
|
||||
# sudo ./test_xsk.sh -v
|
||||
#
|
||||
# Run and dump packet contents:
|
||||
# sudo ./test_xsk.sh -D
|
||||
|
||||
. xsk_prereqs.sh
|
||||
|
||||
while getopts c flag
|
||||
while getopts "cvD" flag
|
||||
do
|
||||
case "${flag}" in
|
||||
c) colorconsole=1;;
|
||||
v) verbose=1;;
|
||||
D) dump_pkts=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -95,13 +103,17 @@ NS1=af_xdp${VETH1_POSTFIX}
|
||||
MTU=1500
|
||||
|
||||
setup_vethPairs() {
|
||||
echo "setting up ${VETH0}: namespace: ${NS0}"
|
||||
if [[ $verbose -eq 1 ]]; then
|
||||
echo "setting up ${VETH0}: namespace: ${NS0}"
|
||||
fi
|
||||
ip netns add ${NS1}
|
||||
ip link add ${VETH0} type veth peer name ${VETH1}
|
||||
if [ -f /proc/net/if_inet6 ]; then
|
||||
echo 1 > /proc/sys/net/ipv6/conf/${VETH0}/disable_ipv6
|
||||
fi
|
||||
echo "setting up ${VETH1}: namespace: ${NS1}"
|
||||
if [[ $verbose -eq 1 ]]; then
|
||||
echo "setting up ${VETH1}: namespace: ${NS1}"
|
||||
fi
|
||||
ip link set ${VETH1} netns ${NS1}
|
||||
ip netns exec ${NS1} ip link set ${VETH1} mtu ${MTU}
|
||||
ip link set ${VETH0} mtu ${MTU}
|
||||
@@ -125,7 +137,14 @@ echo "${VETH0}:${VETH1},${NS1}" > ${SPECFILE}
|
||||
|
||||
validate_veth_spec_file
|
||||
|
||||
echo "Spec file created: ${SPECFILE}"
|
||||
if [[ $verbose -eq 1 ]]; then
|
||||
echo "Spec file created: ${SPECFILE}"
|
||||
VERBOSE_ARG="-v"
|
||||
fi
|
||||
|
||||
if [[ $dump_pkts -eq 1 ]]; then
|
||||
DUMP_PKTS_ARG="-D"
|
||||
fi
|
||||
|
||||
test_status $retval "${TEST_NAME}"
|
||||
|
||||
@@ -133,113 +152,9 @@ test_status $retval "${TEST_NAME}"
|
||||
|
||||
statusList=()
|
||||
|
||||
### TEST 1
|
||||
TEST_NAME="XSK KSELFTEST FRAMEWORK"
|
||||
TEST_NAME="XSK KSELFTESTS"
|
||||
|
||||
echo "Switching interfaces [${VETH0}, ${VETH1}] to XDP Generic mode"
|
||||
vethXDPgeneric ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
retval=$?
|
||||
if [ $retval -eq 0 ]; then
|
||||
echo "Switching interfaces [${VETH0}, ${VETH1}] to XDP Native mode"
|
||||
vethXDPnative ${VETH0} ${VETH1} ${NS1}
|
||||
fi
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 2
|
||||
TEST_NAME="SKB NOPOLL"
|
||||
|
||||
vethXDPgeneric ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-S")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 3
|
||||
TEST_NAME="SKB POLL"
|
||||
|
||||
vethXDPgeneric ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-S" "-p")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 4
|
||||
TEST_NAME="DRV NOPOLL"
|
||||
|
||||
vethXDPnative ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-N")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 5
|
||||
TEST_NAME="DRV POLL"
|
||||
|
||||
vethXDPnative ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-N" "-p")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 6
|
||||
TEST_NAME="SKB SOCKET TEARDOWN"
|
||||
|
||||
vethXDPgeneric ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-S" "-T")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 7
|
||||
TEST_NAME="DRV SOCKET TEARDOWN"
|
||||
|
||||
vethXDPnative ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-N" "-T")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 8
|
||||
TEST_NAME="SKB BIDIRECTIONAL SOCKETS"
|
||||
|
||||
vethXDPgeneric ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-S" "-B")
|
||||
execxdpxceiver params
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
statusList+=($retval)
|
||||
|
||||
### TEST 9
|
||||
TEST_NAME="DRV BIDIRECTIONAL SOCKETS"
|
||||
|
||||
vethXDPnative ${VETH0} ${VETH1} ${NS1}
|
||||
|
||||
params=("-N" "-B")
|
||||
execxdpxceiver params
|
||||
execxdpxceiver
|
||||
|
||||
retval=$?
|
||||
test_status $retval "${TEST_NAME}"
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SK_LOOKUP,
|
||||
.expected_attach_type = BPF_SK_LOOKUP,
|
||||
.runs = -1,
|
||||
},
|
||||
/* invalid 8-byte reads from a 4-byte fields in bpf_sk_lookup */
|
||||
{
|
||||
|
||||
@@ -17,6 +17,9 @@ KCONFIG_URL="https://raw.githubusercontent.com/libbpf/libbpf/master/travis-ci/vm
|
||||
KCONFIG_API_URL="https://api.github.com/repos/libbpf/libbpf/contents/travis-ci/vmtest/configs/latest.config"
|
||||
INDEX_URL="https://raw.githubusercontent.com/libbpf/libbpf/master/travis-ci/vmtest/configs/INDEX"
|
||||
NUM_COMPILE_JOBS="$(nproc)"
|
||||
LOG_FILE_BASE="$(date +"bpf_selftests.%Y-%m-%d_%H-%M-%S")"
|
||||
LOG_FILE="${LOG_FILE_BASE}.log"
|
||||
EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
|
||||
|
||||
usage()
|
||||
{
|
||||
@@ -146,7 +149,6 @@ update_init_script()
|
||||
local init_script_dir="${OUTPUT_DIR}/${MOUNT_DIR}/etc/rcS.d"
|
||||
local init_script="${init_script_dir}/S50-startup"
|
||||
local command="$1"
|
||||
local log_file="$2"
|
||||
|
||||
mount_image
|
||||
|
||||
@@ -163,11 +165,16 @@ EOF
|
||||
sudo bash -c "cat >${init_script}" <<EOF
|
||||
#!/bin/bash
|
||||
|
||||
# Have a default value in the exit status file
|
||||
# incase the VM is forcefully stopped.
|
||||
echo "130" > "/root/${EXIT_STATUS_FILE}"
|
||||
|
||||
{
|
||||
cd /root/bpf
|
||||
echo ${command}
|
||||
stdbuf -oL -eL ${command}
|
||||
} 2>&1 | tee /root/${log_file}
|
||||
echo "\$?" > "/root/${EXIT_STATUS_FILE}"
|
||||
} 2>&1 | tee "/root/${LOG_FILE}"
|
||||
poweroff -f
|
||||
EOF
|
||||
|
||||
@@ -221,10 +228,12 @@ EOF
|
||||
copy_logs()
|
||||
{
|
||||
local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
|
||||
local log_file="${mount_dir}/root/$1"
|
||||
local log_file="${mount_dir}/root/${LOG_FILE}"
|
||||
local exit_status_file="${mount_dir}/root/${EXIT_STATUS_FILE}"
|
||||
|
||||
mount_image
|
||||
sudo cp ${log_file} "${OUTPUT_DIR}"
|
||||
sudo cp ${exit_status_file} "${OUTPUT_DIR}"
|
||||
sudo rm -f ${log_file}
|
||||
unmount_image
|
||||
}
|
||||
@@ -263,7 +272,6 @@ main()
|
||||
{
|
||||
local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
local kernel_checkout=$(realpath "${script_dir}"/../../../../)
|
||||
local log_file="$(date +"bpf_selftests.%Y-%m-%d_%H-%M-%S.log")"
|
||||
# By default the script searches for the kernel in the checkout directory but
|
||||
# it also obeys environment variables O= and KBUILD_OUTPUT=
|
||||
local kernel_bzimage="${kernel_checkout}/${X86_BZIMAGE}"
|
||||
@@ -347,19 +355,23 @@ main()
|
||||
fi
|
||||
|
||||
update_selftests "${kernel_checkout}" "${make_command}"
|
||||
update_init_script "${command}" "${log_file}"
|
||||
update_init_script "${command}"
|
||||
run_vm "${kernel_bzimage}"
|
||||
copy_logs "${log_file}"
|
||||
echo "Logs saved in ${OUTPUT_DIR}/${log_file}"
|
||||
copy_logs
|
||||
echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
|
||||
}
|
||||
|
||||
catch()
|
||||
{
|
||||
local exit_code=$1
|
||||
local exit_status_file="${OUTPUT_DIR}/${EXIT_STATUS_FILE}"
|
||||
# This is just a cleanup and the directory may
|
||||
# have already been unmounted. So, don't let this
|
||||
# clobber the error code we intend to return.
|
||||
unmount_image || true
|
||||
if [[ -f "${exit_status_file}" ]]; then
|
||||
exit_code="$(cat ${exit_status_file})"
|
||||
fi
|
||||
exit ${exit_code}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,7 @@
|
||||
* These selftests test AF_XDP SKB and Native/DRV modes using veth
|
||||
* Virtual Ethernet interfaces.
|
||||
*
|
||||
* The following tests are run:
|
||||
*
|
||||
* 1. AF_XDP SKB mode
|
||||
* Generic mode XDP is driver independent, used when the driver does
|
||||
* not have support for XDP. Works on any netdevice using sockets and
|
||||
* generic XDP path. XDP hook from netif_receive_skb().
|
||||
* For each mode, the following tests are run:
|
||||
* a. nopoll - soft-irq processing
|
||||
* b. poll - using poll() syscall
|
||||
* c. Socket Teardown
|
||||
@@ -33,19 +28,21 @@
|
||||
* Configure sockets as bi-directional tx/rx sockets, sets up fill and
|
||||
* completion rings on each socket, tx/rx in both directions. Only nopoll
|
||||
* mode is used
|
||||
* e. Statistics
|
||||
* Trigger some error conditions and ensure that the appropriate statistics
|
||||
* are incremented. Within this test, the following statistics are tested:
|
||||
* i. rx dropped
|
||||
* Increase the UMEM frame headroom to a value which results in
|
||||
* insufficient space in the rx buffer for both the packet and the headroom.
|
||||
* ii. tx invalid
|
||||
* Set the 'len' field of tx descriptors to an invalid value (umem frame
|
||||
* size + 1).
|
||||
* iii. rx ring full
|
||||
* Reduce the size of the RX ring to a fraction of the fill ring size.
|
||||
* iv. fill queue empty
|
||||
* Do not populate the fill queue and then try to receive pkts.
|
||||
*
|
||||
* 2. AF_XDP DRV/Native mode
|
||||
* Works on any netdevice with XDP_REDIRECT support, driver dependent. Processes
|
||||
* packets before SKB allocation. Provides better performance than SKB. Driver
|
||||
* hook available just after DMA of buffer descriptor.
|
||||
* a. nopoll
|
||||
* b. poll
|
||||
* c. Socket Teardown
|
||||
* d. Bi-directional sockets
|
||||
* - Only copy mode is supported because veth does not currently support
|
||||
* zero-copy mode
|
||||
*
|
||||
* Total tests: 8
|
||||
* Total tests: 10
|
||||
*
|
||||
* Flow:
|
||||
* -----
|
||||
@@ -58,7 +55,7 @@
|
||||
* - Rx thread verifies if all 10k packets were received and delivered in-order,
|
||||
* and have the right content
|
||||
*
|
||||
* Enable/disable debug mode:
|
||||
* Enable/disable packet dump mode:
|
||||
* --------------------------
|
||||
* To enable L2 - L4 headers and payload dump of each packet on STDOUT, add
|
||||
* parameter -D to params array in test_xsk.sh, i.e. params=("-S" "-D")
|
||||
@@ -98,17 +95,24 @@ typedef __u16 __sum16;
|
||||
|
||||
static void __exit_with_error(int error, const char *file, const char *func, int line)
|
||||
{
|
||||
ksft_test_result_fail
|
||||
("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
|
||||
ksft_exit_xfail();
|
||||
if (configured_mode == TEST_MODE_UNCONFIGURED) {
|
||||
ksft_exit_fail_msg
|
||||
("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
|
||||
} else {
|
||||
ksft_test_result_fail
|
||||
("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, strerror(error));
|
||||
ksft_exit_xfail();
|
||||
}
|
||||
}
|
||||
|
||||
#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__)
|
||||
|
||||
#define print_ksft_result(void)\
|
||||
(ksft_test_result_pass("PASS: %s %s %s%s\n", uut ? "DRV" : "SKB", opt_poll ? "POLL" :\
|
||||
"NOPOLL", opt_teardown ? "Socket Teardown" : "",\
|
||||
opt_bidi ? "Bi-directional Sockets" : ""))
|
||||
(ksft_test_result_pass("PASS: %s %s %s%s%s\n", configured_mode ? "DRV" : "SKB",\
|
||||
test_type == TEST_TYPE_POLL ? "POLL" : "NOPOLL",\
|
||||
test_type == TEST_TYPE_TEARDOWN ? "Socket Teardown" : "",\
|
||||
test_type == TEST_TYPE_BIDI ? "Bi-directional Sockets" : "",\
|
||||
test_type == TEST_TYPE_STATS ? "Stats" : ""))
|
||||
|
||||
static void pthread_init_mutex(void)
|
||||
{
|
||||
@@ -270,13 +274,20 @@ static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr)
|
||||
static void xsk_configure_umem(struct ifobject *data, void *buffer, u64 size)
|
||||
{
|
||||
int ret;
|
||||
struct xsk_umem_config cfg = {
|
||||
.fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS,
|
||||
.comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS,
|
||||
.frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE,
|
||||
.frame_headroom = frame_headroom,
|
||||
.flags = XSK_UMEM__DEFAULT_FLAGS
|
||||
};
|
||||
|
||||
data->umem = calloc(1, sizeof(struct xsk_umem_info));
|
||||
if (!data->umem)
|
||||
exit_with_error(errno);
|
||||
|
||||
ret = xsk_umem__create(&data->umem->umem, buffer, size,
|
||||
&data->umem->fq, &data->umem->cq, NULL);
|
||||
&data->umem->fq, &data->umem->cq, &cfg);
|
||||
if (ret)
|
||||
exit_with_error(ret);
|
||||
|
||||
@@ -308,13 +319,13 @@ static int xsk_configure_socket(struct ifobject *ifobject)
|
||||
exit_with_error(errno);
|
||||
|
||||
ifobject->xsk->umem = ifobject->umem;
|
||||
cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
|
||||
cfg.rx_size = rxqsize;
|
||||
cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
|
||||
cfg.libbpf_flags = 0;
|
||||
cfg.xdp_flags = opt_xdp_flags;
|
||||
cfg.bind_flags = opt_xdp_bind_flags;
|
||||
cfg.xdp_flags = xdp_flags;
|
||||
cfg.bind_flags = xdp_bind_flags;
|
||||
|
||||
if (!opt_bidi) {
|
||||
if (test_type != TEST_TYPE_BIDI) {
|
||||
rxr = (ifobject->fv.vector == rx) ? &ifobject->xsk->rx : NULL;
|
||||
txr = (ifobject->fv.vector == tx) ? &ifobject->xsk->tx : NULL;
|
||||
} else {
|
||||
@@ -334,13 +345,8 @@ static int xsk_configure_socket(struct ifobject *ifobject)
|
||||
static struct option long_options[] = {
|
||||
{"interface", required_argument, 0, 'i'},
|
||||
{"queue", optional_argument, 0, 'q'},
|
||||
{"poll", no_argument, 0, 'p'},
|
||||
{"xdp-skb", no_argument, 0, 'S'},
|
||||
{"xdp-native", no_argument, 0, 'N'},
|
||||
{"copy", no_argument, 0, 'c'},
|
||||
{"tear-down", no_argument, 0, 'T'},
|
||||
{"bidi", optional_argument, 0, 'B'},
|
||||
{"debug", optional_argument, 0, 'D'},
|
||||
{"dump-pkts", optional_argument, 0, 'D'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"tx-pkt-count", optional_argument, 0, 'C'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
@@ -352,13 +358,8 @@ static void usage(const char *prog)
|
||||
" Options:\n"
|
||||
" -i, --interface Use interface\n"
|
||||
" -q, --queue=n Use queue n (default 0)\n"
|
||||
" -p, --poll Use poll syscall\n"
|
||||
" -S, --xdp-skb=n Use XDP SKB mode\n"
|
||||
" -N, --xdp-native=n Enforce XDP DRV (native) mode\n"
|
||||
" -c, --copy Force copy mode\n"
|
||||
" -T, --tear-down Tear down sockets by repeatedly recreating them\n"
|
||||
" -B, --bidi Bi-directional sockets test\n"
|
||||
" -D, --debug Debug mode - dump packets L2 - L5\n"
|
||||
" -D, --dump-pkts Dump packets L2 - L5\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -C, --tx-pkt-count=n Number of packets to send\n";
|
||||
ksft_print_msg(str, prog);
|
||||
}
|
||||
@@ -392,7 +393,7 @@ static void *nsswitchthread(void *args)
|
||||
ksft_test_result_fail("ERROR: [%s] interface \"%s\" does not exist\n",
|
||||
__func__, ifdict[targs->idx]->ifname);
|
||||
} else {
|
||||
ksft_print_msg("Interface found: %s\n", ifdict[targs->idx]->ifname);
|
||||
print_verbose("Interface found: %s\n", ifdict[targs->idx]->ifname);
|
||||
targs->retptr = true;
|
||||
}
|
||||
}
|
||||
@@ -422,7 +423,7 @@ static int validate_interfaces(void)
|
||||
pthread_join(ns_thread, NULL);
|
||||
|
||||
if (targs->retptr)
|
||||
ksft_print_msg("NS switched: %s\n", ifdict[i]->nsname);
|
||||
print_verbose("NS switched: %s\n", ifdict[i]->nsname);
|
||||
|
||||
free(targs);
|
||||
} else {
|
||||
@@ -432,7 +433,7 @@ static int validate_interfaces(void)
|
||||
("ERROR: interface \"%s\" does not exist\n", ifdict[i]->ifname);
|
||||
ret = false;
|
||||
} else {
|
||||
ksft_print_msg("Interface found: %s\n", ifdict[i]->ifname);
|
||||
print_verbose("Interface found: %s\n", ifdict[i]->ifname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,7 +447,7 @@ static void parse_command_line(int argc, char **argv)
|
||||
opterr = 0;
|
||||
|
||||
for (;;) {
|
||||
c = getopt_long(argc, argv, "i:q:pSNcTBDC:", long_options, &option_index);
|
||||
c = getopt_long(argc, argv, "i:q:DC:v", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -469,40 +470,26 @@ static void parse_command_line(int argc, char **argv)
|
||||
case 'q':
|
||||
opt_queue = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
opt_poll = 1;
|
||||
break;
|
||||
case 'S':
|
||||
opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
|
||||
opt_xdp_bind_flags |= XDP_COPY;
|
||||
uut = ORDER_CONTENT_VALIDATE_XDP_SKB;
|
||||
break;
|
||||
case 'N':
|
||||
opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
|
||||
opt_xdp_bind_flags |= XDP_COPY;
|
||||
uut = ORDER_CONTENT_VALIDATE_XDP_DRV;
|
||||
break;
|
||||
case 'c':
|
||||
opt_xdp_bind_flags |= XDP_COPY;
|
||||
break;
|
||||
case 'T':
|
||||
opt_teardown = 1;
|
||||
break;
|
||||
case 'B':
|
||||
opt_bidi = 1;
|
||||
break;
|
||||
case 'D':
|
||||
debug_pkt_dump = 1;
|
||||
break;
|
||||
case 'C':
|
||||
opt_pkt_count = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
opt_verbose = 1;
|
||||
break;
|
||||
default:
|
||||
usage(basename(argv[0]));
|
||||
ksft_exit_xfail();
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt_pkt_count) {
|
||||
print_verbose("No tx-pkt-count specified, using default %u\n", DEFAULT_PKT_CNT);
|
||||
opt_pkt_count = DEFAULT_PKT_CNT;
|
||||
}
|
||||
|
||||
if (!validate_interfaces()) {
|
||||
usage(basename(argv[0]));
|
||||
ksft_exit_xfail();
|
||||
@@ -599,6 +586,8 @@ static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
|
||||
{
|
||||
u32 idx;
|
||||
unsigned int i;
|
||||
bool tx_invalid_test = stat_test_type == STAT_TEST_TX_INVALID;
|
||||
u32 len = tx_invalid_test ? XSK_UMEM__DEFAULT_FRAME_SIZE + 1 : PKT_SIZE;
|
||||
|
||||
while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) < batch_size)
|
||||
complete_tx_only(xsk, batch_size);
|
||||
@@ -607,11 +596,16 @@ static void tx_only(struct xsk_socket_info *xsk, u32 *frameptr, int batch_size)
|
||||
struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);
|
||||
|
||||
tx_desc->addr = (*frameptr + i) << XSK_UMEM__DEFAULT_FRAME_SHIFT;
|
||||
tx_desc->len = PKT_SIZE;
|
||||
tx_desc->len = len;
|
||||
}
|
||||
|
||||
xsk_ring_prod__submit(&xsk->tx, batch_size);
|
||||
xsk->outstanding_tx += batch_size;
|
||||
if (!tx_invalid_test) {
|
||||
xsk->outstanding_tx += batch_size;
|
||||
} else {
|
||||
if (!NEED_WAKEUP || xsk_ring_prod__needs_wakeup(&xsk->tx))
|
||||
kick_tx(xsk);
|
||||
}
|
||||
*frameptr += batch_size;
|
||||
*frameptr %= num_frames;
|
||||
complete_tx_only(xsk, batch_size);
|
||||
@@ -654,7 +648,7 @@ static void tx_only_all(struct ifobject *ifobject)
|
||||
while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) {
|
||||
int batch_size = get_batch_size(pkt_cnt);
|
||||
|
||||
if (opt_poll) {
|
||||
if (test_type == TEST_TYPE_POLL) {
|
||||
ret = poll(fds, 1, POLL_TMOUT);
|
||||
if (ret <= 0)
|
||||
continue;
|
||||
@@ -714,7 +708,7 @@ static void worker_pkt_dump(void)
|
||||
int payload = *((uint32_t *)(pkt_buf[iter]->payload + PKT_HDR_SIZE));
|
||||
|
||||
if (payload == EOT) {
|
||||
ksft_print_msg("End-of-transmission frame received\n");
|
||||
print_verbose("End-of-transmission frame received\n");
|
||||
fprintf(stdout, "---------------------------------------\n");
|
||||
break;
|
||||
}
|
||||
@@ -723,6 +717,48 @@ static void worker_pkt_dump(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void worker_stats_validate(struct ifobject *ifobject)
|
||||
{
|
||||
struct xdp_statistics stats;
|
||||
socklen_t optlen;
|
||||
int err;
|
||||
struct xsk_socket *xsk = stat_test_type == STAT_TEST_TX_INVALID ?
|
||||
ifdict[!ifobject->ifdict_index]->xsk->xsk :
|
||||
ifobject->xsk->xsk;
|
||||
int fd = xsk_socket__fd(xsk);
|
||||
unsigned long xsk_stat = 0, expected_stat = opt_pkt_count;
|
||||
|
||||
sigvar = 0;
|
||||
|
||||
optlen = sizeof(stats);
|
||||
err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
if (optlen == sizeof(struct xdp_statistics)) {
|
||||
switch (stat_test_type) {
|
||||
case STAT_TEST_RX_DROPPED:
|
||||
xsk_stat = stats.rx_dropped;
|
||||
break;
|
||||
case STAT_TEST_TX_INVALID:
|
||||
xsk_stat = stats.tx_invalid_descs;
|
||||
break;
|
||||
case STAT_TEST_RX_FULL:
|
||||
xsk_stat = stats.rx_ring_full;
|
||||
expected_stat -= RX_FULL_RXQSIZE;
|
||||
break;
|
||||
case STAT_TEST_RX_FILL_EMPTY:
|
||||
xsk_stat = stats.rx_fill_ring_empty_descs;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (xsk_stat == expected_stat)
|
||||
sigvar = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void worker_pkt_validate(void)
|
||||
{
|
||||
u32 payloadseqnum = -2;
|
||||
@@ -746,7 +782,7 @@ static void worker_pkt_validate(void)
|
||||
}
|
||||
|
||||
if (payloadseqnum == EOT) {
|
||||
ksft_print_msg("End-of-transmission frame received: PASS\n");
|
||||
print_verbose("End-of-transmission frame received: PASS\n");
|
||||
sigvar = 1;
|
||||
break;
|
||||
}
|
||||
@@ -836,7 +872,7 @@ static void *worker_testapp_validate(void *arg)
|
||||
usleep(USLEEP_MAX);
|
||||
}
|
||||
|
||||
ksft_print_msg("Interface [%s] vector [Tx]\n", ifobject->ifname);
|
||||
print_verbose("Interface [%s] vector [Tx]\n", ifobject->ifname);
|
||||
for (int i = 0; i < num_frames; i++) {
|
||||
/*send EOT frame */
|
||||
if (i == (num_frames - 1))
|
||||
@@ -850,7 +886,7 @@ static void *worker_testapp_validate(void *arg)
|
||||
gen_eth_frame(ifobject->umem, i * XSK_UMEM__DEFAULT_FRAME_SIZE);
|
||||
}
|
||||
|
||||
ksft_print_msg("Sending %d packets on interface %s\n",
|
||||
print_verbose("Sending %d packets on interface %s\n",
|
||||
(opt_pkt_count - 1), ifobject->ifname);
|
||||
tx_only_all(ifobject);
|
||||
} else if (ifobject->fv.vector == rx) {
|
||||
@@ -860,8 +896,9 @@ static void *worker_testapp_validate(void *arg)
|
||||
if (!bidi_pass)
|
||||
thread_common_ops(ifobject, bufs, &sync_mutex_tx, &spinning_rx);
|
||||
|
||||
ksft_print_msg("Interface [%s] vector [Rx]\n", ifobject->ifname);
|
||||
xsk_populate_fill_ring(ifobject->umem);
|
||||
print_verbose("Interface [%s] vector [Rx]\n", ifobject->ifname);
|
||||
if (stat_test_type != STAT_TEST_RX_FILL_EMPTY)
|
||||
xsk_populate_fill_ring(ifobject->umem);
|
||||
|
||||
TAILQ_INIT(&head);
|
||||
if (debug_pkt_dump) {
|
||||
@@ -878,26 +915,32 @@ static void *worker_testapp_validate(void *arg)
|
||||
pthread_mutex_unlock(&sync_mutex);
|
||||
|
||||
while (1) {
|
||||
if (opt_poll) {
|
||||
if (test_type == TEST_TYPE_POLL) {
|
||||
ret = poll(fds, 1, POLL_TMOUT);
|
||||
if (ret <= 0)
|
||||
continue;
|
||||
}
|
||||
rx_pkt(ifobject->xsk, fds);
|
||||
worker_pkt_validate();
|
||||
|
||||
if (test_type != TEST_TYPE_STATS) {
|
||||
rx_pkt(ifobject->xsk, fds);
|
||||
worker_pkt_validate();
|
||||
} else {
|
||||
worker_stats_validate(ifobject);
|
||||
}
|
||||
|
||||
if (sigvar)
|
||||
break;
|
||||
}
|
||||
|
||||
ksft_print_msg("Received %d packets on interface %s\n",
|
||||
pkt_counter, ifobject->ifname);
|
||||
if (test_type != TEST_TYPE_STATS)
|
||||
print_verbose("Received %d packets on interface %s\n",
|
||||
pkt_counter, ifobject->ifname);
|
||||
|
||||
if (opt_teardown)
|
||||
ksft_print_msg("Destroying socket\n");
|
||||
if (test_type == TEST_TYPE_TEARDOWN)
|
||||
print_verbose("Destroying socket\n");
|
||||
}
|
||||
|
||||
if (!opt_bidi || bidi_pass) {
|
||||
if ((test_type != TEST_TYPE_BIDI) || bidi_pass) {
|
||||
xsk_socket__delete(ifobject->xsk->xsk);
|
||||
(void)xsk_umem__delete(ifobject->umem->umem);
|
||||
}
|
||||
@@ -907,14 +950,15 @@ static void *worker_testapp_validate(void *arg)
|
||||
static void testapp_validate(void)
|
||||
{
|
||||
struct timespec max_wait = { 0, 0 };
|
||||
bool bidi = test_type == TEST_TYPE_BIDI;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, THREAD_STACK);
|
||||
|
||||
if (opt_bidi && bidi_pass) {
|
||||
if ((test_type == TEST_TYPE_BIDI) && bidi_pass) {
|
||||
pthread_init_mutex();
|
||||
if (!switching_notify) {
|
||||
ksft_print_msg("Switching Tx/Rx vectors\n");
|
||||
print_verbose("Switching Tx/Rx vectors\n");
|
||||
switching_notify++;
|
||||
}
|
||||
}
|
||||
@@ -922,10 +966,10 @@ static void testapp_validate(void)
|
||||
pthread_mutex_lock(&sync_mutex);
|
||||
|
||||
/*Spawn RX thread */
|
||||
if (!opt_bidi || !bidi_pass) {
|
||||
if (!bidi || !bidi_pass) {
|
||||
if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[1]))
|
||||
exit_with_error(errno);
|
||||
} else if (opt_bidi && bidi_pass) {
|
||||
} else if (bidi && bidi_pass) {
|
||||
/*switch Tx/Rx vectors */
|
||||
ifdict[0]->fv.vector = rx;
|
||||
if (pthread_create(&t0, &attr, worker_testapp_validate, ifdict[0]))
|
||||
@@ -942,10 +986,10 @@ static void testapp_validate(void)
|
||||
pthread_mutex_unlock(&sync_mutex);
|
||||
|
||||
/*Spawn TX thread */
|
||||
if (!opt_bidi || !bidi_pass) {
|
||||
if (!bidi || !bidi_pass) {
|
||||
if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[0]))
|
||||
exit_with_error(errno);
|
||||
} else if (opt_bidi && bidi_pass) {
|
||||
} else if (bidi && bidi_pass) {
|
||||
/*switch Tx/Rx vectors */
|
||||
ifdict[1]->fv.vector = tx;
|
||||
if (pthread_create(&t1, &attr, worker_testapp_validate, ifdict[1]))
|
||||
@@ -964,19 +1008,46 @@ static void testapp_validate(void)
|
||||
free(pkt_buf);
|
||||
}
|
||||
|
||||
if (!opt_teardown && !opt_bidi)
|
||||
if (!(test_type == TEST_TYPE_TEARDOWN) && !bidi && !(test_type == TEST_TYPE_STATS))
|
||||
print_ksft_result();
|
||||
}
|
||||
|
||||
static void testapp_sockets(void)
|
||||
{
|
||||
for (int i = 0; i < (opt_teardown ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER); i++) {
|
||||
for (int i = 0; i < ((test_type == TEST_TYPE_TEARDOWN) ? MAX_TEARDOWN_ITER : MAX_BIDI_ITER);
|
||||
i++) {
|
||||
pkt_counter = 0;
|
||||
prev_pkt = -1;
|
||||
sigvar = 0;
|
||||
ksft_print_msg("Creating socket\n");
|
||||
print_verbose("Creating socket\n");
|
||||
testapp_validate();
|
||||
test_type == TEST_TYPE_BIDI ? bidi_pass++ : bidi_pass;
|
||||
}
|
||||
|
||||
print_ksft_result();
|
||||
}
|
||||
|
||||
static void testapp_stats(void)
|
||||
{
|
||||
for (int i = 0; i < STAT_TEST_TYPE_MAX; i++) {
|
||||
stat_test_type = i;
|
||||
|
||||
/* reset defaults */
|
||||
rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
|
||||
frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
|
||||
|
||||
switch (stat_test_type) {
|
||||
case STAT_TEST_RX_DROPPED:
|
||||
frame_headroom = XSK_UMEM__DEFAULT_FRAME_SIZE -
|
||||
XDP_PACKET_HEADROOM - 1;
|
||||
break;
|
||||
case STAT_TEST_RX_FULL:
|
||||
rxqsize = RX_FULL_RXQSIZE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
testapp_validate();
|
||||
opt_bidi ? bidi_pass++ : bidi_pass;
|
||||
}
|
||||
|
||||
print_ksft_result();
|
||||
@@ -1003,6 +1074,104 @@ static void init_iface_config(struct ifaceconfigobj *ifaceconfig)
|
||||
ifdict[1]->src_port = ifaceconfig->dst_port;
|
||||
}
|
||||
|
||||
static void *nsdisablemodethread(void *args)
|
||||
{
|
||||
struct targs *targs = args;
|
||||
|
||||
targs->retptr = false;
|
||||
|
||||
if (switch_namespace(targs->idx)) {
|
||||
targs->retptr = bpf_set_link_xdp_fd(ifdict[targs->idx]->ifindex, -1, targs->flags);
|
||||
} else {
|
||||
targs->retptr = errno;
|
||||
print_verbose("Failed to switch namespace to %s\n", ifdict[targs->idx]->nsname);
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void disable_xdp_mode(int mode)
|
||||
{
|
||||
int err = 0;
|
||||
__u32 flags = XDP_FLAGS_UPDATE_IF_NOEXIST | mode;
|
||||
char *mode_str = mode & XDP_FLAGS_SKB_MODE ? "skb" : "drv";
|
||||
|
||||
for (int i = 0; i < MAX_INTERFACES; i++) {
|
||||
if (strcmp(ifdict[i]->nsname, "")) {
|
||||
struct targs *targs;
|
||||
|
||||
targs = malloc(sizeof(*targs));
|
||||
memset(targs, 0, sizeof(*targs));
|
||||
if (!targs)
|
||||
exit_with_error(errno);
|
||||
|
||||
targs->idx = i;
|
||||
targs->flags = flags;
|
||||
if (pthread_create(&ns_thread, NULL, nsdisablemodethread, targs))
|
||||
exit_with_error(errno);
|
||||
|
||||
pthread_join(ns_thread, NULL);
|
||||
err = targs->retptr;
|
||||
free(targs);
|
||||
} else {
|
||||
err = bpf_set_link_xdp_fd(ifdict[i]->ifindex, -1, flags);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
print_verbose("Failed to disable %s mode on interface %s\n",
|
||||
mode_str, ifdict[i]->ifname);
|
||||
exit_with_error(err);
|
||||
}
|
||||
|
||||
print_verbose("Disabled %s mode for interface: %s\n", mode_str, ifdict[i]->ifname);
|
||||
configured_mode = mode & XDP_FLAGS_SKB_MODE ? TEST_MODE_DRV : TEST_MODE_SKB;
|
||||
}
|
||||
}
|
||||
|
||||
static void run_pkt_test(int mode, int type)
|
||||
{
|
||||
test_type = type;
|
||||
|
||||
/* reset defaults after potential previous test */
|
||||
xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||
pkt_counter = 0;
|
||||
switching_notify = 0;
|
||||
bidi_pass = 0;
|
||||
prev_pkt = -1;
|
||||
ifdict[0]->fv.vector = tx;
|
||||
ifdict[1]->fv.vector = rx;
|
||||
sigvar = 0;
|
||||
stat_test_type = -1;
|
||||
rxqsize = XSK_RING_CONS__DEFAULT_NUM_DESCS;
|
||||
frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
|
||||
|
||||
switch (mode) {
|
||||
case (TEST_MODE_SKB):
|
||||
if (configured_mode == TEST_MODE_DRV)
|
||||
disable_xdp_mode(XDP_FLAGS_DRV_MODE);
|
||||
xdp_flags |= XDP_FLAGS_SKB_MODE;
|
||||
break;
|
||||
case (TEST_MODE_DRV):
|
||||
if (configured_mode == TEST_MODE_SKB)
|
||||
disable_xdp_mode(XDP_FLAGS_SKB_MODE);
|
||||
xdp_flags |= XDP_FLAGS_DRV_MODE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_init_mutex();
|
||||
|
||||
if (test_type == TEST_TYPE_STATS)
|
||||
testapp_stats();
|
||||
else if ((test_type != TEST_TYPE_TEARDOWN) && (test_type != TEST_TYPE_BIDI))
|
||||
testapp_validate();
|
||||
else
|
||||
testapp_sockets();
|
||||
|
||||
pthread_destroy_mutex();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
@@ -1016,6 +1185,7 @@ int main(int argc, char **argv)
|
||||
const char *IP2 = "192.168.100.161";
|
||||
u16 UDP_DST_PORT = 2020;
|
||||
u16 UDP_SRC_PORT = 2121;
|
||||
int i, j;
|
||||
|
||||
ifaceconfig = malloc(sizeof(struct ifaceconfigobj));
|
||||
memcpy(ifaceconfig->dst_mac, MAC1, ETH_ALEN);
|
||||
@@ -1041,24 +1211,18 @@ int main(int argc, char **argv)
|
||||
|
||||
init_iface_config(ifaceconfig);
|
||||
|
||||
pthread_init_mutex();
|
||||
disable_xdp_mode(XDP_FLAGS_DRV_MODE);
|
||||
|
||||
ksft_set_plan(1);
|
||||
ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
|
||||
|
||||
if (!opt_teardown && !opt_bidi) {
|
||||
testapp_validate();
|
||||
} else if (opt_teardown && opt_bidi) {
|
||||
ksft_test_result_fail("ERROR: parameters -T and -B cannot be used together\n");
|
||||
ksft_exit_xfail();
|
||||
} else {
|
||||
testapp_sockets();
|
||||
for (i = 0; i < TEST_MODE_MAX; i++) {
|
||||
for (j = 0; j < TEST_TYPE_MAX; j++)
|
||||
run_pkt_test(i, j);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_INTERFACES; i++)
|
||||
free(ifdict[i]);
|
||||
|
||||
pthread_destroy_mutex();
|
||||
|
||||
ksft_exit_pass();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -41,33 +41,59 @@
|
||||
#define BATCH_SIZE 64
|
||||
#define POLL_TMOUT 1000
|
||||
#define NEED_WAKEUP true
|
||||
#define DEFAULT_PKT_CNT 10000
|
||||
#define RX_FULL_RXQSIZE 32
|
||||
|
||||
#define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0)
|
||||
|
||||
typedef __u32 u32;
|
||||
typedef __u16 u16;
|
||||
typedef __u8 u8;
|
||||
|
||||
enum TESTS {
|
||||
ORDER_CONTENT_VALIDATE_XDP_SKB = 0,
|
||||
ORDER_CONTENT_VALIDATE_XDP_DRV = 1,
|
||||
enum TEST_MODES {
|
||||
TEST_MODE_UNCONFIGURED = -1,
|
||||
TEST_MODE_SKB,
|
||||
TEST_MODE_DRV,
|
||||
TEST_MODE_MAX
|
||||
};
|
||||
|
||||
u8 uut;
|
||||
u8 debug_pkt_dump;
|
||||
u32 num_frames;
|
||||
u8 switching_notify;
|
||||
u8 bidi_pass;
|
||||
enum TEST_TYPES {
|
||||
TEST_TYPE_NOPOLL,
|
||||
TEST_TYPE_POLL,
|
||||
TEST_TYPE_TEARDOWN,
|
||||
TEST_TYPE_BIDI,
|
||||
TEST_TYPE_STATS,
|
||||
TEST_TYPE_MAX
|
||||
};
|
||||
|
||||
enum STAT_TEST_TYPES {
|
||||
STAT_TEST_RX_DROPPED,
|
||||
STAT_TEST_TX_INVALID,
|
||||
STAT_TEST_RX_FULL,
|
||||
STAT_TEST_RX_FILL_EMPTY,
|
||||
STAT_TEST_TYPE_MAX
|
||||
};
|
||||
|
||||
static int configured_mode = TEST_MODE_UNCONFIGURED;
|
||||
static u8 debug_pkt_dump;
|
||||
static u32 num_frames;
|
||||
static u8 switching_notify;
|
||||
static u8 bidi_pass;
|
||||
static int test_type;
|
||||
|
||||
static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||
static int opt_queue;
|
||||
static int opt_pkt_count;
|
||||
static int opt_poll;
|
||||
static int opt_teardown;
|
||||
static int opt_bidi;
|
||||
static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP;
|
||||
static u8 opt_verbose;
|
||||
|
||||
static u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||
static u32 xdp_bind_flags = XDP_USE_NEED_WAKEUP | XDP_COPY;
|
||||
static u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE];
|
||||
static u32 pkt_counter;
|
||||
static u32 prev_pkt = -1;
|
||||
static long prev_pkt = -1;
|
||||
static int sigvar;
|
||||
static int stat_test_type;
|
||||
static u32 rxqsize;
|
||||
static u32 frame_headroom;
|
||||
|
||||
struct xsk_umem_info {
|
||||
struct xsk_ring_prod fq;
|
||||
@@ -137,8 +163,9 @@ pthread_t t0, t1, ns_thread;
|
||||
pthread_attr_t attr;
|
||||
|
||||
struct targs {
|
||||
bool retptr;
|
||||
u8 retptr;
|
||||
int idx;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(head_s, pkt) head = TAILQ_HEAD_INITIALIZER(head);
|
||||
|
||||
@@ -82,24 +82,21 @@ clear_configs()
|
||||
{
|
||||
if [ $(ip netns show | grep $3 &>/dev/null; echo $?;) == 0 ]; then
|
||||
[ $(ip netns exec $3 ip link show $2 &>/dev/null; echo $?;) == 0 ] &&
|
||||
{ echo "removing link $1:$2"; ip netns exec $3 ip link del $2; }
|
||||
echo "removing ns $3"
|
||||
{ ip netns exec $3 ip link del $2; }
|
||||
ip netns del $3
|
||||
fi
|
||||
#Once we delete a veth pair node, the entire veth pair is removed,
|
||||
#this is just to be cautious just incase the NS does not exist then
|
||||
#veth node inside NS won't get removed so we explicitly remove it
|
||||
[ $(ip link show $1 &>/dev/null; echo $?;) == 0 ] &&
|
||||
{ echo "removing link $1"; ip link del $1; }
|
||||
{ ip link del $1; }
|
||||
if [ -f ${SPECFILE} ]; then
|
||||
echo "removing spec file:" ${SPECFILE}
|
||||
rm -f ${SPECFILE}
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_exit()
|
||||
{
|
||||
echo "cleaning up..."
|
||||
clear_configs $1 $2 $3
|
||||
}
|
||||
|
||||
@@ -108,28 +105,7 @@ validate_ip_utility()
|
||||
[ ! $(type -P ip) ] && { echo "'ip' not found. Skipping tests."; test_exit $ksft_skip 1; }
|
||||
}
|
||||
|
||||
vethXDPgeneric()
|
||||
{
|
||||
ip link set dev $1 xdpdrv off
|
||||
ip netns exec $3 ip link set dev $2 xdpdrv off
|
||||
}
|
||||
|
||||
vethXDPnative()
|
||||
{
|
||||
ip link set dev $1 xdpgeneric off
|
||||
ip netns exec $3 ip link set dev $2 xdpgeneric off
|
||||
}
|
||||
|
||||
execxdpxceiver()
|
||||
{
|
||||
local -a 'paramkeys=("${!'"$1"'[@]}")' copy
|
||||
paramkeysstr=${paramkeys[*]}
|
||||
|
||||
for index in $paramkeysstr;
|
||||
do
|
||||
current=$1"[$index]"
|
||||
copy[$index]=${!current}
|
||||
done
|
||||
|
||||
./${XSKOBJ} -i ${VETH0} -i ${VETH1},${NS1} ${copy[*]} -C ${NUMPKTS}
|
||||
./${XSKOBJ} -i ${VETH0} -i ${VETH1},${NS1} -C ${NUMPKTS} ${VERBOSE_ARG} ${DUMP_PKTS_ARG}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user