diff options
Diffstat (limited to 'tools')
91 files changed, 5542 insertions, 802 deletions
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 0dfe4d3f74e2..f41079da38c5 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -213,6 +213,7 @@ #define X86_FEATURE_SEV ( 7*32+20) /* AMD Secure Encrypted Virtualization */ #define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */ +#define X86_FEATURE_USE_IBRS_FW ( 7*32+22) /* "" Use IBRS during runtime firmware calls */ /* Virtualization flags: Linux defined, word 8 */ #define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile index c8ec0ae16bf0..1ea545965ee3 100644 --- a/tools/bpf/Makefile +++ b/tools/bpf/Makefile @@ -1,19 +1,28 @@ # SPDX-License-Identifier: GPL-2.0 -prefix = /usr +include ../scripts/Makefile.include + +prefix ?= /usr/local CC = gcc LEX = flex YACC = bison MAKE = make +INSTALL ?= install CFLAGS += -Wall -O2 -CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include +CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/include/uapi -I$(srctree)/include ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(CURDIR))) srctree := $(patsubst %/,%,$(dir $(srctree))) endif +ifeq ($(V),1) + Q = +else + Q = @ +endif + FEATURE_USER = .bpf FEATURE_TESTS = libbfd disassembler-four-args FEATURE_DISPLAY = libbfd disassembler-four-args @@ -38,40 +47,59 @@ ifeq ($(feature-disassembler-four-args), 1) CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE endif -%.yacc.c: %.y - $(YACC) -o $@ -d $< +$(OUTPUT)%.yacc.c: $(srctree)/tools/bpf/%.y + $(QUIET_BISON)$(YACC) -o $@ -d $< -%.lex.c: %.l - $(LEX) -o $@ $< +$(OUTPUT)%.lex.c: $(srctree)/tools/bpf/%.l + $(QUIET_FLEX)$(LEX) -o $@ $< -all: bpf_jit_disasm bpf_dbg bpf_asm bpftool +$(OUTPUT)%.o: $(srctree)/tools/bpf/%.c + $(QUIET_CC)$(COMPILE.c) -o $@ $< -bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm' -bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl -bpf_jit_disasm : bpf_jit_disasm.o +$(OUTPUT)%.yacc.o: $(OUTPUT)%.yacc.c + $(QUIET_CC)$(COMPILE.c) -o $@ $< +$(OUTPUT)%.lex.o: $(OUTPUT)%.lex.c + $(QUIET_CC)$(COMPILE.c) -o $@ $< -bpf_dbg : LDLIBS = -lreadline -bpf_dbg : bpf_dbg.o +PROGS = $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg $(OUTPUT)bpf_asm -bpf_asm : LDLIBS = -bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o -bpf_exp.lex.o : bpf_exp.yacc.c +all: $(PROGS) bpftool -clean: bpftool_clean - rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.* +$(OUTPUT)bpf_jit_disasm: CFLAGS += -DPACKAGE='bpf_jit_disasm' +$(OUTPUT)bpf_jit_disasm: $(OUTPUT)bpf_jit_disasm.o + $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ -lopcodes -lbfd -ldl -install: bpftool_install - install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm - install bpf_dbg $(prefix)/bin/bpf_dbg - install bpf_asm $(prefix)/bin/bpf_asm +$(OUTPUT)bpf_dbg: $(OUTPUT)bpf_dbg.o + $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ -lreadline + +$(OUTPUT)bpf_asm: $(OUTPUT)bpf_asm.o $(OUTPUT)bpf_exp.yacc.o $(OUTPUT)bpf_exp.lex.o + $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ + +$(OUTPUT)bpf_exp.lex.c: $(OUTPUT)bpf_exp.yacc.c + +clean: bpftool_clean + $(call QUIET_CLEAN, bpf-progs) + $(Q)rm -rf $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \ + $(OUTPUT)bpf_asm $(OUTPUT)bpf_exp.yacc.* $(OUTPUT)bpf_exp.lex.* + $(call QUIET_CLEAN, core-gen) + $(Q)rm -f $(OUTPUT)FEATURE-DUMP.bpf + +install: $(PROGS) bpftool_install + $(call QUIET_INSTALL, bpf_jit_disasm) + $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/bin + $(Q)$(INSTALL) $(OUTPUT)bpf_jit_disasm $(DESTDIR)$(prefix)/bin/bpf_jit_disasm + $(call QUIET_INSTALL, bpf_dbg) + $(Q)$(INSTALL) $(OUTPUT)bpf_dbg $(DESTDIR)$(prefix)/bin/bpf_dbg + $(call QUIET_INSTALL, bpf_asm) + $(Q)$(INSTALL) $(OUTPUT)bpf_asm $(DESTDIR)$(prefix)/bin/bpf_asm bpftool: - $(MAKE) -C bpftool + $(call descend,bpftool) bpftool_install: - $(MAKE) -C bpftool install + $(call descend,bpftool,install) bpftool_clean: - $(MAKE) -C bpftool clean + $(call descend,bpftool,clean) -.PHONY: bpftool FORCE +.PHONY: all install clean bpftool bpftool_install bpftool_clean diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index e4ceee7f2dff..67ca6c69376c 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -21,7 +21,7 @@ MAP COMMANDS ============= | **bpftool** **prog { show | list }** [*PROG*] -| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}] +| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog pin** *PROG* *FILE* | **bpftool** **prog load** *OBJ* *FILE* @@ -39,12 +39,18 @@ DESCRIPTION Output will start with program ID followed by program type and zero or more named attributes (depending on kernel version). - **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** }] - Dump eBPF instructions of the program from the kernel. - If *FILE* is specified image will be written to a file, - otherwise it will be disassembled and printed to stdout. + **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** }] + Dump eBPF instructions of the program from the kernel. By + default, eBPF will be disassembled and printed to standard + output in human-readable format. In this case, **opcodes** + controls if raw opcodes should be printed as well. - **opcodes** controls if raw opcodes will be printed. + If **file** is specified, the binary image will instead be + written to *FILE*. + + If **visual** is specified, control flow graph (CFG) will be + built instead, and eBPF instructions will be presented with + CFG in DOT format, on standard output. **bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** }] Dump jited image (host machine code) of the program. diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 26901ec87361..4e69782c4a79 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -38,7 +38,7 @@ bash_compdir ?= /usr/share/bash-completion/completions CC = gcc CFLAGS += -O2 -CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow +CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow -Wno-missing-field-initializers CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/ CFLAGS += -DBPFTOOL_VERSION='"$(BPFTOOL_VERSION)"' LIBS = -lelf -lbfd -lopcodes $(LIBBPF) @@ -70,7 +70,7 @@ ifeq ($(feature-disassembler-four-args), 1) CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE endif -include $(wildcard *.d) +include $(wildcard $(OUTPUT)*.d) all: $(OUTPUT)bpftool @@ -89,6 +89,8 @@ $(OUTPUT)%.o: %.c clean: $(LIBBPF)-clean $(call QUIET_CLEAN, bpftool) $(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d + $(call QUIET_CLEAN, core-gen) + $(Q)$(RM) $(OUTPUT)FEATURE-DUMP.bpftool install: $(OUTPUT)bpftool $(call QUIET_INSTALL, bpftool) diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 08719c54a614..490811b45fa7 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool @@ -147,7 +147,7 @@ _bpftool() # Deal with simplest keywords case $prev in - help|key|opcodes) + help|key|opcodes|visual) return 0 ;; tag) @@ -223,11 +223,16 @@ _bpftool() return 0 ;; *) - _bpftool_once_attr 'file' + _bpftool_once_attr 'file' + if _bpftool_search_list 'xlated'; then + COMPREPLY+=( $( compgen -W 'opcodes visual' -- \ + "$cur" ) ) + else COMPREPLY+=( $( compgen -W 'opcodes' -- \ "$cur" ) ) - return 0 - ;; + fi + return 0 + ;; esac ;; pin) diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c new file mode 100644 index 000000000000..f30b3a4a840b --- /dev/null +++ b/tools/bpf/bpftool/cfg.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Copyright (C) 2018 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/list.h> +#include <stdlib.h> +#include <string.h> + +#include "cfg.h" +#include "main.h" +#include "xlated_dumper.h" + +struct cfg { + struct list_head funcs; + int func_num; +}; + +struct func_node { + struct list_head l; + struct list_head bbs; + struct bpf_insn *start; + struct bpf_insn *end; + int idx; + int bb_num; +}; + +struct bb_node { + struct list_head l; + struct list_head e_prevs; + struct list_head e_succs; + struct bpf_insn *head; + struct bpf_insn *tail; + int idx; +}; + +#define EDGE_FLAG_EMPTY 0x0 +#define EDGE_FLAG_FALLTHROUGH 0x1 +#define EDGE_FLAG_JUMP 0x2 +struct edge_node { + struct list_head l; + struct bb_node *src; + struct bb_node *dst; + int flags; +}; + +#define ENTRY_BLOCK_INDEX 0 +#define EXIT_BLOCK_INDEX 1 +#define NUM_FIXED_BLOCKS 2 +#define func_prev(func) list_prev_entry(func, l) +#define func_next(func) list_next_entry(func, l) +#define bb_prev(bb) list_prev_entry(bb, l) +#define bb_next(bb) list_next_entry(bb, l) +#define entry_bb(func) func_first_bb(func) +#define exit_bb(func) func_last_bb(func) +#define cfg_first_func(cfg) \ + list_first_entry(&cfg->funcs, struct func_node, l) +#define cfg_last_func(cfg) \ + list_last_entry(&cfg->funcs, struct func_node, l) +#define func_first_bb(func) \ + list_first_entry(&func->bbs, struct bb_node, l) +#define func_last_bb(func) \ + list_last_entry(&func->bbs, struct bb_node, l) + +static struct func_node *cfg_append_func(struct cfg *cfg, struct bpf_insn *insn) +{ + struct func_node *new_func, *func; + + list_for_each_entry(func, &cfg->funcs, l) { + if (func->start == insn) + return func; + else if (func->start > insn) + break; + } + + func = func_prev(func); + new_func = calloc(1, sizeof(*new_func)); + if (!new_func) { + p_err("OOM when allocating FUNC node"); + return NULL; + } + new_func->start = insn; + new_func->idx = cfg->func_num; + list_add(&new_func->l, &func->l); + cfg->func_num++; + + return new_func; +} + +static struct bb_node *func_append_bb(struct func_node *func, + struct bpf_insn *insn) +{ + struct bb_node *new_bb, *bb; + + list_for_each_entry(bb, &func->bbs, l) { + if (bb->head == insn) + return bb; + else if (bb->head > insn) + break; + } + + bb = bb_prev(bb); + new_bb = calloc(1, sizeof(*new_bb)); + if (!new_bb) { + p_err("OOM when allocating BB node"); + return NULL; + } + new_bb->head = insn; + INIT_LIST_HEAD(&new_bb->e_prevs); + INIT_LIST_HEAD(&new_bb->e_succs); + list_add(&new_bb->l, &bb->l); + + return new_bb; +} + +static struct bb_node *func_insert_dummy_bb(struct list_head *after) +{ + struct bb_node *bb; + + bb = calloc(1, sizeof(*bb)); + if (!bb) { + p_err("OOM when allocating BB node"); + return NULL; + } + + INIT_LIST_HEAD(&bb->e_prevs); + INIT_LIST_HEAD(&bb->e_succs); + list_add(&bb->l, after); + + return bb; +} + +static bool cfg_partition_funcs(struct cfg *cfg, struct bpf_insn *cur, + struct bpf_insn *end) +{ + struct func_node *func, *last_func; + + func = cfg_append_func(cfg, cur); + if (!func) + return true; + + for (; cur < end; cur++) { + if (cur->code != (BPF_JMP | BPF_CALL)) + continue; + if (cur->src_reg != BPF_PSEUDO_CALL) + continue; + func = cfg_append_func(cfg, cur + cur->off + 1); + if (!func) + return true; + } + + last_func = cfg_last_func(cfg); + last_func->end = end - 1; + func = cfg_first_func(cfg); + list_for_each_entry_from(func, &last_func->l, l) { + func->end = func_next(func)->start - 1; + } + + return false; +} + +static bool func_partition_bb_head(struct func_node *func) +{ + struct bpf_insn *cur, *end; + struct bb_node *bb; + + cur = func->start; + end = func->end; + INIT_LIST_HEAD(&func->bbs); + bb = func_append_bb(func, cur); + if (!bb) + return true; + + for (; cur <= end; cur++) { + if (BPF_CLASS(cur->code) == BPF_JMP) { + u8 opcode = BPF_OP(cur->code); + + if (opcode == BPF_EXIT || opcode == BPF_CALL) + continue; + + bb = func_append_bb(func, cur + cur->off + 1); + if (!bb) + return true; + + if (opcode != BPF_JA) { + bb = func_append_bb(func, cur + 1); + if (!bb) + return true; + } + } + } + + return false; +} + +static void func_partition_bb_tail(struct func_node *func) +{ + unsigned int bb_idx = NUM_FIXED_BLOCKS; + struct bb_node *bb, *last; + + last = func_last_bb(func); + last->tail = func->end; + bb = func_first_bb(func); + list_for_each_entry_from(bb, &last->l, l) { + bb->tail = bb_next(bb)->head - 1; + bb->idx = bb_idx++; + } + + last->idx = bb_idx++; + func->bb_num = bb_idx; +} + +static bool func_add_special_bb(struct func_node *func) +{ + struct bb_node *bb; + + bb = func_insert_dummy_bb(&func->bbs); + if (!bb) + return true; + bb->idx = ENTRY_BLOCK_INDEX; + + bb = func_insert_dummy_bb(&func_last_bb(func)->l); + if (!bb) + return true; + bb->idx = EXIT_BLOCK_INDEX; + + return false; +} + +static bool func_partition_bb(struct func_node *func) +{ + if (func_partition_bb_head(func)) + return true; + + func_partition_bb_tail(func); + + return false; +} + +static struct bb_node *func_search_bb_with_head(struct func_node *func, + struct bpf_insn *insn) +{ + struct bb_node *bb; + + list_for_each_entry(bb, &func->bbs, l) { + if (bb->head == insn) + return bb; + } + + return NULL; +} + +static struct edge_node *new_edge(struct bb_node *src, struct bb_node *dst, + int flags) +{ + struct edge_node *e; + + e = calloc(1, sizeof(*e)); + if (!e) { + p_err("OOM when allocating edge node"); + return NULL; + } + + if (src) + e->src = src; + if (dst) + e->dst = dst; + + e->flags |= flags; + + return e; +} + +static bool func_add_bb_edges(struct func_node *func) +{ + struct bpf_insn *insn; + struct edge_node *e; + struct bb_node *bb; + + bb = entry_bb(func); + e = new_edge(bb, bb_next(bb), EDGE_FLAG_FALLTHROUGH); + if (!e) + return true; + list_add_tail(&e->l, &bb->e_succs); + + bb = exit_bb(func); + e = new_edge(bb_prev(bb), bb, EDGE_FLAG_FALLTHROUGH); + if (!e) + return true; + list_add_tail(&e->l, &bb->e_prevs); + + bb = entry_bb(func); + bb = bb_next(bb); + list_for_each_entry_from(bb, &exit_bb(func)->l, l) { + e = new_edge(bb, NULL, EDGE_FLAG_EMPTY); + if (!e) + return true; + e->src = bb; + + insn = bb->tail; + if (BPF_CLASS(insn->code) != BPF_JMP || + BPF_OP(insn->code) == BPF_EXIT) { + e->dst = bb_next(bb); + e->flags |= EDGE_FLAG_FALLTHROUGH; + list_add_tail(&e->l, &bb->e_succs); + continue; + } else if (BPF_OP(insn->code) == BPF_JA) { + e->dst = func_search_bb_with_head(func, + insn + insn->off + 1); + e->flags |= EDGE_FLAG_JUMP; + list_add_tail(&e->l, &bb->e_succs); + continue; + } + + e->dst = bb_next(bb); + e->flags |= EDGE_FLAG_FALLTHROUGH; + list_add_tail(&e->l, &bb->e_succs); + + e = new_edge(bb, NULL, EDGE_FLAG_JUMP); + if (!e) + return true; + e->src = bb; + e->dst = func_search_bb_with_head(func, insn + insn->off + 1); + list_add_tail(&e->l, &bb->e_succs); + } + + return false; +} + +static bool cfg_build(struct cfg *cfg, struct bpf_insn *insn, unsigned int len) +{ + int cnt = len / sizeof(*insn); + struct func_node *func; + + INIT_LIST_HEAD(&cfg->funcs); + + if (cfg_partition_funcs(cfg, insn, insn + cnt)) + return true; + + list_for_each_entry(func, &cfg->funcs, l) { + if (func_partition_bb(func) || func_add_special_bb(func)) + return true; + + if (func_add_bb_edges(func)) + return true; + } + + return false; +} + +static void cfg_destroy(struct cfg *cfg) +{ + struct func_node *func, *func2; + + list_for_each_entry_safe(func, func2, &cfg->funcs, l) { + struct bb_node *bb, *bb2; + + list_for_each_entry_safe(bb, bb2, &func->bbs, l) { + struct edge_node *e, *e2; + + list_for_each_entry_safe(e, e2, &bb->e_prevs, l) { + list_del(&e->l); + free(e); + } + + list_for_each_entry_safe(e, e2, &bb->e_succs, l) { + list_del(&e->l); + free(e); + } + + list_del(&bb->l); + free(bb); + } + + list_del(&func->l); + free(func); + } +} + +static void draw_bb_node(struct func_node *func, struct bb_node *bb) +{ + const char *shape; + + if (bb->idx == ENTRY_BLOCK_INDEX || bb->idx == EXIT_BLOCK_INDEX) + shape = "Mdiamond"; + else + shape = "record"; + + printf("\tfn_%d_bb_%d [shape=%s,style=filled,label=\"", + func->idx, bb->idx, shape); + + if (bb->idx == ENTRY_BLOCK_INDEX) { + printf("ENTRY"); + } else if (bb->idx == EXIT_BLOCK_INDEX) { + printf("EXIT"); + } else { + unsigned int start_idx; + struct dump_data dd = {}; + + printf("{"); + kernel_syms_load(&dd); + start_idx = bb->head - func->start; + dump_xlated_for_graph(&dd, bb->head, bb->tail, start_idx); + kernel_syms_destroy(&dd); + printf("}"); + } + + printf("\"];\n\n"); +} + +static void draw_bb_succ_edges(struct func_node *func, struct bb_node *bb) +{ + const char *style = "\"solid,bold\""; + const char *color = "black"; + int func_idx = func->idx; + struct edge_node *e; + int weight = 10; + + if (list_empty(&bb->e_succs)) + return; + + list_for_each_entry(e, &bb->e_succs, l) { + printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=%s, color=%s, weight=%d, constraint=true", + func_idx, e->src->idx, func_idx, e->dst->idx, + style, color, weight); + printf("];\n"); + } +} + +static void func_output_bb_def(struct func_node *func) +{ + struct bb_node *bb; + + list_for_each_entry(bb, &func->bbs, l) { + draw_bb_node(func, bb); + } +} + +static void func_output_edges(struct func_node *func) +{ + int func_idx = func->idx; + struct bb_node *bb; + + list_for_each_entry(bb, &func->bbs, l) { + draw_bb_succ_edges(func, bb); + } + + /* Add an invisible edge from ENTRY to EXIT, this is to + * improve the graph layout. + */ + printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=\"invis\", constraint=true];\n", + func_idx, ENTRY_BLOCK_INDEX, func_idx, EXIT_BLOCK_INDEX); +} + +static void cfg_dump(struct cfg *cfg) +{ + struct func_node *func; + + printf("digraph \"DOT graph for eBPF program\" {\n"); + list_for_each_entry(func, &cfg->funcs, l) { + printf("subgraph \"cluster_%d\" {\n\tstyle=\"dashed\";\n\tcolor=\"black\";\n\tlabel=\"func_%d ()\";\n", + func->idx, func->idx); + func_output_bb_def(func); + func_output_edges(func); + printf("}\n"); + } + printf("}\n"); +} + +void dump_xlated_cfg(void *buf, unsigned int len) +{ + struct bpf_insn *insn = buf; + struct cfg cfg; + + memset(&cfg, 0, sizeof(cfg)); + if (cfg_build(&cfg, insn, len)) + return; + + cfg_dump(&cfg); + + cfg_destroy(&cfg); +} diff --git a/tools/bpf/bpftool/cfg.h b/tools/bpf/bpftool/cfg.h new file mode 100644 index 000000000000..2cc9bd990b13 --- /dev/null +++ b/tools/bpf/bpftool/cfg.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Copyright (C) 2018 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BPF_TOOL_CFG_H +#define __BPF_TOOL_CFG_H + +void dump_xlated_cfg(void *buf, unsigned int len); + +#endif /* __BPF_TOOL_CFG_H */ diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 0b482c0070e0..465995281dcd 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -55,6 +55,10 @@ #include "main.h" +#ifndef BPF_FS_MAGIC +#define BPF_FS_MAGIC 0xcafe4a11 +#endif + void p_err(const char *fmt, ...) { va_list ap; diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 185acfa229b5..1ec852d21d44 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -46,6 +46,9 @@ #include "main.h" +#define BATCH_LINE_LEN_MAX 65536 +#define BATCH_ARG_NB_MAX 4096 + const char *bin_name; static int last_argc; static char **last_argv; @@ -157,6 +160,54 @@ void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) } } +/* Split command line into argument vector. */ +static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb) +{ + static const char ws[] = " \t\r\n"; + char *cp = line; + int n_argc = 0; + + while (*cp) { + /* Skip leading whitespace. */ + cp += strspn(cp, ws); + + if (*cp == '\0') + break; + + if (n_argc >= (maxargs - 1)) { + p_err("too many arguments to command %d", cmd_nb); + return -1; + } + + /* Word begins with quote. */ + if (*cp == '\'' || *cp == '"') { + char quote = *cp++; + + n_argv[n_argc++] = cp; + /* Find ending quote. */ + cp = strchr(cp, quote); + if (!cp) { + p_err("unterminated quoted string in command %d", + cmd_nb); + return -1; + } + } else { + n_argv[n_argc++] = cp; + + /* Find end of word. */ + cp += strcspn(cp, ws); + if (*cp == '\0') + break; + } + + /* Separate words. */ + *cp++ = 0; + } + n_argv[n_argc] = NULL; + + return n_argc; +} + static int do_batch(int argc, char **argv); static const struct cmd cmds[] = { @@ -171,11 +222,12 @@ static const struct cmd cmds[] = { static int do_batch(int argc, char **argv) { + char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX]; + char *n_argv[BATCH_ARG_NB_MAX]; unsigned int lines = 0; - char *n_argv[4096]; - char buf[65536]; int n_argc; FILE *fp; + char *cp; int err; int i; @@ -191,7 +243,10 @@ static int do_batch(int argc, char **argv) } NEXT_ARG(); - fp = fopen(*argv, "r"); + if (!strcmp(*argv, "-")) + fp = stdin; + else + fp = fopen(*argv, "r"); if (!fp) { p_err("Can't open file (%s): %s", *argv, strerror(errno)); return -1; @@ -200,27 +255,45 @@ static int do_batch(int argc, char **argv) if (json_output) jsonw_start_array(json_wtr); while (fgets(buf, sizeof(buf), fp)) { + cp = strchr(buf, '#'); + if (cp) + *cp = '\0'; + if (strlen(buf) == sizeof(buf) - 1) { errno = E2BIG; break; } - n_argc = 0; - n_argv[n_argc] = strtok(buf, " \t\n"); - - while (n_argv[n_argc]) { - n_argc++; - if (n_argc == ARRAY_SIZE(n_argv)) { - p_err("line %d has too many arguments, skip", + /* Append continuation lines if any (coming after a line ending + * with '\' in the batch file). + */ + while ((cp = strstr(buf, "\\\n")) != NULL) { + if (!fgets(contline, sizeof(contline), fp) || + strlen(contline) == 0) { + p_err("missing continuation line on command %d", lines); - n_argc = 0; - break; + err = -1; + goto err_close; + } + + cp = strchr(contline, '#'); + if (cp) + *cp = '\0'; + + if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) { + p_err("command %d is too long", lines); + err = -1; + goto err_close; } - n_argv[n_argc] = strtok(NULL, " \t\n"); + buf[strlen(buf) - 2] = '\0'; + strcat(buf, contline); } + n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines); if (!n_argc) continue; + if (n_argc < 0) + goto err_close; if (json_output) { jsonw_start_object(json_wtr); @@ -247,11 +320,12 @@ static int do_batch(int argc, char **argv) p_err("reading batch file failed: %s", strerror(errno)); err = -1; } else { - p_info("processed %d lines", lines); + p_info("processed %d commands", lines); err = 0; } err_close: - fclose(fp); + if (fp != stdin) + fclose(fp); if (json_output) jsonw_end_array(json_wtr); diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index e549e329be82..f7a810897eac 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -47,8 +47,9 @@ #include <bpf.h> #include <libbpf.h> +#include "cfg.h" #include "main.h" -#include "disasm.h" +#include "xlated_dumper.h" static const char * const prog_type_name[] = { [BPF_PROG_TYPE_UNSPEC] = "unspec", @@ -407,259 +408,6 @@ static int do_show(int argc, char **argv) return err; } -#define SYM_MAX_NAME 256 - -struct kernel_sym { - unsigned long address; - char name[SYM_MAX_NAME]; -}; - -struct dump_data { - unsigned long address_call_base; - struct kernel_sym *sym_mapping; - __u32 sym_count; - char scratch_buff[SYM_MAX_NAME]; -}; - -static int kernel_syms_cmp(const void *sym_a, const void *sym_b) -{ - return ((struct kernel_sym *)sym_a)->address - - ((struct kernel_sym *)sym_b)->address; -} - -static void kernel_syms_load(struct dump_data *dd) -{ - struct kernel_sym *sym; - char buff[256]; - void *tmp, *address; - FILE *fp; - - fp = fopen("/proc/kallsyms", "r"); - if (!fp) - return; - - while (!feof(fp)) { - if (!fgets(buff, sizeof(buff), fp)) - break; - tmp = realloc(dd->sym_mapping, - (dd->sym_count + 1) * - sizeof(*dd->sym_mapping)); - if (!tmp) { -out: - free(dd->sym_mapping); - dd->sym_mapping = NULL; - fclose(fp); - return; - } - dd->sym_mapping = tmp; - sym = &dd->sym_mapping[dd->sym_count]; - if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) - continue; - sym->address = (unsigned long)address; - if (!strcmp(sym->name, "__bpf_call_base")) { - dd->address_call_base = sym->address; - /* sysctl kernel.kptr_restrict was set */ - if (!sym->address) - goto out; - } - if (sym->address) - dd->sym_count++; - } - - fclose(fp); - - qsort(dd->sym_mapping, dd->sym_count, - sizeof(*dd->sym_mapping), kernel_syms_cmp); -} - -static void kernel_syms_destroy(struct dump_data *dd) -{ - free(dd->sym_mapping); -} - -static struct kernel_sym *kernel_syms_search(struct dump_data *dd, - unsigned long key) -{ - struct kernel_sym sym = { - .address = key, - }; - - return dd->sym_mapping ? - bsearch(&sym, dd->sym_mapping, dd->sym_count, - sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; -} - -static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); -} - -static const char *print_call_pcrel(struct dump_data *dd, - struct kernel_sym *sym, - unsigned long address, - const struct bpf_insn *insn) -{ - if (sym) - snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), - "%+d#%s", insn->off, sym->name); - else - snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), - "%+d#0x%lx", insn->off, address); - return dd->scratch_buff; -} - -static const char *print_call_helper(struct dump_data *dd, - struct kernel_sym *sym, - unsigned long address) -{ - if (sym) - snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), - "%s", sym->name); - else - snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), - "0x%lx", address); - return dd->scratch_buff; -} - -static const char *print_call(void *private_data, - const struct bpf_insn *insn) -{ - struct dump_data *dd = private_data; - unsigned long address = dd->address_call_base + insn->imm; - struct kernel_sym *sym; - - sym = kernel_syms_search(dd, address); - if (insn->src_reg == BPF_PSEUDO_CALL) - return print_call_pcrel(dd, sym, address, insn); - else - return print_call_helper(dd, sym, address); -} - -static const char *print_imm(void *private_data, - const struct bpf_insn *insn, - __u64 full_imm) -{ - struct dump_data *dd = private_data; - - if (insn->src_reg == BPF_PSEUDO_MAP_FD) - snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), - "map[id:%u]", insn->imm); - else - snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), - "0x%llx", (unsigned long long)full_imm); - return dd->scratch_buff; -} - -static void dump_xlated_plain(struct dump_data *dd, void *buf, - unsigned int len, bool opcodes) -{ - const struct bpf_insn_cbs cbs = { - .cb_print = print_insn, - .cb_call = print_call, - .cb_imm = print_imm, - .private_data = dd, - }; - struct bpf_insn *insn = buf; - bool double_insn = false; - unsigned int i; - - for (i = 0; i < len / sizeof(*insn); i++) { - if (double_insn) { - double_insn = false; - continue; - } - - double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); - - printf("% 4d: ", i); - print_bpf_insn(&cbs, NULL, insn + i, true); - - if (opcodes) { - printf(" "); - fprint_hex(stdout, insn + i, 8, " "); - if (double_insn && i < len - 1) { - printf(" "); - fprint_hex(stdout, insn + i + 1, 8, " "); - } - printf("\n"); - } - } -} - -static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) -{ - unsigned int l = strlen(fmt); - char chomped_fmt[l]; - va_list args; - - va_start(args, fmt); - if (l > 0) { - strncpy(chomped_fmt, fmt, l - 1); - chomped_fmt[l - 1] = '\0'; - } - jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); - va_end(args); -} - -static void dump_xlated_json(struct dump_data *dd, void *buf, - unsigned int len, bool opcodes) -{ - const struct bpf_insn_cbs cbs = { - .cb_print = print_insn_json, - .cb_call = print_call, - .cb_imm = print_imm, - .private_data = dd, - }; - struct bpf_insn *insn = buf; - bool double_insn = false; - unsigned int i; - - jsonw_start_array(json_wtr); - for (i = 0; i < len / sizeof(*insn); i++) { - if (double_insn) { - double_insn = false; - continue; - } - double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); - - jsonw_start_object(json_wtr); - jsonw_name(json_wtr, "disasm"); - print_bpf_insn(&cbs, NULL, insn + i, true); - - if (opcodes) { - jsonw_name(json_wtr, "opcodes"); - jsonw_start_object(json_wtr); - - jsonw_name(json_wtr, "code"); - jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); - - jsonw_name(json_wtr, "src_reg"); - jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); - - jsonw_name(json_wtr, "dst_reg"); - jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); - - jsonw_name(json_wtr, "off"); - print_hex_data_json((uint8_t *)(&insn[i].off), 2); - - jsonw_name(json_wtr, "imm"); - if (double_insn && i < len - 1) - print_hex_data_json((uint8_t *)(&insn[i].imm), - 12); - else - print_hex_data_json((uint8_t *)(&insn[i].imm), - 4); - jsonw_end_object(json_wtr); - } - jsonw_end_object(json_wtr); - } - jsonw_end_array(json_wtr); -} - static int do_dump(int argc, char **argv) { struct bpf_prog_info info = {}; @@ -668,6 +416,7 @@ static int do_dump(int argc, char **argv) unsigned int buf_size; char *filepath = NULL; bool opcodes = false; + bool visual = false; unsigned char *buf; __u32 *member_len; __u64 *member_ptr; @@ -706,6 +455,9 @@ static int do_dump(int argc, char **argv) } else if (is_prefix(*argv, "opcodes")) { opcodes = true; NEXT_ARG(); + } else if (is_prefix(*argv, "visual")) { + visual = true; + NEXT_ARG(); } if (argc) { @@ -777,27 +529,30 @@ static int do_dump(int argc, char **argv) if (json_output) jsonw_null(json_wtr); - } else { - if (member_len == &info.jited_prog_len) { - const char *name = NULL; - - if (info.ifindex) { - name = ifindex_to_bfd_name_ns(info.ifindex, - info.netns_dev, - info.netns_ino); - if (!name) - goto err_free; - } - - disasm_print_insn(buf, *member_len, opcodes, name); - } else { - kernel_syms_load(&dd); - if (json_output) - dump_xlated_json(&dd, buf, *member_len, opcodes); - else - dump_xlated_plain(&dd, buf, *member_len, opcodes); - kernel_syms_destroy(&dd); + } else if (member_len == &info.jited_prog_len) { + const char *name = NULL; + + if (info.ifindex) { + name = ifindex_to_bfd_name_ns(info.ifindex, + info.netns_dev, + info.netns_ino); + if (!name) + goto err_free; } + + disasm_print_insn(buf, *member_len, opcodes, name); + } else if (visual) { + if (json_output) + jsonw_null(json_wtr); + else + dump_xlated_cfg(buf, *member_len); + } else { + kernel_syms_load(&dd); + if (json_output) + dump_xlated_json(&dd, buf, *member_len, opcodes); + else + dump_xlated_plain(&dd, buf, *member_len, opcodes); + kernel_syms_destroy(&dd); } free(buf); @@ -851,7 +606,7 @@ static int do_help(int argc, char **argv) fprintf(stderr, "Usage: %s %s { show | list } [PROG]\n" - " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" + " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" " %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s pin PROG FILE\n" " %s %s load OBJ FILE\n" diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c new file mode 100644 index 000000000000..20da835e9e38 --- /dev/null +++ b/tools/bpf/bpftool/xlated_dumper.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Copyright (C) 2018 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "disasm.h" +#include "json_writer.h" +#include "main.h" +#include "xlated_dumper.h" + +static int kernel_syms_cmp(const void *sym_a, const void *sym_b) +{ + return ((struct kernel_sym *)sym_a)->address - + ((struct kernel_sym *)sym_b)->address; +} + +void kernel_syms_load(struct dump_data *dd) +{ + struct kernel_sym *sym; + char buff[256]; + void *tmp, *address; + FILE *fp; + + fp = fopen("/proc/kallsyms", "r"); + if (!fp) + return; + + while (!feof(fp)) { + if (!fgets(buff, sizeof(buff), fp)) + break; + tmp = realloc(dd->sym_mapping, + (dd->sym_count + 1) * + sizeof(*dd->sym_mapping)); + if (!tmp) { +out: + free(dd->sym_mapping); + dd->sym_mapping = NULL; + fclose(fp); + return; + } + dd->sym_mapping = tmp; + sym = &dd->sym_mapping[dd->sym_count]; + if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) + continue; + sym->address = (unsigned long)address; + if (!strcmp(sym->name, "__bpf_call_base")) { + dd->address_call_base = sym->address; + /* sysctl kernel.kptr_restrict was set */ + if (!sym->address) + goto out; + } + if (sym->address) + dd->sym_count++; + } + + fclose(fp); + + qsort(dd->sym_mapping, dd->sym_count, + sizeof(*dd->sym_mapping), kernel_syms_cmp); +} + +void kernel_syms_destroy(struct dump_data *dd) +{ + free(dd->sym_mapping); +} + +static struct kernel_sym *kernel_syms_search(struct dump_data *dd, + unsigned long key) +{ + struct kernel_sym sym = { + .address = key, + }; + + return dd->sym_mapping ? + bsearch(&sym, dd->sym_mapping, dd->sym_count, + sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; +} + +static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +static void +print_insn_for_graph(struct bpf_verifier_env *env, const char *fmt, ...) +{ + char buf[64], *p; + va_list args; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + p = buf; + while (*p != '\0') { + if (*p == '\n') { + memmove(p + 3, p, strlen(buf) + 1 - (p - buf)); + /* Align each instruction dump row left. */ + *p++ = '\\'; + *p++ = 'l'; + /* Output multiline concatenation. */ + *p++ = '\\'; + } else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') { + memmove(p + 1, p, strlen(buf) + 1 - (p - buf)); + /* Escape special character. */ + *p++ = '\\'; + } + + p++; + } + + printf("%s", buf); +} + +static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) +{ + unsigned int l = strlen(fmt); + char chomped_fmt[l]; + va_list args; + + va_start(args, fmt); + if (l > 0) { + strncpy(chomped_fmt, fmt, l - 1); + chomped_fmt[l - 1] = '\0'; + } + jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); + va_end(args); +} + +static const char *print_call_pcrel(struct dump_data *dd, + struct kernel_sym *sym, + unsigned long address, + const struct bpf_insn *insn) +{ + if (sym) + snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), + "%+d#%s", insn->off, sym->name); + else + snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), + "%+d#0x%lx", insn->off, address); + return dd->scratch_buff; +} + +static const char *print_call_helper(struct dump_data *dd, + struct kernel_sym *sym, + unsigned long address) +{ + if (sym) + snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), + "%s", sym->name); + else + snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), + "0x%lx", address); + return dd->scratch_buff; +} + +static const char *print_call(void *private_data, + const struct bpf_insn *insn) +{ + struct dump_data *dd = private_data; + unsigned long address = dd->address_call_base + insn->imm; + struct kernel_sym *sym; + + sym = kernel_syms_search(dd, address); + if (insn->src_reg == BPF_PSEUDO_CALL) + return print_call_pcrel(dd, sym, address, insn); + else + return print_call_helper(dd, sym, address); +} + +static const char *print_imm(void *private_data, + const struct bpf_insn *insn, + __u64 full_imm) +{ + struct dump_data *dd = private_data; + + if (insn->src_reg == BPF_PSEUDO_MAP_FD) + snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), + "map[id:%u]", insn->imm); + else + snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), + "0x%llx", (unsigned long long)full_imm); + return dd->scratch_buff; +} + +void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, + bool opcodes) +{ + const struct bpf_insn_cbs cbs = { + .cb_print = print_insn_json, + .cb_call = print_call, + .cb_imm = print_imm, + .private_data = dd, + }; + struct bpf_insn *insn = buf; + bool double_insn = false; + unsigned int i; + + jsonw_start_array(json_wtr); + for (i = 0; i < len / sizeof(*insn); i++) { + if (double_insn) { + double_insn = false; + continue; + } + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); + + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "disasm"); + print_bpf_insn(&cbs, NULL, insn + i, true); + + if (opcodes) { + jsonw_name(json_wtr, "opcodes"); + jsonw_start_object(json_wtr); + + jsonw_name(json_wtr, "code"); + jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); + + jsonw_name(json_wtr, "src_reg"); + jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); + + jsonw_name(json_wtr, "dst_reg"); + jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); + + jsonw_name(json_wtr, "off"); + print_hex_data_json((uint8_t *)(&insn[i].off), 2); + + jsonw_name(json_wtr, "imm"); + if (double_insn && i < len - 1) + print_hex_data_json((uint8_t *)(&insn[i].imm), + 12); + else + print_hex_data_json((uint8_t *)(&insn[i].imm), + 4); + jsonw_end_object(json_wtr); + } + jsonw_end_object(json_wtr); + } + jsonw_end_array(json_wtr); +} + +void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, + bool opcodes) +{ + const struct bpf_insn_cbs cbs = { + .cb_print = print_insn, + .cb_call = print_call, + .cb_imm = print_imm, + .private_data = dd, + }; + struct bpf_insn *insn = buf; + bool double_insn = false; + unsigned int i; + + for (i = 0; i < len / sizeof(*insn); i++) { + if (double_insn) { + double_insn = false; + continue; + } + + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); + + printf("% 4d: ", i); + print_bpf_insn(&cbs, NULL, insn + i, true); + + if (opcodes) { + printf(" "); + fprint_hex(stdout, insn + i, 8, " "); + if (double_insn && i < len - 1) { + printf(" "); + fprint_hex(stdout, insn + i + 1, 8, " "); + } + printf("\n"); + } + } +} + +void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end, + unsigned int start_idx) +{ + const struct bpf_insn_cbs cbs = { + .cb_print = print_insn_for_graph, + .cb_call = print_call, + .cb_imm = print_imm, + .private_data = dd, + }; + struct bpf_insn *insn_start = buf_start; + struct bpf_insn *insn_end = buf_end; + struct bpf_insn *cur = insn_start; + + for (; cur <= insn_end; cur++) { + printf("% 4d: ", (int)(cur - insn_start + start_idx)); + print_bpf_insn(&cbs, NULL, cur, true); + if (cur != insn_end) + printf(" | "); + } +} diff --git a/tools/bpf/bpftool/xlated_dumper.h b/tools/bpf/bpftool/xlated_dumper.h new file mode 100644 index 000000000000..b34affa7ef2d --- /dev/null +++ b/tools/bpf/bpftool/xlated_dumper.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * Copyright (C) 2018 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BPF_TOOL_XLATED_DUMPER_H +#define __BPF_TOOL_XLATED_DUMPER_H + +#define SYM_MAX_NAME 256 + +struct kernel_sym { + unsigned long address; + char name[SYM_MAX_NAME]; +}; + +struct dump_data { + unsigned long address_call_base; + struct kernel_sym *sym_mapping; + __u32 sym_count; + char scratch_buff[SYM_MAX_NAME + 8]; +}; + +void kernel_syms_load(struct dump_data *dd); +void kernel_syms_destroy(struct dump_data *dd); +void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len, + bool opcodes); +void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len, + bool opcodes); +void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end, + unsigned int start_index); + +#endif diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index db6bdc375126..d245c41213ac 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -133,6 +133,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, + BPF_PROG_TYPE_SK_MSG, }; enum bpf_attach_type { @@ -143,6 +144,7 @@ enum bpf_attach_type { BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT, BPF_CGROUP_DEVICE, + BPF_SK_MSG_VERDICT, __MAX_BPF_ATTACH_TYPE }; @@ -231,6 +233,28 @@ enum bpf_attach_type { #define BPF_F_RDONLY (1U << 3) #define BPF_F_WRONLY (1U << 4) +/* Flag for stack_map, store build_id+offset instead of pointer */ +#define BPF_F_STACK_BUILD_ID (1U << 5) + +enum bpf_stack_build_id_status { + /* user space need an empty entry to identify end of a trace */ + BPF_STACK_BUILD_ID_EMPTY = 0, + /* with valid build_id and offset */ + BPF_STACK_BUILD_ID_VALID = 1, + /* couldn't get build_id, fallback to ip */ + BPF_STACK_BUILD_ID_IP = 2, +}; + +#define BPF_BUILD_ID_SIZE 20 +struct bpf_stack_build_id { + __s32 status; + unsigned char build_id[BPF_BUILD_ID_SIZE]; + union { + __u64 offset; + __u64 ip; + }; +}; + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -696,6 +720,15 @@ union bpf_attr { * int bpf_override_return(pt_regs, rc) * @pt_regs: pointer to struct pt_regs * @rc: the return value to set + * + * int bpf_msg_redirect_map(map, key, flags) + * Redirect msg to a sock in map using key as a lookup key for the + * sock in map. + * @map: pointer to sockmap + * @key: key to lookup sock in map + * @flags: reserved for future use + * Return: SK_PASS + * */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -757,7 +790,11 @@ union bpf_attr { FN(perf_prog_read_value), \ FN(getsockopt), \ FN(override_return), \ - FN(sock_ops_cb_flags_set), + FN(sock_ops_cb_flags_set), \ + FN(msg_redirect_map), \ + FN(msg_apply_bytes), \ + FN(msg_cork_bytes), \ + FN(msg_pull_data), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -919,6 +956,14 @@ enum sk_action { SK_PASS, }; +/* user accessible metadata for SK_MSG packet hook, new fields must + * be added to the end of this structure + */ +struct sk_msg_md { + void *data; + void *data_end; +}; + #define BPF_TAG_SIZE 8 struct bpf_prog_info { diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h index 0fb5ef939732..7b26d4b0b052 100644 --- a/tools/include/uapi/linux/kvm.h +++ b/tools/include/uapi/linux/kvm.h @@ -761,6 +761,7 @@ struct kvm_ppc_resize_hpt { #define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 #define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) +#define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list) /* * Extension capability list. @@ -934,6 +935,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_AIS_MIGRATION 150 #define KVM_CAP_PPC_GET_CPU_CHAR 151 #define KVM_CAP_S390_BPB 152 +#define KVM_CAP_GET_MSR_FEATURES 153 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat index a5684d0968b4..5898c22ba310 100755 --- a/tools/kvm/kvm_stat/kvm_stat +++ b/tools/kvm/kvm_stat/kvm_stat @@ -33,7 +33,7 @@ import resource import struct import re import subprocess -from collections import defaultdict +from collections import defaultdict, namedtuple VMX_EXIT_REASONS = { 'EXCEPTION_NMI': 0, @@ -228,6 +228,7 @@ IOCTL_NUMBERS = { } ENCODING = locale.getpreferredencoding(False) +TRACE_FILTER = re.compile(r'^[^\(]*$') class Arch(object): @@ -260,6 +261,11 @@ class Arch(object): return ArchX86(SVM_EXIT_REASONS) return + def tracepoint_is_child(self, field): + if (TRACE_FILTER.match(field)): + return None + return field.split('(', 1)[0] + class ArchX86(Arch): def __init__(self, exit_reasons): @@ -267,6 +273,10 @@ class ArchX86(Arch): self.ioctl_numbers = IOCTL_NUMBERS self.exit_reasons = exit_reasons + def debugfs_is_child(self, field): + """ Returns name of parent if 'field' is a child, None otherwise """ + return None + class ArchPPC(Arch): def __init__(self): @@ -282,6 +292,10 @@ class ArchPPC(Arch): self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16 self.exit_reasons = {} + def debugfs_is_child(self, field): + """ Returns name of parent if 'field' is a child, None otherwise """ + return None + class ArchA64(Arch): def __init__(self): @@ -289,6 +303,10 @@ class ArchA64(Arch): self.ioctl_numbers = IOCTL_NUMBERS self.exit_reasons = AARCH64_EXIT_REASONS + def debugfs_is_child(self, field): + """ Returns name of parent if 'field' is a child, None otherwise """ + return None + class ArchS390(Arch): def __init__(self): @@ -296,6 +314,12 @@ class ArchS390(Arch): self.ioctl_numbers = IOCTL_NUMBERS self.exit_reasons = None + def debugfs_is_child(self, field): + """ Returns name of parent if 'field' is a child, None otherwise """ + if field.startswith('instruction_'): + return 'exit_instruction' + + ARCH = Arch.get_arch() @@ -331,9 +355,6 @@ class perf_event_attr(ctypes.Structure): PERF_TYPE_TRACEPOINT = 2 PERF_FORMAT_GROUP = 1 << 3 -PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing' -PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm' - class Group(object): """Represents a perf event group.""" @@ -376,8 +397,8 @@ class Event(object): self.syscall = self.libc.syscall self.name = name self.fd = None - self.setup_event(group, trace_cpu, trace_pid, trace_point, - trace_filter, trace_set) + self._setup_event(group, trace_cpu, trace_pid, trace_point, + trace_filter, trace_set) def __del__(self): """Closes the event's file descriptor. @@ -390,7 +411,7 @@ class Event(object): if self.fd: os.close(self.fd) - def perf_event_open(self, attr, pid, cpu, group_fd, flags): + def _perf_event_open(self, attr, pid, cpu, group_fd, flags): """Wrapper for the sys_perf_evt_open() syscall. Used to set up performance events, returns a file descriptor or -1 @@ -409,7 +430,7 @@ class Event(object): ctypes.c_int(pid), ctypes.c_int(cpu), ctypes.c_int(group_fd), ctypes.c_long(flags)) - def setup_event_attribute(self, trace_set, trace_point): + def _setup_event_attribute(self, trace_set, trace_point): """Returns an initialized ctype perf_event_attr struct.""" id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set, @@ -419,8 +440,8 @@ class Event(object): event_attr.config = int(open(id_path).read()) return event_attr - def setup_event(self, group, trace_cpu, trace_pid, trace_point, - trace_filter, trace_set): + def _setup_event(self, group, trace_cpu, trace_pid, trace_point, + trace_filter, trace_set): """Sets up the perf event in Linux. Issues the syscall to register the event in the kernel and @@ -428,7 +449,7 @@ class Event(object): """ - event_attr = self.setup_event_attribute(trace_set, trace_point) + event_attr = self._setup_event_attribute(trace_set, trace_point) # First event will be group leader. group_leader = -1 @@ -437,8 +458,8 @@ class Event(object): if group.events: group_leader = group.events[0].fd - fd = self.perf_event_open(event_attr, trace_pid, - trace_cpu, group_leader, 0) + fd = self._perf_event_open(event_attr, trace_pid, + trace_cpu, group_leader, 0) if fd == -1: err = ctypes.get_errno() raise OSError(err, os.strerror(err), @@ -475,6 +496,10 @@ class Event(object): class Provider(object): """Encapsulates functionalities used by all providers.""" + def __init__(self, pid): + self.child_events = False + self.pid = pid + @staticmethod def is_field_wanted(fields_filter, field): """Indicate whether field is valid according to fields_filter.""" @@ -500,12 +525,12 @@ class TracepointProvider(Provider): """ def __init__(self, pid, fields_filter): self.group_leaders = [] - self.filters = self.get_filters() + self.filters = self._get_filters() self.update_fields(fields_filter) - self.pid = pid + super(TracepointProvider, self).__init__(pid) @staticmethod - def get_filters(): + def _get_filters(): """Returns a dict of trace events, their filter ids and the values that can be filtered. @@ -521,8 +546,8 @@ class TracepointProvider(Provider): filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons) return filters - def get_available_fields(self): - """Returns a list of available event's of format 'event name(filter + def _get_available_fields(self): + """Returns a list of available events of format 'event name(filter name)'. All available events have directories under @@ -549,11 +574,12 @@ class TracepointProvider(Provider): def update_fields(self, fields_filter): """Refresh fields, applying fields_filter""" - self.fields = [field for field in self.get_available_fields() - if self.is_field_wanted(fields_filter, field)] + self.fields = [field for field in self._get_available_fields() + if self.is_field_wanted(fields_filter, field) or + ARCH.tracepoint_is_child(field)] @staticmethod - def get_online_cpus(): + def _get_online_cpus(): """Returns a list of cpu id integers.""" def parse_int_list(list_string): """Returns an int list from a string of comma separated integers and @@ -575,17 +601,17 @@ class TracepointProvider(Provider): cpu_string = cpu_list.readline() return parse_int_list(cpu_string) - def setup_traces(self): + def _setup_traces(self): """Creates all event and group objects needed to be able to retrieve data.""" - fields = self.get_available_fields() + fields = self._get_available_fields() if self._pid > 0: # Fetch list of all threads of the monitored pid, as qemu # starts a thread for each vcpu. path = os.path.join('/proc', str(self._pid), 'task') groupids = self.walkdir(path)[1] else: - groupids = self.get_online_cpus() + groupids = self._get_online_cpus() # The constant is needed as a buffer for python libs, std # streams and other files that the script opens. @@ -663,7 +689,7 @@ class TracepointProvider(Provider): # The garbage collector will get rid of all Event/Group # objects and open files after removing the references. self.group_leaders = [] - self.setup_traces() + self._setup_traces() self.fields = self._fields def read(self, by_guest=0): @@ -671,8 +697,12 @@ class TracepointProvider(Provider): ret = defaultdict(int) for group in self.group_leaders: for name, val in group.read().items(): - if name in self._fields: - ret[name] += val + if name not in self._fields: + continue + parent = ARCH.tracepoint_is_child(name) + if parent: + name += ' ' + parent + ret[name] += val return ret def reset(self): @@ -690,11 +720,11 @@ class DebugfsProvider(Provider): self._baseline = {} self.do_read = True self.paths = [] - self.pid = pid + super(DebugfsProvider, self).__init__(pid) if include_past: - self.restore() + self._restore() - def get_available_fields(self): + def _get_available_fields(self): """"Returns a list of available fields. The fields are all available KVM debugfs files @@ -704,8 +734,9 @@ class DebugfsProvider(Provider): def update_fields(self, fields_filter): """Refresh fields, applying fields_filter""" - self._fields = [field for field in self.get_available_fields() - if self.is_field_wanted(fields_filter, field)] + self._fields = [field for field in self._get_available_fields() + if self.is_field_wanted(fields_filter, field) or + ARCH.debugfs_is_child(field)] @property def fields(self): @@ -758,7 +789,7 @@ class DebugfsProvider(Provider): paths.append(dir) for path in paths: for field in self._fields: - value = self.read_field(field, path) + value = self._read_field(field, path) key = path + field if reset == 1: self._baseline[key] = value @@ -766,20 +797,21 @@ class DebugfsProvider(Provider): self._baseline[key] = 0 if self._baseline.get(key, -1) == -1: self._baseline[key] = value - increment = (results.get(field, 0) + value - - self._baseline.get(key, 0)) - if by_guest: - pid = key.split('-')[0] - if pid in results: - results[pid] += increment - else: - results[pid] = increment + parent = ARCH.debugfs_is_child(field) + if parent: + field = field + ' ' + parent + else: + if by_guest: + field = key.split('-')[0] # set 'field' to 'pid' + increment = value - self._baseline.get(key, 0) + if field in results: + results[field] += increment else: results[field] = increment return results - def read_field(self, field, path): + def _read_field(self, field, path): """Returns the value of a single field from a specific VM.""" try: return int(open(os.path.join(PATH_DEBUGFS_KVM, @@ -794,12 +826,15 @@ class DebugfsProvider(Provider): self._baseline = {} self.read(1) - def restore(self): + def _restore(self): """Reset field counters""" self._baseline = {} self.read(2) +EventStat = namedtuple('EventStat', ['value', 'delta']) + + class Stats(object): """Manages the data providers and the data they provide. @@ -808,13 +843,13 @@ class Stats(object): """ def __init__(self, options): - self.providers = self.get_providers(options) + self.providers = self._get_providers(options) self._pid_filter = options.pid self._fields_filter = options.fields self.values = {} + self._child_events = False - @staticmethod - def get_providers(options): + def _get_providers(self, options): """Returns a list of data providers depending on the passed options.""" providers = [] @@ -826,7 +861,7 @@ class Stats(object): return providers - def update_provider_filters(self): + def _update_provider_filters(self): """Propagates fields filters to providers.""" # As we reset the counters when updating the fields we can # also clear the cache of old values. @@ -847,7 +882,7 @@ class Stats(object): def fields_filter(self, fields_filter): if fields_filter != self._fields_filter: self._fields_filter = fields_filter - self.update_provider_filters() + self._update_provider_filters() @property def pid_filter(self): @@ -861,16 +896,33 @@ class Stats(object): for provider in self.providers: provider.pid = self._pid_filter + @property + def child_events(self): + return self._child_events + + @child_events.setter + def child_events(self, val): + self._child_events = val + for provider in self.providers: + provider.child_events = val + def get(self, by_guest=0): """Returns a dict with field -> (value, delta to last value) of all - provider data.""" + provider data. + Key formats: + * plain: 'key' is event name + * child-parent: 'key' is in format '<child> <parent>' + * pid: 'key' is the pid of the guest, and the record contains the + aggregated event data + These formats are generated by the providers, and handled in class TUI. + """ for provider in self.providers: new = provider.read(by_guest=by_guest) - for key in new if by_guest else provider.fields: - oldval = self.values.get(key, (0, 0))[0] + for key in new: + oldval = self.values.get(key, EventStat(0, 0)).value newval = new.get(key, 0) newdelta = newval - oldval - self.values[key] = (newval, newdelta) + self.values[key] = EventStat(newval, newdelta) return self.values def toggle_display_guests(self, to_pid): @@ -899,10 +951,10 @@ class Stats(object): self.get(to_pid) return 0 + DELAY_DEFAULT = 3.0 MAX_GUEST_NAME_LEN = 48 MAX_REGEX_LEN = 44 -DEFAULT_REGEX = r'^[^\(]*$' SORT_DEFAULT = 0 @@ -969,7 +1021,7 @@ class Tui(object): return res - def print_all_gnames(self, row): + def _print_all_gnames(self, row): """Print a list of all running guests along with their pids.""" self.screen.addstr(row, 2, '%8s %-60s' % ('Pid', 'Guest Name (fuzzy list, might be ' @@ -1032,19 +1084,13 @@ class Tui(object): return name - def update_drilldown(self): - """Sets or removes a filter that only allows fields without braces.""" - if not self.stats.fields_filter: - self.stats.fields_filter = DEFAULT_REGEX - - elif self.stats.fields_filter == DEFAULT_REGEX: - self.stats.fields_filter = None - - def update_pid(self, pid): + def _update_pid(self, pid): """Propagates pid selection to stats object.""" + self.screen.addstr(4, 1, 'Updating pid filter...') + self.screen.refresh() self.stats.pid_filter = pid - def refresh_header(self, pid=None): + def _refresh_header(self, pid=None): """Refreshes the header.""" if pid is None: pid = self.stats.pid_filter @@ -1059,8 +1105,7 @@ class Tui(object): .format(pid, gname), curses.A_BOLD) else: self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD) - if self.stats.fields_filter and self.stats.fields_filter \ - != DEFAULT_REGEX: + if self.stats.fields_filter: regex = self.stats.fields_filter if len(regex) > MAX_REGEX_LEN: regex = regex[:MAX_REGEX_LEN] + '...' @@ -1075,56 +1120,99 @@ class Tui(object): self.screen.addstr(4, 1, 'Collecting data...') self.screen.refresh() - def refresh_body(self, sleeptime): + def _refresh_body(self, sleeptime): + def is_child_field(field): + return field.find('(') != -1 + + def insert_child(sorted_items, child, values, parent): + num = len(sorted_items) + for i in range(0, num): + # only add child if parent is present + if parent.startswith(sorted_items[i][0]): + sorted_items.insert(i + 1, (' ' + child, values)) + + def get_sorted_events(self, stats): + """ separate parent and child events """ + if self._sorting == SORT_DEFAULT: + def sortkey((_k, v)): + # sort by (delta value, overall value) + return (v.delta, v.value) + else: + def sortkey((_k, v)): + # sort by overall value + return v.value + + childs = [] + sorted_items = [] + # we can't rule out child events to appear prior to parents even + # when sorted - separate out all children first, and add in later + for key, values in sorted(stats.items(), key=sortkey, + reverse=True): + if values == (0, 0): + continue + if key.find(' ') != -1: + if not self.stats.child_events: + continue + childs.insert(0, (key, values)) + else: + sorted_items.append((key, values)) + if self.stats.child_events: + for key, values in childs: + (child, parent) = key.split(' ') + insert_child(sorted_items, child, values, parent) + + return sorted_items + row = 3 self.screen.move(row, 0) self.screen.clrtobot() stats = self.stats.get(self._display_guests) - - def sortCurAvg(x): - # sort by current events if available - if stats[x][1]: - return (-stats[x][1], -stats[x][0]) + total = 0. + ctotal = 0. + for key, values in stats.items(): + if self._display_guests: + if self.get_gname_from_pid(key): + total += values.value + continue + if not key.find(' ') != -1: + total += values.value else: - return (0, -stats[x][0]) + ctotal += values.value + if total == 0.: + # we don't have any fields, or all non-child events are filtered + total = ctotal - def sortTotal(x): - # sort by totals - return (0, -stats[x][0]) - total = 0. - for key in stats.keys(): - if key.find('(') is -1: - total += stats[key][0] - if self._sorting == SORT_DEFAULT: - sortkey = sortCurAvg - else: - sortkey = sortTotal + # print events tavg = 0 - for key in sorted(stats.keys(), key=sortkey): - if row >= self.screen.getmaxyx()[0] - 1: - break - values = stats[key] - if not values[0] and not values[1]: + tcur = 0 + for key, values in get_sorted_events(self, stats): + if row >= self.screen.getmaxyx()[0] - 1 or values == (0, 0): break - if values[0] is not None: - cur = int(round(values[1] / sleeptime)) if values[1] else '' - if self._display_guests: - key = self.get_gname_from_pid(key) - self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % - (key, values[0], values[0] * 100 / total, - cur)) - if cur is not '' and key.find('(') is -1: - tavg += cur + if self._display_guests: + key = self.get_gname_from_pid(key) + if not key: + continue + cur = int(round(values.delta / sleeptime)) if values.delta else '' + if key[0] != ' ': + if values.delta: + tcur += values.delta + ptotal = values.value + ltotal = total + else: + ltotal = ptotal + self.screen.addstr(row, 1, '%-40s %10d%7.1f %8s' % (key, + values.value, + values.value * 100 / float(ltotal), cur)) row += 1 if row == 3: self.screen.addstr(4, 1, 'No matching events reported yet') - else: + if row > 4: + tavg = int(round(tcur / sleeptime)) if tcur > 0 else '' self.screen.addstr(row, 1, '%-40s %10d %8s' % - ('Total', total, tavg if tavg else ''), - curses.A_BOLD) + ('Total', total, tavg), curses.A_BOLD) self.screen.refresh() - def show_msg(self, text): + def _show_msg(self, text): """Display message centered text and exit on key press""" hint = 'Press any key to continue' curses.cbreak() @@ -1139,16 +1227,16 @@ class Tui(object): curses.A_STANDOUT) self.screen.getkey() - def show_help_interactive(self): + def _show_help_interactive(self): """Display help with list of interactive commands""" msg = (' b toggle events by guests (debugfs only, honors' ' filters)', ' c clear filter', ' f filter by regular expression', - ' g filter by guest name', + ' g filter by guest name/PID', ' h display interactive commands reference', ' o toggle sorting order (Total vs CurAvg/s)', - ' p filter by PID', + ' p filter by guest name/PID', ' q quit', ' r reset stats', ' s set update interval', @@ -1165,14 +1253,15 @@ class Tui(object): self.screen.addstr(row, 0, line) row += 1 self.screen.getkey() - self.refresh_header() + self._refresh_header() - def show_filter_selection(self): + def _show_filter_selection(self): """Draws filter selection mask. Asks for a valid regex and sets the fields filter accordingly. """ + msg = '' while True: self.screen.erase() self.screen.addstr(0, 0, @@ -1181,61 +1270,25 @@ class Tui(object): self.screen.addstr(2, 0, "Current regex: {0}" .format(self.stats.fields_filter)) + self.screen.addstr(5, 0, msg) self.screen.addstr(3, 0, "New regex: ") curses.echo() regex = self.screen.getstr().decode(ENCODING) curses.noecho() if len(regex) == 0: - self.stats.fields_filter = DEFAULT_REGEX - self.refresh_header() + self.stats.fields_filter = '' + self._refresh_header() return try: re.compile(regex) self.stats.fields_filter = regex - self.refresh_header() + self._refresh_header() return except re.error: + msg = '"' + regex + '": Not a valid regular expression' continue - def show_vm_selection_by_pid(self): - """Draws PID selection mask. - - Asks for a pid until a valid pid or 0 has been entered. - - """ - msg = '' - while True: - self.screen.erase() - self.screen.addstr(0, 0, - 'Show statistics for specific pid.', - curses.A_BOLD) - self.screen.addstr(1, 0, - 'This might limit the shown data to the trace ' - 'statistics.') - self.screen.addstr(5, 0, msg) - self.print_all_gnames(7) - - curses.echo() - self.screen.addstr(3, 0, "Pid [0 or pid]: ") - pid = self.screen.getstr().decode(ENCODING) - curses.noecho() - - try: - if len(pid) > 0: - pid = int(pid) - if pid != 0 and not os.path.isdir(os.path.join('/proc/', - str(pid))): - msg = '"' + str(pid) + '": Not a running process' - continue - else: - pid = 0 - self.refresh_header(pid) - self.update_pid(pid) - break - except ValueError: - msg = '"' + str(pid) + '": Not a valid pid' - - def show_set_update_interval(self): + def _show_set_update_interval(self): """Draws update interval selection mask.""" msg = '' while True: @@ -1265,60 +1318,67 @@ class Tui(object): except ValueError: msg = '"' + str(val) + '": Invalid value' - self.refresh_header() + self._refresh_header() - def show_vm_selection_by_guest_name(self): + def _show_vm_selection_by_guest(self): """Draws guest selection mask. - Asks for a guest name until a valid guest name or '' is entered. + Asks for a guest name or pid until a valid guest name or '' is entered. """ msg = '' while True: self.screen.erase() self.screen.addstr(0, 0, - 'Show statistics for specific guest.', + 'Show statistics for specific guest or pid.', curses.A_BOLD) self.screen.addstr(1, 0, 'This might limit the shown data to the trace ' 'statistics.') self.screen.addstr(5, 0, msg) - self.print_all_gnames(7) + self._print_all_gnames(7) curses.echo() - self.screen.addstr(3, 0, "Guest [ENTER or guest]: ") - gname = self.screen.getstr().decode(ENCODING) + curses.curs_set(1) + self.screen.addstr(3, 0, "Guest or pid [ENTER exits]: ") + guest = self.screen.getstr().decode(ENCODING) curses.noecho() - if not gname: - self.refresh_header(0) - self.update_pid(0) + pid = 0 + if not guest or guest == '0': break - else: - pids = [] - try: - pids = self.get_pid_from_gname(gname) - except: - msg = '"' + gname + '": Internal error while searching, ' \ - 'use pid filter instead' - continue - if len(pids) == 0: - msg = '"' + gname + '": Not an active guest' + if guest.isdigit(): + if not os.path.isdir(os.path.join('/proc/', guest)): + msg = '"' + guest + '": Not a running process' continue - if len(pids) > 1: - msg = '"' + gname + '": Multiple matches found, use pid ' \ - 'filter instead' - continue - self.refresh_header(pids[0]) - self.update_pid(pids[0]) + pid = int(guest) break + pids = [] + try: + pids = self.get_pid_from_gname(guest) + except: + msg = '"' + guest + '": Internal error while searching, ' \ + 'use pid filter instead' + continue + if len(pids) == 0: + msg = '"' + guest + '": Not an active guest' + continue + if len(pids) > 1: + msg = '"' + guest + '": Multiple matches found, use pid ' \ + 'filter instead' + continue + pid = pids[0] + break + curses.curs_set(0) + self._refresh_header(pid) + self._update_pid(pid) def show_stats(self): """Refreshes the screen and processes user input.""" sleeptime = self._delay_initial - self.refresh_header() + self._refresh_header() start = 0.0 # result based on init value never appears on screen while True: - self.refresh_body(time.time() - start) + self._refresh_body(time.time() - start) curses.halfdelay(int(sleeptime * 10)) start = time.time() sleeptime = self._delay_regular @@ -1327,47 +1387,39 @@ class Tui(object): if char == 'b': self._display_guests = not self._display_guests if self.stats.toggle_display_guests(self._display_guests): - self.show_msg(['Command not available with tracepoints' - ' enabled', 'Restart with debugfs only ' - '(see option \'-d\') and try again!']) + self._show_msg(['Command not available with ' + 'tracepoints enabled', 'Restart with ' + 'debugfs only (see option \'-d\') and ' + 'try again!']) self._display_guests = not self._display_guests - self.refresh_header() + self._refresh_header() if char == 'c': - self.stats.fields_filter = DEFAULT_REGEX - self.refresh_header(0) - self.update_pid(0) + self.stats.fields_filter = '' + self._refresh_header(0) + self._update_pid(0) if char == 'f': curses.curs_set(1) - self.show_filter_selection() + self._show_filter_selection() curses.curs_set(0) sleeptime = self._delay_initial - if char == 'g': - curses.curs_set(1) - self.show_vm_selection_by_guest_name() - curses.curs_set(0) + if char == 'g' or char == 'p': + self._show_vm_selection_by_guest() sleeptime = self._delay_initial if char == 'h': - self.show_help_interactive() + self._show_help_interactive() if char == 'o': self._sorting = not self._sorting - if char == 'p': - curses.curs_set(1) - self.show_vm_selection_by_pid() - curses.curs_set(0) - sleeptime = self._delay_initial if char == 'q': break if char == 'r': self.stats.reset() if char == 's': curses.curs_set(1) - self.show_set_update_interval() + self._show_set_update_interval() curses.curs_set(0) sleeptime = self._delay_initial if char == 'x': - self.update_drilldown() - # prevents display of current values on next refresh - self.stats.get(self._display_guests) + self.stats.child_events = not self.stats.child_events except KeyboardInterrupt: break except curses.error: @@ -1380,9 +1432,9 @@ def batch(stats): s = stats.get() time.sleep(1) s = stats.get() - for key in sorted(s.keys()): - values = s[key] - print('%-42s%10d%10d' % (key, values[0], values[1])) + for key, values in sorted(s.items()): + print('%-42s%10d%10d' % (key.split(' ')[0], values.value, + values.delta)) except KeyboardInterrupt: pass @@ -1392,14 +1444,14 @@ def log(stats): keys = sorted(stats.get().keys()) def banner(): - for k in keys: - print(k, end=' ') + for key in keys: + print(key.split(' ')[0], end=' ') print() def statline(): s = stats.get() - for k in keys: - print(' %9d' % s[k][1], end=' ') + for key in keys: + print(' %9d' % s[key].delta, end=' ') print() line = 0 banner_repeat = 20 @@ -1504,7 +1556,7 @@ Press any other key to refresh statistics immediately. ) optparser.add_option('-f', '--fields', action='store', - default=DEFAULT_REGEX, + default='', dest='fields', help='''fields to display (regex) "-f help" for a list of available events''', @@ -1539,17 +1591,6 @@ Press any other key to refresh statistics immediately. def check_access(options): """Exits if the current user can't access all needed directories.""" - if not os.path.exists('/sys/kernel/debug'): - sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.') - sys.exit(1) - - if not os.path.exists(PATH_DEBUGFS_KVM): - sys.stderr.write("Please make sure, that debugfs is mounted and " - "readable by the current user:\n" - "('mount -t debugfs debugfs /sys/kernel/debug')\n" - "Also ensure, that the kvm modules are loaded.\n") - sys.exit(1) - if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints or not options.debugfs): sys.stderr.write("Please enable CONFIG_TRACING in your kernel " @@ -1567,7 +1608,33 @@ def check_access(options): return options +def assign_globals(): + global PATH_DEBUGFS_KVM + global PATH_DEBUGFS_TRACING + + debugfs = '' + for line in file('/proc/mounts'): + if line.split(' ')[0] == 'debugfs': + debugfs = line.split(' ')[1] + break + if debugfs == '': + sys.stderr.write("Please make sure that CONFIG_DEBUG_FS is enabled in " + "your kernel, mounted and\nreadable by the current " + "user:\n" + "('mount -t debugfs debugfs /sys/kernel/debug')\n") + sys.exit(1) + + PATH_DEBUGFS_KVM = os.path.join(debugfs, 'kvm') + PATH_DEBUGFS_TRACING = os.path.join(debugfs, 'tracing') + + if not os.path.exists(PATH_DEBUGFS_KVM): + sys.stderr.write("Please make sure that CONFIG_KVM is enabled in " + "your kernel and that the modules are loaded.\n") + sys.exit(1) + + def main(): + assign_globals() options = get_options() options = check_access(options) diff --git a/tools/kvm/kvm_stat/kvm_stat.txt b/tools/kvm/kvm_stat/kvm_stat.txt index b5b3810c9e94..0811d860fe75 100644 --- a/tools/kvm/kvm_stat/kvm_stat.txt +++ b/tools/kvm/kvm_stat/kvm_stat.txt @@ -35,13 +35,13 @@ INTERACTIVE COMMANDS *f*:: filter by regular expression -*g*:: filter by guest name +*g*:: filter by guest name/PID *h*:: display interactive commands reference *o*:: toggle sorting order (Total vs CurAvg/s) -*p*:: filter by PID +*p*:: filter by guest name/PID *q*:: quit diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 5bbbf285af74..64a8fc384186 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1857,6 +1857,7 @@ static const struct { BPF_PROG_SEC("lwt_xmit", BPF_PROG_TYPE_LWT_XMIT), BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS), BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB), + BPF_PROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG), }; #undef BPF_PROG_SEC diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 57254f5b2779..694abc628e9b 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -29,7 +29,7 @@ #include "builtin.h" #include "check.h" -bool no_fp, no_unreachable; +bool no_fp, no_unreachable, retpoline, module; static const char * const check_usage[] = { "objtool check [<options>] file.o", @@ -39,6 +39,8 @@ static const char * const check_usage[] = { const struct option check_options[] = { OPT_BOOLEAN('f', "no-fp", &no_fp, "Skip frame pointer validation"), OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"), + OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), + OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), OPT_END(), }; @@ -53,5 +55,5 @@ int cmd_check(int argc, const char **argv) objname = argv[0]; - return check(objname, no_fp, no_unreachable, false); + return check(objname, false); } diff --git a/tools/objtool/builtin-orc.c b/tools/objtool/builtin-orc.c index 91e8e19ff5e0..77ea2b97117d 100644 --- a/tools/objtool/builtin-orc.c +++ b/tools/objtool/builtin-orc.c @@ -25,7 +25,6 @@ */ #include <string.h> -#include <subcmd/parse-options.h> #include "builtin.h" #include "check.h" @@ -36,9 +35,6 @@ static const char *orc_usage[] = { NULL, }; -extern const struct option check_options[]; -extern bool no_fp, no_unreachable; - int cmd_orc(int argc, const char **argv) { const char *objname; @@ -54,7 +50,7 @@ int cmd_orc(int argc, const char **argv) objname = argv[0]; - return check(objname, no_fp, no_unreachable, true); + return check(objname, true); } if (!strcmp(argv[0], "dump")) { diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h index dd526067fed5..28ff40e19a14 100644 --- a/tools/objtool/builtin.h +++ b/tools/objtool/builtin.h @@ -17,6 +17,11 @@ #ifndef _BUILTIN_H #define _BUILTIN_H +#include <subcmd/parse-options.h> + +extern const struct option check_options[]; +extern bool no_fp, no_unreachable, retpoline, module; + extern int cmd_check(int argc, const char **argv); extern int cmd_orc(int argc, const char **argv); diff --git a/tools/objtool/check.c b/tools/objtool/check.c index a8cb69a26576..92b6a2c21631 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -18,6 +18,7 @@ #include <string.h> #include <stdlib.h> +#include "builtin.h" #include "check.h" #include "elf.h" #include "special.h" @@ -33,7 +34,6 @@ struct alternative { }; const char *objname; -static bool no_fp; struct cfi_state initial_func_cfi; struct instruction *find_insn(struct objtool_file *file, @@ -497,6 +497,7 @@ static int add_jump_destinations(struct objtool_file *file) * disguise, so convert them accordingly. */ insn->type = INSN_JUMP_DYNAMIC; + insn->retpoline_safe = true; continue; } else { /* sibling call */ @@ -548,7 +549,8 @@ static int add_call_destinations(struct objtool_file *file) if (!insn->call_dest && !insn->ignore) { WARN_FUNC("unsupported intra-function call", insn->sec, insn->offset); - WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE."); + if (retpoline) + WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE."); return -1; } @@ -923,7 +925,11 @@ static struct rela *find_switch_table(struct objtool_file *file, if (find_symbol_containing(file->rodata, text_rela->addend)) continue; - return find_rela_by_dest(file->rodata, text_rela->addend); + rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend); + if (!rodata_rela) + continue; + + return rodata_rela; } return NULL; @@ -1108,6 +1114,41 @@ static int read_unwind_hints(struct objtool_file *file) return 0; } +static int read_retpoline_hints(struct objtool_file *file) +{ + struct section *sec; + struct instruction *insn; + struct rela *rela; + + sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); + if (!sec) + return 0; + + list_for_each_entry(rela, &sec->rela_list, list) { + if (rela->sym->type != STT_SECTION) { + WARN("unexpected relocation symbol type in %s", sec->name); + return -1; + } + + insn = find_insn(file, rela->sym->sec, rela->addend); + if (!insn) { + WARN("bad .discard.retpoline_safe entry"); + return -1; + } + + if (insn->type != INSN_JUMP_DYNAMIC && + insn->type != INSN_CALL_DYNAMIC) { + WARN_FUNC("retpoline_safe hint not an indirect jump/call", + insn->sec, insn->offset); + return -1; + } + + insn->retpoline_safe = true; + } + + return 0; +} + static int decode_sections(struct objtool_file *file) { int ret; @@ -1146,6 +1187,10 @@ static int decode_sections(struct objtool_file *file) if (ret) return ret; + ret = read_retpoline_hints(file); + if (ret) + return ret; + return 0; } @@ -1891,6 +1936,38 @@ static int validate_unwind_hints(struct objtool_file *file) return warnings; } +static int validate_retpoline(struct objtool_file *file) +{ + struct instruction *insn; + int warnings = 0; + + for_each_insn(file, insn) { + if (insn->type != INSN_JUMP_DYNAMIC && + insn->type != INSN_CALL_DYNAMIC) + continue; + + if (insn->retpoline_safe) + continue; + + /* + * .init.text code is ran before userspace and thus doesn't + * strictly need retpolines, except for modules which are + * loaded late, they very much do need retpoline in their + * .init.text + */ + if (!strcmp(insn->sec->name, ".init.text") && !module) + continue; + + WARN_FUNC("indirect %s found in RETPOLINE build", + insn->sec, insn->offset, + insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call"); + + warnings++; + } + + return warnings; +} + static bool is_kasan_insn(struct instruction *insn) { return (insn->type == INSN_CALL && @@ -2022,13 +2099,12 @@ static void cleanup(struct objtool_file *file) elf_close(file->elf); } -int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc) +int check(const char *_objname, bool orc) { struct objtool_file file; int ret, warnings = 0; objname = _objname; - no_fp = _no_fp; file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY); if (!file.elf) @@ -2052,6 +2128,13 @@ int check(const char *_objname, bool _no_fp, bool no_unreachable, bool orc) if (list_empty(&file.insn_list)) goto out; + if (retpoline) { + ret = validate_retpoline(&file); + if (ret < 0) + return ret; + warnings += ret; + } + ret = validate_functions(&file); if (ret < 0) goto out; diff --git a/tools/objtool/check.h b/tools/objtool/check.h index 23a1d065cae1..c6b68fcb926f 100644 --- a/tools/objtool/check.h +++ b/tools/objtool/check.h @@ -45,6 +45,7 @@ struct instruction { unsigned char type; unsigned long immediate; bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts; + bool retpoline_safe; struct symbol *call_dest; struct instruction *jump_dest; struct instruction *first_jump_src; @@ -63,7 +64,7 @@ struct objtool_file { bool ignore_unreachables, c_file, hints; }; -int check(const char *objname, bool no_fp, bool no_unreachable, bool orc); +int check(const char *objname, bool orc); struct instruction *find_insn(struct objtool_file *file, struct section *sec, unsigned long offset); diff --git a/tools/perf/Documentation/perf-kallsyms.txt b/tools/perf/Documentation/perf-kallsyms.txt index 954ea9e21236..cf9f4040ea5c 100644 --- a/tools/perf/Documentation/perf-kallsyms.txt +++ b/tools/perf/Documentation/perf-kallsyms.txt @@ -8,7 +8,7 @@ perf-kallsyms - Searches running kernel for symbols SYNOPSIS -------- [verse] -'perf kallsyms <options> symbol_name[,symbol_name...]' +'perf kallsyms' [<options>] symbol_name[,symbol_name...] DESCRIPTION ----------- diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index bf4ca749d1ac..a217623fec2e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -881,6 +881,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } } + /* + * If we have just single event and are sending data + * through pipe, we need to force the ids allocation, + * because we synthesize event name through the pipe + * and need the id for that. + */ + if (data->is_pipe && rec->evlist->nr_entries == 1) + rec->opts.sample_id = true; + if (record__open(rec) != 0) { err = -1; goto out_child; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 98bf9d32f222..54a4c152edb3 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -917,7 +917,7 @@ static void print_metric_csv(void *ctx, char buf[64], *vals, *ends; if (unit == NULL || fmt == NULL) { - fprintf(out, "%s%s%s%s", csv_sep, csv_sep, csv_sep, csv_sep); + fprintf(out, "%s%s", csv_sep, csv_sep); return; } snprintf(buf, sizeof(buf), fmt, val); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b7c823ba8374..35ac016fcb98 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -991,7 +991,7 @@ static int perf_top_overwrite_fallback(struct perf_top *top, evlist__for_each_entry(evlist, counter) counter->attr.write_backward = false; opts->overwrite = false; - ui__warning("fall back to non-overwrite mode\n"); + pr_debug2("fall back to non-overwrite mode\n"); return 1; } diff --git a/tools/perf/perf.h b/tools/perf/perf.h index cfe46236a5e5..57b9b342d533 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -61,6 +61,7 @@ struct record_opts { bool tail_synthesize; bool overwrite; bool ignore_missing_thread; + bool sample_id; unsigned int freq; unsigned int mmap_pages; unsigned int auxtrace_mmap_pages; diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 286427975112..fbf927cf775d 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -327,7 +327,32 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) if (!disasm_line__is_valid_jump(cursor, sym)) return; + /* + * This first was seen with a gcc function, _cpp_lex_token, that + * has the usual jumps: + * + * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> + * + * I.e. jumps to a label inside that function (_cpp_lex_token), and + * those works, but also this kind: + * + * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72> + * + * I.e. jumps to another function, outside _cpp_lex_token, which + * are not being correctly handled generating as a side effect references + * to ab->offset[] entries that are set to NULL, so to make this code + * more robust, check that here. + * + * A proper fix for will be put in place, looking at the function + * name right after the '<' token and probably treating this like a + * 'call' instruction. + */ target = ab->offsets[cursor->ops.target.offset]; + if (target == NULL) { + ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n", + cursor->ops.target.offset); + return; + } bcursor = browser_line(&cursor->al); btarget = browser_line(target); diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 9faf3b5367db..6470ea2aa25e 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -60,6 +60,12 @@ #include "sane_ctype.h" #include "symbol/kallsyms.h" +static bool auxtrace__dont_decode(struct perf_session *session) +{ + return !session->itrace_synth_opts || + session->itrace_synth_opts->dont_decode; +} + int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, struct auxtrace_mmap_params *mp, void *userpg, int fd) @@ -762,6 +768,9 @@ int auxtrace_queues__process_index(struct auxtrace_queues *queues, size_t i; int err; + if (auxtrace__dont_decode(session)) + return 0; + list_for_each_entry(auxtrace_index, &session->auxtrace_index, list) { for (i = 0; i < auxtrace_index->nr; i++) { ent = &auxtrace_index->entries[i]; @@ -892,12 +901,6 @@ out_free: return err; } -static bool auxtrace__dont_decode(struct perf_session *session) -{ - return !session->itrace_synth_opts || - session->itrace_synth_opts->dont_decode; -} - int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_session *session) diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 1e97937b03a9..6f09e4962dad 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -137,6 +137,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, struct perf_evsel *evsel; bool use_sample_identifier = false; bool use_comm_exec; + bool sample_id = opts->sample_id; /* * Set the evsel leader links before we configure attributes, @@ -163,8 +164,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, * match the id. */ use_sample_identifier = perf_can_sample_identifier(); - evlist__for_each_entry(evlist, evsel) - perf_evsel__set_sample_id(evsel, use_sample_identifier); + sample_id = true; } else if (evlist->nr_entries > 1) { struct perf_evsel *first = perf_evlist__first(evlist); @@ -174,6 +174,10 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts, use_sample_identifier = perf_can_sample_identifier(); break; } + sample_id = true; + } + + if (sample_id) { evlist__for_each_entry(evlist, evsel) perf_evsel__set_sample_id(evsel, use_sample_identifier); } diff --git a/tools/perf/util/trigger.h b/tools/perf/util/trigger.h index 370138e7e35c..88223bc7c82b 100644 --- a/tools/perf/util/trigger.h +++ b/tools/perf/util/trigger.h @@ -12,7 +12,7 @@ * States and transits: * * - * OFF--(on)--> READY --(hit)--> HIT + * OFF--> ON --> READY --(hit)--> HIT * ^ | * | (ready) * | | @@ -27,8 +27,9 @@ struct trigger { volatile enum { TRIGGER_ERROR = -2, TRIGGER_OFF = -1, - TRIGGER_READY = 0, - TRIGGER_HIT = 1, + TRIGGER_ON = 0, + TRIGGER_READY = 1, + TRIGGER_HIT = 2, } state; const char *name; }; @@ -50,7 +51,7 @@ static inline bool trigger_is_error(struct trigger *t) static inline void trigger_on(struct trigger *t) { TRIGGER_WARN_ONCE(t, TRIGGER_OFF); - t->state = TRIGGER_READY; + t->state = TRIGGER_ON; } static inline void trigger_ready(struct trigger *t) diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c index 44ef9eba5a7a..6c645eb77d42 100644 --- a/tools/testing/radix-tree/idr-test.c +++ b/tools/testing/radix-tree/idr-test.c @@ -178,6 +178,55 @@ void idr_get_next_test(int base) idr_destroy(&idr); } +int idr_u32_cb(int id, void *ptr, void *data) +{ + BUG_ON(id < 0); + BUG_ON(ptr != DUMMY_PTR); + return 0; +} + +void idr_u32_test1(struct idr *idr, u32 handle) +{ + static bool warned = false; + u32 id = handle; + int sid = 0; + void *ptr; + + BUG_ON(idr_alloc_u32(idr, DUMMY_PTR, &id, id, GFP_KERNEL)); + BUG_ON(id != handle); + BUG_ON(idr_alloc_u32(idr, DUMMY_PTR, &id, id, GFP_KERNEL) != -ENOSPC); + BUG_ON(id != handle); + if (!warned && id > INT_MAX) + printk("vvv Ignore these warnings\n"); + ptr = idr_get_next(idr, &sid); + if (id > INT_MAX) { + BUG_ON(ptr != NULL); + BUG_ON(sid != 0); + } else { + BUG_ON(ptr != DUMMY_PTR); + BUG_ON(sid != id); + } + idr_for_each(idr, idr_u32_cb, NULL); + if (!warned && id > INT_MAX) { + printk("^^^ Warnings over\n"); + warned = true; + } + BUG_ON(idr_remove(idr, id) != DUMMY_PTR); + BUG_ON(!idr_is_empty(idr)); +} + +void idr_u32_test(int base) +{ + DEFINE_IDR(idr); + idr_init_base(&idr, base); + idr_u32_test1(&idr, 10); + idr_u32_test1(&idr, 0x7fffffff); + idr_u32_test1(&idr, 0x80000000); + idr_u32_test1(&idr, 0x80000001); + idr_u32_test1(&idr, 0xffe00000); + idr_u32_test1(&idr, 0xffffffff); +} + void idr_checks(void) { unsigned long i; @@ -248,6 +297,9 @@ void idr_checks(void) idr_get_next_test(0); idr_get_next_test(1); idr_get_next_test(4); + idr_u32_test(4); + idr_u32_test(1); + idr_u32_test(0); } /* diff --git a/tools/testing/radix-tree/linux.c b/tools/testing/radix-tree/linux.c index 6903ccf35595..44a0d1ad4408 100644 --- a/tools/testing/radix-tree/linux.c +++ b/tools/testing/radix-tree/linux.c @@ -29,7 +29,7 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, int flags) { struct radix_tree_node *node; - if (flags & __GFP_NOWARN) + if (!(flags & __GFP_DIRECT_RECLAIM)) return NULL; pthread_mutex_lock(&cachep->lock); @@ -73,10 +73,17 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) void *kmalloc(size_t size, gfp_t gfp) { - void *ret = malloc(size); + void *ret; + + if (!(gfp & __GFP_DIRECT_RECLAIM)) + return NULL; + + ret = malloc(size); uatomic_inc(&nr_allocated); if (kmalloc_verbose) printf("Allocating %p from malloc\n", ret); + if (gfp & __GFP_ZERO) + memset(ret, 0, size); return ret; } diff --git a/tools/testing/radix-tree/linux/compiler_types.h b/tools/testing/radix-tree/linux/compiler_types.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/radix-tree/linux/compiler_types.h diff --git a/tools/testing/radix-tree/linux/gfp.h b/tools/testing/radix-tree/linux/gfp.h index e9fff59dfd8a..e3201ccf54c3 100644 --- a/tools/testing/radix-tree/linux/gfp.h +++ b/tools/testing/radix-tree/linux/gfp.h @@ -11,6 +11,7 @@ #define __GFP_IO 0x40u #define __GFP_FS 0x80u #define __GFP_NOWARN 0x200u +#define __GFP_ZERO 0x8000u #define __GFP_ATOMIC 0x80000u #define __GFP_ACCOUNT 0x100000u #define __GFP_DIRECT_RECLAIM 0x400000u diff --git a/tools/testing/radix-tree/linux/slab.h b/tools/testing/radix-tree/linux/slab.h index 979baeec7e70..a037def0dec6 100644 --- a/tools/testing/radix-tree/linux/slab.h +++ b/tools/testing/radix-tree/linux/slab.h @@ -3,6 +3,7 @@ #define SLAB_H #include <linux/types.h> +#include <linux/gfp.h> #define SLAB_HWCACHE_ALIGN 1 #define SLAB_PANIC 2 @@ -11,6 +12,11 @@ void *kmalloc(size_t size, gfp_t); void kfree(void *); +static inline void *kzalloc(size_t size, gfp_t gfp) +{ + return kmalloc(size, gfp | __GFP_ZERO); +} + void *kmem_cache_alloc(struct kmem_cache *cachep, int flags); void kmem_cache_free(struct kmem_cache *cachep, void *objp); diff --git a/tools/testing/selftests/android/Makefile b/tools/testing/selftests/android/Makefile index 1a7492268993..f6304d2be90c 100644 --- a/tools/testing/selftests/android/Makefile +++ b/tools/testing/selftests/android/Makefile @@ -11,11 +11,11 @@ all: BUILD_TARGET=$(OUTPUT)/$$DIR; \ mkdir $$BUILD_TARGET -p; \ make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ - #SUBDIR test prog name should be in the form: SUBDIR_test.sh + #SUBDIR test prog name should be in the form: SUBDIR_test.sh \ TEST=$$DIR"_test.sh"; \ - if [ -e $$DIR/$$TEST ]; then - rsync -a $$DIR/$$TEST $$BUILD_TARGET/; - fi + if [ -e $$DIR/$$TEST ]; then \ + rsync -a $$DIR/$$TEST $$BUILD_TARGET/; \ + fi \ done override define RUN_TESTS diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 8567a858b789..f35fb02bdf56 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -13,6 +13,14 @@ endif CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include LDLIBS += -lcap -lelf -lrt -lpthread +TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read +all: $(TEST_CUSTOM_PROGS) + +$(TEST_CUSTOM_PROGS): urandom_read + +urandom_read: urandom_read.c + $(CC) -o $(TEST_CUSTOM_PROGS) -static $< + # Order correspond to 'make run_tests' order TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align test_verifier_log test_dev_cgroup test_tcpbpf_user @@ -21,7 +29,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \ test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \ - sample_map_ret0.o test_tcpbpf_kern.o + sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \ + sockmap_tcp_msg_prog.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \ @@ -74,3 +83,5 @@ $(OUTPUT)/%.o: %.c $(CLANG) $(CLANG_FLAGS) \ -O2 -target bpf -emit-llvm -c $< -o - | \ $(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@ + +EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index dde2c11d7771..7cae376d8d0c 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -86,6 +86,14 @@ static int (*bpf_perf_prog_read_value)(void *ctx, void *buf, (void *) BPF_FUNC_perf_prog_read_value; static int (*bpf_override_return)(void *ctx, unsigned long rc) = (void *) BPF_FUNC_override_return; +static int (*bpf_msg_redirect_map)(void *ctx, void *map, int key, int flags) = + (void *) BPF_FUNC_msg_redirect_map; +static int (*bpf_msg_apply_bytes)(void *ctx, int len) = + (void *) BPF_FUNC_msg_apply_bytes; +static int (*bpf_msg_cork_bytes)(void *ctx, int len) = + (void *) BPF_FUNC_msg_cork_bytes; +static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) = + (void *) BPF_FUNC_msg_pull_data; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions @@ -123,6 +131,8 @@ static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = (void *) BPF_FUNC_skb_under_cgroup; static int (*bpf_skb_change_head)(void *, int len, int flags) = (void *) BPF_FUNC_skb_change_head; +static int (*bpf_skb_pull_data)(void *, int len) = + (void *) BPF_FUNC_skb_pull_data; /* Scan the ARCH passed in from ARCH env variable (see Makefile) */ #if defined(__TARGET_ARCH_x86) diff --git a/tools/testing/selftests/bpf/bpf_rlimit.h b/tools/testing/selftests/bpf/bpf_rlimit.h new file mode 100644 index 000000000000..9dac9b30f8ef --- /dev/null +++ b/tools/testing/selftests/bpf/bpf_rlimit.h @@ -0,0 +1,28 @@ +#include <sys/resource.h> +#include <stdio.h> + +static __attribute__((constructor)) void bpf_rlimit_ctor(void) +{ + struct rlimit rlim_old, rlim_new = { + .rlim_cur = RLIM_INFINITY, + .rlim_max = RLIM_INFINITY, + }; + + getrlimit(RLIMIT_MEMLOCK, &rlim_old); + /* For the sake of running the test cases, we temporarily + * set rlimit to infinity in order for kernel to focus on + * errors from actual test cases and not getting noise + * from hitting memlock limits. The limit is on per-process + * basis and not a global one, hence destructor not really + * needed here. + */ + if (setrlimit(RLIMIT_MEMLOCK, &rlim_new) < 0) { + perror("Unable to lift memlock rlimit"); + /* Trying out lower limit, but expect potential test + * case failures from this! + */ + rlim_new.rlim_cur = rlim_old.rlim_cur + (1UL << 20); + rlim_new.rlim_max = rlim_old.rlim_max + (1UL << 20); + setrlimit(RLIMIT_MEMLOCK, &rlim_new); + } +} diff --git a/tools/testing/selftests/bpf/sockmap_parse_prog.c b/tools/testing/selftests/bpf/sockmap_parse_prog.c index a1dec2b6d9c5..0f92858f6226 100644 --- a/tools/testing/selftests/bpf/sockmap_parse_prog.c +++ b/tools/testing/selftests/bpf/sockmap_parse_prog.c @@ -20,14 +20,25 @@ int bpf_prog1(struct __sk_buff *skb) __u32 lport = skb->local_port; __u32 rport = skb->remote_port; __u8 *d = data; + __u32 len = (__u32) data_end - (__u32) data; + int err; - if (data + 10 > data_end) - return skb->len; + if (data + 10 > data_end) { + err = bpf_skb_pull_data(skb, 10); + if (err) + return SK_DROP; + + data_end = (void *)(long)skb->data_end; + data = (void *)(long)skb->data; + if (data + 10 > data_end) + return SK_DROP; + } /* This write/read is a bit pointless but tests the verifier and * strparser handler for read/write pkt data and access into sk * fields. */ + d = data; d[7] = 1; return skb->len; } diff --git a/tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c b/tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c new file mode 100644 index 000000000000..12a7b5c82ed6 --- /dev/null +++ b/tools/testing/selftests/bpf/sockmap_tcp_msg_prog.c @@ -0,0 +1,33 @@ +#include <linux/bpf.h> +#include "bpf_helpers.h" +#include "bpf_util.h" +#include "bpf_endian.h" + +int _version SEC("version") = 1; + +#define bpf_printk(fmt, ...) \ +({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ +}) + +SEC("sk_msg1") +int bpf_prog1(struct sk_msg_md *msg) +{ + void *data_end = (void *)(long) msg->data_end; + void *data = (void *)(long) msg->data; + + char *d; + + if (data + 8 > data_end) + return SK_DROP; + + bpf_printk("data length %i\n", (__u64)msg->data_end - (__u64)msg->data); + d = (char *)data; + bpf_printk("hello sendmsg hook %i %i\n", d[0], d[1]); + + return SK_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/sockmap_verdict_prog.c index d7bea972cb21..2ce7634a4012 100644 --- a/tools/testing/selftests/bpf/sockmap_verdict_prog.c +++ b/tools/testing/selftests/bpf/sockmap_verdict_prog.c @@ -26,6 +26,13 @@ struct bpf_map_def SEC("maps") sock_map_tx = { .max_entries = 20, }; +struct bpf_map_def SEC("maps") sock_map_msg = { + .type = BPF_MAP_TYPE_SOCKMAP, + .key_size = sizeof(int), + .value_size = sizeof(int), + .max_entries = 20, +}; + struct bpf_map_def SEC("maps") sock_map_break = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(int), diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c index ff8bd7e3e50c..6b1b302310fe 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -9,8 +9,6 @@ #include <stddef.h> #include <stdbool.h> -#include <sys/resource.h> - #include <linux/unistd.h> #include <linux/filter.h> #include <linux/bpf_perf_event.h> @@ -19,6 +17,7 @@ #include <bpf/bpf.h> #include "../../../include/linux/filter.h" +#include "bpf_rlimit.h" #ifndef ARRAY_SIZE # define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -702,9 +701,6 @@ static int do_test(unsigned int from, unsigned int to) int main(int argc, char **argv) { unsigned int from = 0, to = ARRAY_SIZE(tests); - struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; - - setrlimit(RLIMIT_MEMLOCK, &rinf); if (argc == 3) { unsigned int l = atoi(argv[argc - 2]); diff --git a/tools/testing/selftests/bpf/test_dev_cgroup.c b/tools/testing/selftests/bpf/test_dev_cgroup.c index 3489cc283433..9c8b50bac7e0 100644 --- a/tools/testing/selftests/bpf/test_dev_cgroup.c +++ b/tools/testing/selftests/bpf/test_dev_cgroup.c @@ -11,13 +11,13 @@ #include <errno.h> #include <assert.h> #include <sys/time.h> -#include <sys/resource.h> #include <linux/bpf.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> #include "cgroup_helpers.h" +#include "bpf_rlimit.h" #define DEV_CGROUP_PROG "./dev_cgroup.o" @@ -25,15 +25,11 @@ int main(int argc, char **argv) { - struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; struct bpf_object *obj; int error = EXIT_FAILURE; int prog_fd, cgroup_fd; __u32 prog_cnt; - if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0) - perror("Unable to lift memlock rlimit"); - if (bpf_prog_load(DEV_CGROUP_PROG, BPF_PROG_TYPE_CGROUP_DEVICE, &obj, &prog_fd)) { printf("Failed to load DEV_CGROUP program\n"); diff --git a/tools/testing/selftests/bpf/test_lpm_map.c b/tools/testing/selftests/bpf/test_lpm_map.c index 2be87e9ee28d..147e34cfceb7 100644 --- a/tools/testing/selftests/bpf/test_lpm_map.c +++ b/tools/testing/selftests/bpf/test_lpm_map.c @@ -22,10 +22,11 @@ #include <unistd.h> #include <arpa/inet.h> #include <sys/time.h> -#include <sys/resource.h> #include <bpf/bpf.h> + #include "bpf_util.h" +#include "bpf_rlimit.h" struct tlpm_node { struct tlpm_node *next; @@ -736,17 +737,11 @@ static void test_lpm_multi_thread(void) int main(void) { - struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; - int i, ret; + int i; /* we want predictable, pseudo random tests */ srand(0xf00ba1); - /* allow unlimited locked memory */ - ret = setrlimit(RLIMIT_MEMLOCK, &limit); - if (ret < 0) - perror("Unable to lift memlock rlimit"); - test_lpm_basic(); test_lpm_order(); @@ -755,11 +750,8 @@ int main(void) test_lpm_map(i); test_lpm_ipaddr(); - test_lpm_delete(); - test_lpm_get_next_key(); - test_lpm_multi_thread(); printf("test_lpm: OK\n"); diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index 8c10c9180c1a..781c7de343be 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -16,10 +16,11 @@ #include <time.h> #include <sys/wait.h> -#include <sys/resource.h> #include <bpf/bpf.h> + #include "bpf_util.h" +#include "bpf_rlimit.h" #define LOCAL_FREE_TARGET (128) #define PERCPU_FREE_TARGET (4) @@ -613,7 +614,6 @@ static void test_lru_sanity6(int map_type, int map_flags, int tgt_free) int main(int argc, char **argv) { - struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; int map_types[] = {BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH}; int map_flags[] = {0, BPF_F_NO_COMMON_LRU}; @@ -621,8 +621,6 @@ int main(int argc, char **argv) setbuf(stdout, NULL); - assert(!setrlimit(RLIMIT_MEMLOCK, &r)); - nr_cpus = bpf_num_possible_cpus(); assert(nr_cpus != -1); printf("nr_cpus:%d\n\n", nr_cpus); diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 9e03a4c356a4..6c253343a6f9 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -17,13 +17,14 @@ #include <stdlib.h> #include <sys/wait.h> -#include <sys/resource.h> #include <linux/bpf.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> + #include "bpf_util.h" +#include "bpf_rlimit.h" static int map_flags; @@ -463,15 +464,17 @@ static void test_devmap(int task, void *data) #include <linux/err.h> #define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o" #define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o" +#define SOCKMAP_TCP_MSG_PROG "./sockmap_tcp_msg_prog.o" static void test_sockmap(int tasks, void *data) { - int one = 1, map_fd_rx = 0, map_fd_tx = 0, map_fd_break, s, sc, rc; - struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_break; + struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_msg, *bpf_map_break; + int map_fd_msg = 0, map_fd_rx = 0, map_fd_tx = 0, map_fd_break; int ports[] = {50200, 50201, 50202, 50204}; int err, i, fd, udp, sfd[6] = {0xdeadbeef}; u8 buf[20] = {0x0, 0x5, 0x3, 0x2, 0x1, 0x0}; - int parse_prog, verdict_prog; + int parse_prog, verdict_prog, msg_prog; struct sockaddr_in addr; + int one = 1, s, sc, rc; struct bpf_object *obj; struct timeval to; __u32 key, value; @@ -583,6 +586,12 @@ static void test_sockmap(int tasks, void *data) goto out_sockmap; } + err = bpf_prog_attach(-1, fd, BPF_SK_MSG_VERDICT, 0); + if (!err) { + printf("Failed invalid msg verdict prog attach\n"); + goto out_sockmap; + } + err = bpf_prog_attach(-1, fd, __MAX_BPF_ATTACH_TYPE, 0); if (!err) { printf("Failed unknown prog attach\n"); @@ -601,6 +610,12 @@ static void test_sockmap(int tasks, void *data) goto out_sockmap; } + err = bpf_prog_detach(fd, BPF_SK_MSG_VERDICT); + if (err) { + printf("Failed empty msg verdict prog detach\n"); + goto out_sockmap; + } + err = bpf_prog_detach(fd, __MAX_BPF_ATTACH_TYPE); if (!err) { printf("Detach invalid prog successful\n"); @@ -615,6 +630,13 @@ static void test_sockmap(int tasks, void *data) goto out_sockmap; } + err = bpf_prog_load(SOCKMAP_TCP_MSG_PROG, + BPF_PROG_TYPE_SK_MSG, &obj, &msg_prog); + if (err) { + printf("Failed to load SK_SKB msg prog\n"); + goto out_sockmap; + } + err = bpf_prog_load(SOCKMAP_VERDICT_PROG, BPF_PROG_TYPE_SK_SKB, &obj, &verdict_prog); if (err) { @@ -630,7 +652,7 @@ static void test_sockmap(int tasks, void *data) map_fd_rx = bpf_map__fd(bpf_map_rx); if (map_fd_rx < 0) { - printf("Failed to get map fd\n"); + printf("Failed to get map rx fd\n"); goto out_sockmap; } @@ -646,6 +668,18 @@ static void test_sockmap(int tasks, void *data) goto out_sockmap; } + bpf_map_msg = bpf_object__find_map_by_name(obj, "sock_map_msg"); + if (IS_ERR(bpf_map_msg)) { + printf("Failed to load map msg from msg_verdict prog\n"); + goto out_sockmap; + } + + map_fd_msg = bpf_map__fd(bpf_map_msg); + if (map_fd_msg < 0) { + printf("Failed to get map msg fd\n"); + goto out_sockmap; + } + bpf_map_break = bpf_object__find_map_by_name(obj, "sock_map_break"); if (IS_ERR(bpf_map_break)) { printf("Failed to load map tx from verdict prog\n"); @@ -679,6 +713,12 @@ static void test_sockmap(int tasks, void *data) goto out_sockmap; } + err = bpf_prog_attach(msg_prog, map_fd_msg, BPF_SK_MSG_VERDICT, 0); + if (err) { + printf("Failed msg verdict bpf prog attach\n"); + goto out_sockmap; + } + err = bpf_prog_attach(verdict_prog, map_fd_rx, __MAX_BPF_ATTACH_TYPE, 0); if (!err) { @@ -718,6 +758,14 @@ static void test_sockmap(int tasks, void *data) } } + /* Put sfd[2] (sending fd below) into msg map to test sendmsg bpf */ + i = 0; + err = bpf_map_update_elem(map_fd_msg, &i, &sfd[2], BPF_ANY); + if (err) { + printf("Failed map_fd_msg update sockmap %i\n", err); + goto out_sockmap; + } + /* Test map send/recv */ for (i = 0; i < 2; i++) { buf[0] = i; @@ -1126,10 +1174,6 @@ static void run_all_tests(void) int main(void) { - struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; - - setrlimit(RLIMIT_MEMLOCK, &rinf); - map_flags = 0; run_all_tests(); diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index b549308abd19..e9df48b306df 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -26,7 +26,6 @@ typedef __u16 __sum16; #include <sys/ioctl.h> #include <sys/wait.h> -#include <sys/resource.h> #include <sys/types.h> #include <fcntl.h> @@ -34,9 +33,11 @@ typedef __u16 __sum16; #include <linux/err.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> + #include "test_iptunnel_common.h" #include "bpf_util.h" #include "bpf_endian.h" +#include "bpf_rlimit.h" static int error_cnt, pass_cnt; @@ -840,7 +841,8 @@ static void test_tp_attach_query(void) static int compare_map_keys(int map1_fd, int map2_fd) { __u32 key, next_key; - char val_buf[PERF_MAX_STACK_DEPTH * sizeof(__u64)]; + char val_buf[PERF_MAX_STACK_DEPTH * + sizeof(struct bpf_stack_build_id)]; int err; err = bpf_map_get_next_key(map1_fd, NULL, &key); @@ -963,12 +965,168 @@ out: return; } -int main(void) +static int extract_build_id(char *build_id, size_t size) +{ + FILE *fp; + char *line = NULL; + size_t len = 0; + + fp = popen("readelf -n ./urandom_read | grep 'Build ID'", "r"); + if (fp == NULL) + return -1; + + if (getline(&line, &len, fp) == -1) + goto err; + fclose(fp); + + if (len > size) + len = size; + memcpy(build_id, line, len); + build_id[len] = '\0'; + return 0; +err: + fclose(fp); + return -1; +} + +static void test_stacktrace_build_id(void) { - struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + int control_map_fd, stackid_hmap_fd, stackmap_fd; + const char *file = "./test_stacktrace_build_id.o"; + int bytes, efd, err, pmu_fd, prog_fd; + struct perf_event_attr attr = {}; + __u32 key, previous_key, val, duration = 0; + struct bpf_object *obj; + char buf[256]; + int i, j; + struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH]; + int build_id_matches = 0; - setrlimit(RLIMIT_MEMLOCK, &rinf); + err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); + if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno)) + goto out; + + /* Get the ID for the sched/sched_switch tracepoint */ + snprintf(buf, sizeof(buf), + "/sys/kernel/debug/tracing/events/random/urandom_read/id"); + efd = open(buf, O_RDONLY, 0); + if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) + goto close_prog; + bytes = read(efd, buf, sizeof(buf)); + close(efd); + if (CHECK(bytes <= 0 || bytes >= sizeof(buf), + "read", "bytes %d errno %d\n", bytes, errno)) + goto close_prog; + + /* Open the perf event and attach bpf progrram */ + attr.config = strtol(buf, NULL, 0); + attr.type = PERF_TYPE_TRACEPOINT; + attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN; + attr.sample_period = 1; + attr.wakeup_events = 1; + pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, + 0 /* cpu 0 */, -1 /* group id */, + 0 /* flags */); + if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", + pmu_fd, errno)) + goto close_prog; + + err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); + if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", + err, errno)) + goto close_pmu; + + err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); + if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", + err, errno)) + goto disable_pmu; + + /* find map fds */ + control_map_fd = bpf_find_map(__func__, obj, "control_map"); + if (CHECK(control_map_fd < 0, "bpf_find_map control_map", + "err %d errno %d\n", err, errno)) + goto disable_pmu; + + stackid_hmap_fd = bpf_find_map(__func__, obj, "stackid_hmap"); + if (CHECK(stackid_hmap_fd < 0, "bpf_find_map stackid_hmap", + "err %d errno %d\n", err, errno)) + goto disable_pmu; + + stackmap_fd = bpf_find_map(__func__, obj, "stackmap"); + if (CHECK(stackmap_fd < 0, "bpf_find_map stackmap", "err %d errno %d\n", + err, errno)) + goto disable_pmu; + + assert(system("dd if=/dev/urandom of=/dev/zero count=4 2> /dev/null") + == 0); + assert(system("./urandom_read if=/dev/urandom of=/dev/zero count=4 2> /dev/null") == 0); + /* disable stack trace collection */ + key = 0; + val = 1; + bpf_map_update_elem(control_map_fd, &key, &val, 0); + + /* for every element in stackid_hmap, we can find a corresponding one + * in stackmap, and vise versa. + */ + err = compare_map_keys(stackid_hmap_fd, stackmap_fd); + if (CHECK(err, "compare_map_keys stackid_hmap vs. stackmap", + "err %d errno %d\n", err, errno)) + goto disable_pmu; + + err = compare_map_keys(stackmap_fd, stackid_hmap_fd); + if (CHECK(err, "compare_map_keys stackmap vs. stackid_hmap", + "err %d errno %d\n", err, errno)) + goto disable_pmu; + + err = extract_build_id(buf, 256); + + if (CHECK(err, "get build_id with readelf", + "err %d errno %d\n", err, errno)) + goto disable_pmu; + + err = bpf_map_get_next_key(stackmap_fd, NULL, &key); + if (CHECK(err, "get_next_key from stackmap", + "err %d, errno %d\n", err, errno)) + goto disable_pmu; + + do { + char build_id[64]; + + err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs); + if (CHECK(err, "lookup_elem from stackmap", + "err %d, errno %d\n", err, errno)) + goto disable_pmu; + for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i) + if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID && + id_offs[i].offset != 0) { + for (j = 0; j < 20; ++j) + sprintf(build_id + 2 * j, "%02x", + id_offs[i].build_id[j] & 0xff); + if (strstr(buf, build_id) != NULL) + build_id_matches = 1; + } + previous_key = key; + } while (bpf_map_get_next_key(stackmap_fd, &previous_key, &key) == 0); + + CHECK(build_id_matches < 1, "build id match", + "Didn't find expected build ID from the map"); + +disable_pmu: + ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); + +close_pmu: + close(pmu_fd); + +close_prog: + bpf_object__close(obj); + +out: + return; +} + +int main(void) +{ test_pkt_access(); test_xdp(); test_l4lb_all(); @@ -979,6 +1137,7 @@ int main(void) test_obj_name(); test_tp_attach_query(); test_stacktrace_map(); + test_stacktrace_build_id(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/tools/testing/selftests/bpf/test_stacktrace_build_id.c b/tools/testing/selftests/bpf/test_stacktrace_build_id.c new file mode 100644 index 000000000000..b755bd783ce5 --- /dev/null +++ b/tools/testing/selftests/bpf/test_stacktrace_build_id.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/bpf.h> +#include "bpf_helpers.h" + +#ifndef PERF_MAX_STACK_DEPTH +#define PERF_MAX_STACK_DEPTH 127 +#endif + +struct bpf_map_def SEC("maps") control_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = 1, +}; + +struct bpf_map_def SEC("maps") stackid_hmap = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = 10000, +}; + +struct bpf_map_def SEC("maps") stackmap = { + .type = BPF_MAP_TYPE_STACK_TRACE, + .key_size = sizeof(__u32), + .value_size = sizeof(struct bpf_stack_build_id) + * PERF_MAX_STACK_DEPTH, + .max_entries = 128, + .map_flags = BPF_F_STACK_BUILD_ID, +}; + +/* taken from /sys/kernel/debug/tracing/events/random/urandom_read/format */ +struct random_urandom_args { + unsigned long long pad; + int got_bits; + int pool_left; + int input_left; +}; + +SEC("tracepoint/random/urandom_read") +int oncpu(struct random_urandom_args *args) +{ + __u32 key = 0, val = 0, *value_p; + + value_p = bpf_map_lookup_elem(&control_map, &key); + if (value_p && *value_p) + return 0; /* skip if non-zero *value_p */ + + /* The size of stackmap and stackid_hmap should be the same */ + key = bpf_get_stackid(args, &stackmap, BPF_F_USER_STACK); + if ((int)key >= 0) + bpf_map_update_elem(&stackid_hmap, &key, &val, 0); + + return 0; +} + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */ diff --git a/tools/testing/selftests/bpf/test_tag.c b/tools/testing/selftests/bpf/test_tag.c index 8b201895c569..6272c784ca2a 100644 --- a/tools/testing/selftests/bpf/test_tag.c +++ b/tools/testing/selftests/bpf/test_tag.c @@ -12,7 +12,6 @@ #include <assert.h> #include <sys/socket.h> -#include <sys/resource.h> #include <linux/filter.h> #include <linux/bpf.h> @@ -21,6 +20,7 @@ #include <bpf/bpf.h> #include "../../../include/linux/filter.h" +#include "bpf_rlimit.h" static struct bpf_insn prog[BPF_MAXINSNS]; @@ -184,11 +184,9 @@ static void do_test(uint32_t *tests, int start_insns, int fd_map, int main(void) { - struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; uint32_t tests = 0; int i, fd_map; - setrlimit(RLIMIT_MEMLOCK, &rinf); fd_map = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(int), 1, BPF_F_NO_PREALLOC); assert(fd_map > 0); diff --git a/tools/testing/selftests/bpf/test_tcpbpf_user.c b/tools/testing/selftests/bpf/test_tcpbpf_user.c index 5d73db416460..84ab5163c828 100644 --- a/tools/testing/selftests/bpf/test_tcpbpf_user.c +++ b/tools/testing/selftests/bpf/test_tcpbpf_user.c @@ -12,13 +12,13 @@ #include <linux/bpf.h> #include <sys/ioctl.h> #include <sys/time.h> -#include <sys/resource.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> #include "bpf_util.h" +#include "bpf_rlimit.h" #include <linux/perf_event.h> #include "test_tcpbpf.h" @@ -44,7 +44,6 @@ static int bpf_find_map(const char *test, struct bpf_object *obj, int main(int argc, char **argv) { - struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; const char *file = "test_tcpbpf_kern.o"; struct tcpbpf_globals g = {0}; int cg_fd, prog_fd, map_fd; @@ -57,9 +56,6 @@ int main(int argc, char **argv) int pid; int rv; - if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0) - perror("Unable to lift memlock rlimit"); - if (argc > 1 && strcmp(argv[1], "-d") == 0) debug_flag = true; diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 2164d218322e..3e7718b1a9ae 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -24,7 +24,6 @@ #include <limits.h> #include <sys/capability.h> -#include <sys/resource.h> #include <linux/unistd.h> #include <linux/filter.h> @@ -41,7 +40,7 @@ # define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 # endif #endif - +#include "bpf_rlimit.h" #include "../../../include/linux/filter.h" #ifndef ARRAY_SIZE @@ -1598,6 +1597,60 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SK_SKB, }, { + "direct packet read for SK_MSG", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, + offsetof(struct sk_msg_md, data)), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, + offsetof(struct sk_msg_md, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SK_MSG, + }, + { + "direct packet write for SK_MSG", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, + offsetof(struct sk_msg_md, data)), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, + offsetof(struct sk_msg_md, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SK_MSG, + }, + { + "overlapping checks for direct packet access SK_MSG", + .insns = { + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, + offsetof(struct sk_msg_md, data)), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, + offsetof(struct sk_msg_md, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 6), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_2, 6), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SK_MSG, + }, + { "check skb->mark is not writeable by sockets", .insns = { BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, @@ -2590,17 +2643,74 @@ static struct bpf_test tests[] = { .result = ACCEPT, }, { + "runtime/jit: tail_call within bounds, prog once", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog = { 1 }, + .result = ACCEPT, + .retval = 42, + }, + { + "runtime/jit: tail_call within bounds, prog loop", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 1), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog = { 1 }, + .result = ACCEPT, + .retval = 41, + }, + { + "runtime/jit: tail_call within bounds, no prog", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog = { 1 }, + .result = ACCEPT, + .retval = 1, + }, + { + "runtime/jit: tail_call out of bounds", + .insns = { + BPF_MOV64_IMM(BPF_REG_3, 256), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, + .fixup_prog = { 1 }, + .result = ACCEPT, + .retval = 2, + }, + { "runtime/jit: pass negative index to tail_call", .insns = { BPF_MOV64_IMM(BPF_REG_3, -1), BPF_LD_MAP_FD(BPF_REG_2, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), - BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 2), BPF_EXIT_INSN(), }, .fixup_prog = { 1 }, .result = ACCEPT, + .retval = 2, }, { "runtime/jit: pass > 32bit index to tail_call", @@ -2609,11 +2719,12 @@ static struct bpf_test tests[] = { BPF_LD_MAP_FD(BPF_REG_2, 0), BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), - BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 2), BPF_EXIT_INSN(), }, .fixup_prog = { 2 }, .result = ACCEPT, + .retval = 42, }, { "stack pointer arithmetic", @@ -11254,7 +11365,64 @@ static struct bpf_test tests[] = { .result = ACCEPT, .retval = 2, }, - + { + "xadd/w check unaligned stack", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_STX_XADD(BPF_W, BPF_REG_10, BPF_REG_0, -7), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "misaligned stack access off", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "xadd/w check unaligned map", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_STX_XADD(BPF_W, BPF_REG_0, BPF_REG_1, 3), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 3), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = REJECT, + .errstr = "misaligned value access off", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "xadd/w check unaligned pkt", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct xdp_md, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct xdp_md, data_end)), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), + BPF_JMP_REG(BPF_JLT, BPF_REG_1, BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_0, 99), + BPF_JMP_IMM(BPF_JA, 0, 0, 6), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0), + BPF_ST_MEM(BPF_W, BPF_REG_2, 3, 0), + BPF_STX_XADD(BPF_W, BPF_REG_2, BPF_REG_0, 1), + BPF_STX_XADD(BPF_W, BPF_REG_2, BPF_REG_0, 2), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 1), + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "BPF_XADD stores into R2 packet", + .prog_type = BPF_PROG_TYPE_XDP, + }, }; static int probe_filter_length(const struct bpf_insn *fp) @@ -11279,16 +11447,61 @@ static int create_map(uint32_t size_value, uint32_t max_elem) return fd; } +static int create_prog_dummy1(void) +{ + struct bpf_insn prog[] = { + BPF_MOV64_IMM(BPF_REG_0, 42), + BPF_EXIT_INSN(), + }; + + return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, + ARRAY_SIZE(prog), "GPL", 0, NULL, 0); +} + +static int create_prog_dummy2(int mfd, int idx) +{ + struct bpf_insn prog[] = { + BPF_MOV64_IMM(BPF_REG_3, idx), + BPF_LD_MAP_FD(BPF_REG_2, mfd), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 41), + BPF_EXIT_INSN(), + }; + + return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, + ARRAY_SIZE(prog), "GPL", 0, NULL, 0); +} + static int create_prog_array(void) { - int fd; + int p1key = 0, p2key = 1; + int mfd, p1fd, p2fd; - fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), - sizeof(int), 4, 0); - if (fd < 0) + mfd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), + sizeof(int), 4, 0); + if (mfd < 0) { printf("Failed to create prog array '%s'!\n", strerror(errno)); + return -1; + } - return fd; + p1fd = create_prog_dummy1(); + p2fd = create_prog_dummy2(mfd, p2key); + if (p1fd < 0 || p2fd < 0) + goto out; + if (bpf_map_update_elem(mfd, &p1key, &p1fd, BPF_ANY) < 0) + goto out; + if (bpf_map_update_elem(mfd, &p2key, &p2fd, BPF_ANY) < 0) + goto out; + close(p2fd); + close(p1fd); + + return mfd; +out: + close(p2fd); + close(p1fd); + close(mfd); + return -1; } static int create_map_in_map(void) @@ -11543,8 +11756,6 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to) int main(int argc, char **argv) { - struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; - struct rlimit rlim = { 1 << 20, 1 << 20 }; unsigned int from = 0, to = ARRAY_SIZE(tests); bool unpriv = !is_admin(); @@ -11572,6 +11783,5 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - setrlimit(RLIMIT_MEMLOCK, unpriv ? &rlim : &rinf); return do_test(unpriv, from, to); } diff --git a/tools/testing/selftests/bpf/test_verifier_log.c b/tools/testing/selftests/bpf/test_verifier_log.c index e9626cf5607a..8d6918c3b4a2 100644 --- a/tools/testing/selftests/bpf/test_verifier_log.c +++ b/tools/testing/selftests/bpf/test_verifier_log.c @@ -4,7 +4,6 @@ #include <string.h> #include <unistd.h> #include <sys/time.h> -#include <sys/resource.h> #include <linux/bpf.h> #include <linux/filter.h> @@ -12,6 +11,8 @@ #include <bpf/bpf.h> +#include "bpf_rlimit.h" + #define LOG_SIZE (1 << 20) #define err(str...) printf("ERROR: " str) @@ -133,16 +134,11 @@ static void test_log_bad(char *log, size_t log_len, int log_level) int main(int argc, char **argv) { - struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; char full_log[LOG_SIZE]; char log[LOG_SIZE]; size_t want_len; int i; - /* allow unlimited locked memory to have more consistent error code */ - if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0) - perror("Unable to lift memlock rlimit"); - memset(log, 1, LOG_SIZE); /* Test incorrect attr */ diff --git a/tools/testing/selftests/bpf/urandom_read.c b/tools/testing/selftests/bpf/urandom_read.c new file mode 100644 index 000000000000..4acfdebf36fa --- /dev/null +++ b/tools/testing/selftests/bpf/urandom_read.c @@ -0,0 +1,22 @@ +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +#define BUF_SIZE 256 +int main(void) +{ + int fd = open("/dev/urandom", O_RDONLY); + int i; + char buf[BUF_SIZE]; + + if (fd < 0) + return 1; + for (i = 0; i < 4; ++i) + read(fd, buf, BUF_SIZE); + + close(fd); + return 0; +} diff --git a/tools/testing/selftests/futex/Makefile b/tools/testing/selftests/futex/Makefile index cea4adcd42b8..a63e8453984d 100644 --- a/tools/testing/selftests/futex/Makefile +++ b/tools/testing/selftests/futex/Makefile @@ -12,9 +12,9 @@ all: BUILD_TARGET=$(OUTPUT)/$$DIR; \ mkdir $$BUILD_TARGET -p; \ make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ - if [ -e $$DIR/$(TEST_PROGS) ]; then - rsync -a $$DIR/$(TEST_PROGS) $$BUILD_TARGET/; - fi + if [ -e $$DIR/$(TEST_PROGS) ]; then \ + rsync -a $$DIR/$(TEST_PROGS) $$BUILD_TARGET/; \ + fi \ done override define RUN_TESTS diff --git a/tools/testing/selftests/memfd/config b/tools/testing/selftests/memfd/config new file mode 100644 index 000000000000..835c7f4dadcd --- /dev/null +++ b/tools/testing/selftests/memfd/config @@ -0,0 +1 @@ +CONFIG_FUSE_FS=m diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 86636d207adf..686da510f989 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile @@ -4,8 +4,9 @@ all: include ../lib.mk TEST_PROGS := mem-on-off-test.sh -override RUN_TESTS := ./mem-on-off-test.sh -r 2 && echo "selftests: memory-hotplug [PASS]" || echo "selftests: memory-hotplug [FAIL]" -override EMIT_TESTS := echo "$(RUN_TESTS)" +override RUN_TESTS := @./mem-on-off-test.sh -r 2 && echo "selftests: memory-hotplug [PASS]" || echo "selftests: memory-hotplug [FAIL]" + +override EMIT_TESTS := echo "$(subst @,,$(RUN_TESTS))" run_full_test: @/bin/bash ./mem-on-off-test.sh && echo "memory-hotplug selftests: [PASS]" || echo "memory-hotplug selftests: [FAIL]" diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 229a038966e3..785fc18a16b4 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -5,7 +5,7 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g CFLAGS += -I../../../../usr/include/ TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh -TEST_PROGS += fib_tests.sh fib-onlink-tests.sh +TEST_PROGS += fib_tests.sh fib-onlink-tests.sh pmtu.sh TEST_GEN_FILES = socket TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config index 7177bea1fdfa..6a75a3ea44ad 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -2,3 +2,8 @@ CONFIG_USER_NS=y CONFIG_BPF_SYSCALL=y CONFIG_TEST_BPF=m CONFIG_NUMA=y +CONFIG_NET_VRF=y +CONFIG_NET_L3_MASTER_DEV=y +CONFIG_IPV6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_VETH=y diff --git a/tools/testing/selftests/net/fib-onlink-tests.sh b/tools/testing/selftests/net/fib-onlink-tests.sh index 06b1d7cc12cc..3991ad1a368d 100755 --- a/tools/testing/selftests/net/fib-onlink-tests.sh +++ b/tools/testing/selftests/net/fib-onlink-tests.sh @@ -56,7 +56,8 @@ TEST_NET6[2]=2001:db8:102 # connected gateway CONGW[1]=169.254.1.254 -CONGW[2]=169.254.5.254 +CONGW[2]=169.254.3.254 +CONGW[3]=169.254.5.254 # recursive gateway RECGW4[1]=169.254.11.254 @@ -232,6 +233,23 @@ run_ip() log_test $? ${exp_rc} "${desc}" } +run_ip_mpath() +{ + local table="$1" + local prefix="$2" + local nh1="$3" + local nh2="$4" + local exp_rc="$5" + local desc="$6" + + # dev arg may be empty + [ -n "${dev}" ] && dev="dev ${dev}" + + run_cmd ip ro add table "${table}" "${prefix}"/32 \ + nexthop via ${nh1} nexthop via ${nh2} + log_test $? ${exp_rc} "${desc}" +} + valid_onlink_ipv4() { # - unicast connected, unicast recursive @@ -243,13 +261,37 @@ valid_onlink_ipv4() log_subsection "VRF ${VRF}" - run_ip ${VRF_TABLE} ${TEST_NET4[2]}.1 ${CONGW[2]} ${NETIFS[p5]} 0 "unicast connected" + run_ip ${VRF_TABLE} ${TEST_NET4[2]}.1 ${CONGW[3]} ${NETIFS[p5]} 0 "unicast connected" run_ip ${VRF_TABLE} ${TEST_NET4[2]}.2 ${RECGW4[2]} ${NETIFS[p5]} 0 "unicast recursive" log_subsection "VRF device, PBR table" - run_ip ${PBR_TABLE} ${TEST_NET4[2]}.3 ${CONGW[2]} ${NETIFS[p5]} 0 "unicast connected" + run_ip ${PBR_TABLE} ${TEST_NET4[2]}.3 ${CONGW[3]} ${NETIFS[p5]} 0 "unicast connected" run_ip ${PBR_TABLE} ${TEST_NET4[2]}.4 ${RECGW4[2]} ${NETIFS[p5]} 0 "unicast recursive" + + # multipath version + # + log_subsection "default VRF - main table - multipath" + + run_ip_mpath 254 ${TEST_NET4[1]}.5 \ + "${CONGW[1]} dev ${NETIFS[p1]} onlink" \ + "${CONGW[2]} dev ${NETIFS[p3]} onlink" \ + 0 "unicast connected - multipath" + + run_ip_mpath 254 ${TEST_NET4[1]}.6 \ + "${RECGW4[1]} dev ${NETIFS[p1]} onlink" \ + "${RECGW4[2]} dev ${NETIFS[p3]} onlink" \ + 0 "unicast recursive - multipath" + + run_ip_mpath 254 ${TEST_NET4[1]}.7 \ + "${CONGW[1]} dev ${NETIFS[p1]}" \ + "${CONGW[2]} dev ${NETIFS[p3]} onlink" \ + 0 "unicast connected - multipath onlink first only" + + run_ip_mpath 254 ${TEST_NET4[1]}.8 \ + "${CONGW[1]} dev ${NETIFS[p1]} onlink" \ + "${CONGW[2]} dev ${NETIFS[p3]}" \ + 0 "unicast connected - multipath onlink second only" } invalid_onlink_ipv4() @@ -289,6 +331,21 @@ run_ip6() log_test $? ${exp_rc} "${desc}" } +run_ip6_mpath() +{ + local table="$1" + local prefix="$2" + local opts="$3" + local nh1="$4" + local nh2="$5" + local exp_rc="$6" + local desc="$7" + + run_cmd ip -6 ro add table "${table}" "${prefix}"/128 "${opts}" \ + nexthop via ${nh1} nexthop via ${nh2} + log_test $? ${exp_rc} "${desc}" +} + valid_onlink_ipv6() { # - unicast connected, unicast recursive, v4-mapped @@ -310,6 +367,40 @@ valid_onlink_ipv6() run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::4 ${V6ADDRS[p5]/::*}::64 ${NETIFS[p5]} 0 "unicast connected" run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::5 ${RECGW6[2]} ${NETIFS[p5]} 0 "unicast recursive" run_ip6 ${PBR_TABLE} ${TEST_NET6[2]}::6 ::ffff:${TEST_NET4IN6[2]} ${NETIFS[p5]} 0 "v4-mapped" + + # multipath version + # + log_subsection "default VRF - main table - multipath" + + run_ip6_mpath 254 ${TEST_NET6[1]}::4 "onlink" \ + "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]}" \ + "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]}" \ + 0 "unicast connected - multipath onlink" + + run_ip6_mpath 254 ${TEST_NET6[1]}::5 "onlink" \ + "${RECGW6[1]} dev ${NETIFS[p1]}" \ + "${RECGW6[2]} dev ${NETIFS[p3]}" \ + 0 "unicast recursive - multipath onlink" + + run_ip6_mpath 254 ${TEST_NET6[1]}::6 "onlink" \ + "::ffff:${TEST_NET4IN6[1]} dev ${NETIFS[p1]}" \ + "::ffff:${TEST_NET4IN6[2]} dev ${NETIFS[p3]}" \ + 0 "v4-mapped - multipath onlink" + + run_ip6_mpath 254 ${TEST_NET6[1]}::7 "" \ + "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]} onlink" \ + "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]} onlink" \ + 0 "unicast connected - multipath onlink both nexthops" + + run_ip6_mpath 254 ${TEST_NET6[1]}::8 "" \ + "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]} onlink" \ + "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]}" \ + 0 "unicast connected - multipath onlink first only" + + run_ip6_mpath 254 ${TEST_NET6[1]}::9 "" \ + "${V6ADDRS[p1]/::*}::64 dev ${NETIFS[p1]}" \ + "${V6ADDRS[p3]/::*}::64 dev ${NETIFS[p3]} onlink" \ + 0 "unicast connected - multipath onlink second only" } invalid_onlink_ipv6() @@ -355,6 +446,7 @@ run_onlink_tests() log_section "IPv6 onlink" log_subsection "Valid onlink commands" valid_onlink_ipv6 + log_subsection "Invalid onlink commands" invalid_onlink_ipv6 } diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh index b617985ecdc1..9164e60d4b66 100755 --- a/tools/testing/selftests/net/fib_tests.sh +++ b/tools/testing/selftests/net/fib_tests.sh @@ -6,7 +6,9 @@ ret=0 +VERBOSE=${VERBOSE:=0} PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} +IP="ip -netns testns" log_test() { @@ -15,10 +17,10 @@ log_test() local msg="$3" if [ ${rc} -eq ${expected} ]; then - printf " %-60s [ OK ]\n" "${msg}" + printf " TEST: %-60s [ OK ]\n" "${msg}" else ret=1 - printf " %-60s [FAIL]\n" "${msg}" + printf " TEST: %-60s [FAIL]\n" "${msg}" if [ "${PAUSE_ON_FAIL}" = "yes" ]; then echo echo "hit enter to continue, 'q' to quit" @@ -32,22 +34,44 @@ setup() { set -e ip netns add testns - ip -netns testns link set dev lo up + $IP link set dev lo up - ip -netns testns link add dummy0 type dummy - ip -netns testns link set dev dummy0 up - ip -netns testns address add 198.51.100.1/24 dev dummy0 - ip -netns testns -6 address add 2001:db8:1::1/64 dev dummy0 + $IP link add dummy0 type dummy + $IP link set dev dummy0 up + $IP address add 198.51.100.1/24 dev dummy0 + $IP -6 address add 2001:db8:1::1/64 dev dummy0 set +e } cleanup() { - ip -netns testns link del dev dummy0 &> /dev/null + $IP link del dev dummy0 &> /dev/null ip netns del testns } +get_linklocal() +{ + local dev=$1 + local addr + + addr=$($IP -6 -br addr show dev ${dev} | \ + awk '{ + for (i = 3; i <= NF; ++i) { + if ($i ~ /^fe80/) + print $i + } + }' + ) + addr=${addr/\/*} + + [ -z "$addr" ] && return 1 + + echo $addr + + return 0 +} + fib_unreg_unicast_test() { echo @@ -56,19 +80,19 @@ fib_unreg_unicast_test() setup echo " Start point" - ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null + $IP route get fibmatch 198.51.100.2 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null log_test $? 0 "IPv6 fibmatch" set -e - ip -netns testns link del dev dummy0 + $IP link del dev dummy0 set +e echo " Nexthop device deleted" - ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null + $IP route get fibmatch 198.51.100.2 &> /dev/null log_test $? 2 "IPv4 fibmatch - no route" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null log_test $? 2 "IPv6 fibmatch - no route" cleanup @@ -83,43 +107,43 @@ fib_unreg_multipath_test() setup set -e - ip -netns testns link add dummy1 type dummy - ip -netns testns link set dev dummy1 up - ip -netns testns address add 192.0.2.1/24 dev dummy1 - ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy1 + $IP link add dummy1 type dummy + $IP link set dev dummy1 up + $IP address add 192.0.2.1/24 dev dummy1 + $IP -6 address add 2001:db8:2::1/64 dev dummy1 - ip -netns testns route add 203.0.113.0/24 \ + $IP route add 203.0.113.0/24 \ nexthop via 198.51.100.2 dev dummy0 \ nexthop via 192.0.2.2 dev dummy1 - ip -netns testns -6 route add 2001:db8:3::/64 \ + $IP -6 route add 2001:db8:3::/64 \ nexthop via 2001:db8:1::2 dev dummy0 \ nexthop via 2001:db8:2::2 dev dummy1 set +e echo " Start point" - ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null + $IP route get fibmatch 203.0.113.1 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null log_test $? 0 "IPv6 fibmatch" set -e - ip -netns testns link del dev dummy0 + $IP link del dev dummy0 set +e echo " One nexthop device deleted" - ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null + $IP route get fibmatch 203.0.113.1 &> /dev/null log_test $? 2 "IPv4 - multipath route removed on delete" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null # In IPv6 we do not flush the entire multipath route. log_test $? 0 "IPv6 - multipath down to single path" set -e - ip -netns testns link del dev dummy1 + $IP link del dev dummy1 set +e echo " Second nexthop device deleted" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null log_test $? 2 "IPv6 - no route" cleanup @@ -139,19 +163,19 @@ fib_down_unicast_test() setup echo " Start point" - ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null + $IP route get fibmatch 198.51.100.2 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null log_test $? 0 "IPv6 fibmatch" set -e - ip -netns testns link set dev dummy0 down + $IP link set dev dummy0 down set +e echo " Route deleted on down" - ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null + $IP route get fibmatch 198.51.100.2 &> /dev/null log_test $? 2 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null log_test $? 2 "IPv6 fibmatch" cleanup @@ -162,31 +186,31 @@ fib_down_multipath_test_do() local down_dev=$1 local up_dev=$2 - ip -netns testns route get fibmatch 203.0.113.1 \ + $IP route get fibmatch 203.0.113.1 \ oif $down_dev &> /dev/null log_test $? 2 "IPv4 fibmatch on down device" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 \ + $IP -6 route get fibmatch 2001:db8:3::1 \ oif $down_dev &> /dev/null log_test $? 2 "IPv6 fibmatch on down device" - ip -netns testns route get fibmatch 203.0.113.1 \ + $IP route get fibmatch 203.0.113.1 \ oif $up_dev &> /dev/null log_test $? 0 "IPv4 fibmatch on up device" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 \ + $IP -6 route get fibmatch 2001:db8:3::1 \ oif $up_dev &> /dev/null log_test $? 0 "IPv6 fibmatch on up device" - ip -netns testns route get fibmatch 203.0.113.1 | \ + $IP route get fibmatch 203.0.113.1 | \ grep $down_dev | grep -q "dead linkdown" log_test $? 0 "IPv4 flags on down device" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 | \ + $IP -6 route get fibmatch 2001:db8:3::1 | \ grep $down_dev | grep -q "dead linkdown" log_test $? 0 "IPv6 flags on down device" - ip -netns testns route get fibmatch 203.0.113.1 | \ + $IP route get fibmatch 203.0.113.1 | \ grep $up_dev | grep -q "dead linkdown" log_test $? 1 "IPv4 flags on up device" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 | \ + $IP -6 route get fibmatch 2001:db8:3::1 | \ grep $up_dev | grep -q "dead linkdown" log_test $? 1 "IPv6 flags on up device" } @@ -199,53 +223,53 @@ fib_down_multipath_test() setup set -e - ip -netns testns link add dummy1 type dummy - ip -netns testns link set dev dummy1 up + $IP link add dummy1 type dummy + $IP link set dev dummy1 up - ip -netns testns address add 192.0.2.1/24 dev dummy1 - ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy1 + $IP address add 192.0.2.1/24 dev dummy1 + $IP -6 address add 2001:db8:2::1/64 dev dummy1 - ip -netns testns route add 203.0.113.0/24 \ + $IP route add 203.0.113.0/24 \ nexthop via 198.51.100.2 dev dummy0 \ nexthop via 192.0.2.2 dev dummy1 - ip -netns testns -6 route add 2001:db8:3::/64 \ + $IP -6 route add 2001:db8:3::/64 \ nexthop via 2001:db8:1::2 dev dummy0 \ nexthop via 2001:db8:2::2 dev dummy1 set +e echo " Verify start point" - ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null + $IP route get fibmatch 203.0.113.1 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null log_test $? 0 "IPv6 fibmatch" set -e - ip -netns testns link set dev dummy0 down + $IP link set dev dummy0 down set +e echo " One device down, one up" fib_down_multipath_test_do "dummy0" "dummy1" set -e - ip -netns testns link set dev dummy0 up - ip -netns testns link set dev dummy1 down + $IP link set dev dummy0 up + $IP link set dev dummy1 down set +e echo " Other device down and up" fib_down_multipath_test_do "dummy1" "dummy0" set -e - ip -netns testns link set dev dummy0 down + $IP link set dev dummy0 down set +e echo " Both devices down" - ip -netns testns route get fibmatch 203.0.113.1 &> /dev/null + $IP route get fibmatch 203.0.113.1 &> /dev/null log_test $? 2 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:3::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null log_test $? 2 "IPv6 fibmatch" - ip -netns testns link del dev dummy1 + $IP link del dev dummy1 cleanup } @@ -264,55 +288,55 @@ fib_carrier_local_test() setup set -e - ip -netns testns link set dev dummy0 carrier on + $IP link set dev dummy0 carrier on set +e echo " Start point" - ip -netns testns route get fibmatch 198.51.100.1 &> /dev/null + $IP route get fibmatch 198.51.100.1 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null log_test $? 0 "IPv6 fibmatch" - ip -netns testns route get fibmatch 198.51.100.1 | \ + $IP route get fibmatch 198.51.100.1 | \ grep -q "linkdown" log_test $? 1 "IPv4 - no linkdown flag" - ip -netns testns -6 route get fibmatch 2001:db8:1::1 | \ + $IP -6 route get fibmatch 2001:db8:1::1 | \ grep -q "linkdown" log_test $? 1 "IPv6 - no linkdown flag" set -e - ip -netns testns link set dev dummy0 carrier off + $IP link set dev dummy0 carrier off sleep 1 set +e echo " Carrier off on nexthop" - ip -netns testns route get fibmatch 198.51.100.1 &> /dev/null + $IP route get fibmatch 198.51.100.1 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null log_test $? 0 "IPv6 fibmatch" - ip -netns testns route get fibmatch 198.51.100.1 | \ + $IP route get fibmatch 198.51.100.1 | \ grep -q "linkdown" log_test $? 1 "IPv4 - linkdown flag set" - ip -netns testns -6 route get fibmatch 2001:db8:1::1 | \ + $IP -6 route get fibmatch 2001:db8:1::1 | \ grep -q "linkdown" log_test $? 1 "IPv6 - linkdown flag set" set -e - ip -netns testns address add 192.0.2.1/24 dev dummy0 - ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy0 + $IP address add 192.0.2.1/24 dev dummy0 + $IP -6 address add 2001:db8:2::1/64 dev dummy0 set +e echo " Route to local address with carrier down" - ip -netns testns route get fibmatch 192.0.2.1 &> /dev/null + $IP route get fibmatch 192.0.2.1 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:2::1 &> /dev/null + $IP -6 route get fibmatch 2001:db8:2::1 &> /dev/null log_test $? 0 "IPv6 fibmatch" - ip -netns testns route get fibmatch 192.0.2.1 | \ + $IP route get fibmatch 192.0.2.1 | \ grep -q "linkdown" log_test $? 1 "IPv4 linkdown flag set" - ip -netns testns -6 route get fibmatch 2001:db8:2::1 | \ + $IP -6 route get fibmatch 2001:db8:2::1 | \ grep -q "linkdown" log_test $? 1 "IPv6 linkdown flag set" @@ -329,54 +353,54 @@ fib_carrier_unicast_test() setup set -e - ip -netns testns link set dev dummy0 carrier on + $IP link set dev dummy0 carrier on set +e echo " Start point" - ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null + $IP route get fibmatch 198.51.100.2 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null log_test $? 0 "IPv6 fibmatch" - ip -netns testns route get fibmatch 198.51.100.2 | \ + $IP route get fibmatch 198.51.100.2 | \ grep -q "linkdown" log_test $? 1 "IPv4 no linkdown flag" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 | \ + $IP -6 route get fibmatch 2001:db8:1::2 | \ grep -q "linkdown" log_test $? 1 "IPv6 no linkdown flag" set -e - ip -netns testns link set dev dummy0 carrier off + $IP link set dev dummy0 carrier off set +e echo " Carrier down" - ip -netns testns route get fibmatch 198.51.100.2 &> /dev/null + $IP route get fibmatch 198.51.100.2 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null log_test $? 0 "IPv6 fibmatch" - ip -netns testns route get fibmatch 198.51.100.2 | \ + $IP route get fibmatch 198.51.100.2 | \ grep -q "linkdown" log_test $? 0 "IPv4 linkdown flag set" - ip -netns testns -6 route get fibmatch 2001:db8:1::2 | \ + $IP -6 route get fibmatch 2001:db8:1::2 | \ grep -q "linkdown" log_test $? 0 "IPv6 linkdown flag set" set -e - ip -netns testns address add 192.0.2.1/24 dev dummy0 - ip -netns testns -6 address add 2001:db8:2::1/64 dev dummy0 + $IP address add 192.0.2.1/24 dev dummy0 + $IP -6 address add 2001:db8:2::1/64 dev dummy0 set +e echo " Second address added with carrier down" - ip -netns testns route get fibmatch 192.0.2.2 &> /dev/null + $IP route get fibmatch 192.0.2.2 &> /dev/null log_test $? 0 "IPv4 fibmatch" - ip -netns testns -6 route get fibmatch 2001:db8:2::2 &> /dev/null + $IP -6 route get fibmatch 2001:db8:2::2 &> /dev/null log_test $? 0 "IPv6 fibmatch" - ip -netns testns route get fibmatch 192.0.2.2 | \ + $IP route get fibmatch 192.0.2.2 | \ grep -q "linkdown" log_test $? 0 "IPv4 linkdown flag set" - ip -netns testns -6 route get fibmatch 2001:db8:2::2 | \ + $IP -6 route get fibmatch 2001:db8:2::2 | \ grep -q "linkdown" log_test $? 0 "IPv6 linkdown flag set" @@ -389,11 +413,168 @@ fib_carrier_test() fib_carrier_unicast_test } +################################################################################ +# Tests on nexthop spec + +# run 'ip route add' with given spec +add_rt() +{ + local desc="$1" + local erc=$2 + local vrf=$3 + local pfx=$4 + local gw=$5 + local dev=$6 + local cmd out rc + + [ "$vrf" = "-" ] && vrf="default" + [ -n "$gw" ] && gw="via $gw" + [ -n "$dev" ] && dev="dev $dev" + + cmd="$IP route add vrf $vrf $pfx $gw $dev" + if [ "$VERBOSE" = "1" ]; then + printf "\n COMMAND: $cmd\n" + fi + + out=$(eval $cmd 2>&1) + rc=$? + if [ "$VERBOSE" = "1" -a -n "$out" ]; then + echo " $out" + fi + log_test $rc $erc "$desc" +} + +fib4_nexthop() +{ + echo + echo "IPv4 nexthop tests" + + echo "<<< write me >>>" +} + +fib6_nexthop() +{ + local lldummy=$(get_linklocal dummy0) + local llv1=$(get_linklocal dummy0) + + if [ -z "$lldummy" ]; then + echo "Failed to get linklocal address for dummy0" + return 1 + fi + if [ -z "$llv1" ]; then + echo "Failed to get linklocal address for veth1" + return 1 + fi + + echo + echo "IPv6 nexthop tests" + + add_rt "Directly connected nexthop, unicast address" 0 \ + - 2001:db8:101::/64 2001:db8:1::2 + add_rt "Directly connected nexthop, unicast address with device" 0 \ + - 2001:db8:102::/64 2001:db8:1::2 "dummy0" + add_rt "Gateway is linklocal address" 0 \ + - 2001:db8:103::1/64 $llv1 "veth0" + + # fails because LL address requires a device + add_rt "Gateway is linklocal address, no device" 2 \ + - 2001:db8:104::1/64 $llv1 + + # local address can not be a gateway + add_rt "Gateway can not be local unicast address" 2 \ + - 2001:db8:105::/64 2001:db8:1::1 + add_rt "Gateway can not be local unicast address, with device" 2 \ + - 2001:db8:106::/64 2001:db8:1::1 "dummy0" + add_rt "Gateway can not be a local linklocal address" 2 \ + - 2001:db8:107::1/64 $lldummy "dummy0" + + # VRF tests + add_rt "Gateway can be local address in a VRF" 0 \ + - 2001:db8:108::/64 2001:db8:51::2 + add_rt "Gateway can be local address in a VRF, with device" 0 \ + - 2001:db8:109::/64 2001:db8:51::2 "veth0" + add_rt "Gateway can be local linklocal address in a VRF" 0 \ + - 2001:db8:110::1/64 $llv1 "veth0" + + add_rt "Redirect to VRF lookup" 0 \ + - 2001:db8:111::/64 "" "red" + + add_rt "VRF route, gateway can be local address in default VRF" 0 \ + red 2001:db8:112::/64 2001:db8:51::1 + + # local address in same VRF fails + add_rt "VRF route, gateway can not be a local address" 2 \ + red 2001:db8:113::1/64 2001:db8:2::1 + add_rt "VRF route, gateway can not be a local addr with device" 2 \ + red 2001:db8:114::1/64 2001:db8:2::1 "dummy1" +} + +# Default VRF: +# dummy0 - 198.51.100.1/24 2001:db8:1::1/64 +# veth0 - 192.0.2.1/24 2001:db8:51::1/64 +# +# VRF red: +# dummy1 - 192.168.2.1/24 2001:db8:2::1/64 +# veth1 - 192.0.2.2/24 2001:db8:51::2/64 +# +# [ dummy0 veth0 ]--[ veth1 dummy1 ] + +fib_nexthop_test() +{ + setup + + set -e + + $IP -4 rule add pref 32765 table local + $IP -4 rule del pref 0 + $IP -6 rule add pref 32765 table local + $IP -6 rule del pref 0 + + $IP link add red type vrf table 1 + $IP link set red up + $IP -4 route add vrf red unreachable default metric 4278198272 + $IP -6 route add vrf red unreachable default metric 4278198272 + + $IP link add veth0 type veth peer name veth1 + $IP link set dev veth0 up + $IP address add 192.0.2.1/24 dev veth0 + $IP -6 address add 2001:db8:51::1/64 dev veth0 + + $IP link set dev veth1 vrf red up + $IP address add 192.0.2.2/24 dev veth1 + $IP -6 address add 2001:db8:51::2/64 dev veth1 + + $IP link add dummy1 type dummy + $IP link set dev dummy1 vrf red up + $IP address add 192.168.2.1/24 dev dummy1 + $IP -6 address add 2001:db8:2::1/64 dev dummy1 + set +e + + sleep 1 + fib4_nexthop + fib6_nexthop + + ( + $IP link del dev dummy1 + $IP link del veth0 + $IP link del red + ) 2>/dev/null + cleanup +} + +################################################################################ +# + fib_test() { - fib_unreg_test - fib_down_test - fib_carrier_test + if [ -n "$TEST" ]; then + eval $TEST + else + fib_unreg_test + fib_down_test + fib_carrier_test + fib_nexthop_test + fi } if [ "$(id -u)" -ne 0 ];then diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh new file mode 100755 index 000000000000..1cddf06f691d --- /dev/null +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_unaware.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +NUM_NETIFS=4 +source lib.sh + +h1_create() +{ + simple_if_init $h1 192.0.2.1/24 2001:db8:1::1/64 +} + +h1_destroy() +{ + simple_if_fini $h1 192.0.2.1/24 2001:db8:1::1/64 +} + +h2_create() +{ + simple_if_init $h2 192.0.2.2/24 2001:db8:1::2/64 +} + +h2_destroy() +{ + simple_if_fini $h2 192.0.2.2/24 2001:db8:1::2/64 +} + +switch_create() +{ + # 10 Seconds ageing time. + ip link add dev br0 type bridge ageing_time 1000 mcast_snooping 0 + + ip link set dev $swp1 master br0 + ip link set dev $swp2 master br0 + + ip link set dev br0 up + ip link set dev $swp1 up + ip link set dev $swp2 up +} + +switch_destroy() +{ + ip link set dev $swp2 down + ip link set dev $swp1 down + + ip link del dev br0 +} + +setup_prepare() +{ + h1=${NETIFS[p1]} + swp1=${NETIFS[p2]} + + swp2=${NETIFS[p3]} + h2=${NETIFS[p4]} + + vrf_prepare + + h1_create + h2_create + + switch_create +} + +cleanup() +{ + pre_cleanup + + switch_destroy + + h2_destroy + h1_destroy + + vrf_cleanup +} + +trap cleanup EXIT + +setup_prepare +setup_wait + +ping_test $h1 192.0.2.2 +ping6_test $h1 2001:db8:1::2 +learning_test "br0" $swp1 $h1 $h2 +flood_test $swp2 $h1 $h2 + +exit $EXIT_STATUS diff --git a/tools/testing/selftests/net/forwarding/forwarding.config.sample b/tools/testing/selftests/net/forwarding/forwarding.config.sample index ab235c124f20..e819d049d9ce 100644 --- a/tools/testing/selftests/net/forwarding/forwarding.config.sample +++ b/tools/testing/selftests/net/forwarding/forwarding.config.sample @@ -29,3 +29,7 @@ WAIT_TIME=5 PAUSE_ON_FAIL=no # Whether to pause on cleanup or not. PAUSE_ON_CLEANUP=no +# Type of network interface to create +NETIF_TYPE=veth +# Whether to create virtual interfaces (veth) or not +NETIF_CREATE=yes diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index d0af52109360..1ac6c62271f3 100644 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -11,6 +11,8 @@ MZ=${MZ:=mausezahn} WAIT_TIME=${WAIT_TIME:=5} PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no} PAUSE_ON_CLEANUP=${PAUSE_ON_CLEANUP:=no} +NETIF_TYPE=${NETIF_TYPE:=veth} +NETIF_CREATE=${NETIF_CREATE:=yes} if [[ -f forwarding.config ]]; then source forwarding.config @@ -50,12 +52,12 @@ fi if [[ ! -x "$(command -v $MZ)" ]]; then echo "SKIP: $MZ not installed" - exit 0 + exit 1 fi if [[ ! -v NUM_NETIFS ]]; then echo "SKIP: importer does not define \"NUM_NETIFS\"" - exit 0 + exit 1 fi ############################################################################## @@ -76,11 +78,46 @@ done ############################################################################## # Network interfaces configuration +create_netif_veth() +{ + local i + + for i in $(eval echo {1..$NUM_NETIFS}); do + local j=$((i+1)) + + ip link show dev ${NETIFS[p$i]} &> /dev/null + if [[ $? -ne 0 ]]; then + ip link add ${NETIFS[p$i]} type veth \ + peer name ${NETIFS[p$j]} + if [[ $? -ne 0 ]]; then + echo "Failed to create netif" + exit 1 + fi + fi + i=$j + done +} + +create_netif() +{ + case "$NETIF_TYPE" in + veth) create_netif_veth + ;; + *) echo "Can not create interfaces of type \'$NETIF_TYPE\'" + exit 1 + ;; + esac +} + +if [[ "$NETIF_CREATE" = "yes" ]]; then + create_netif +fi + for i in $(eval echo {1..$NUM_NETIFS}); do ip link show dev ${NETIFS[p$i]} &> /dev/null if [[ $? -ne 0 ]]; then echo "SKIP: could not find all required interfaces" - exit 0 + exit 1 fi done diff --git a/tools/testing/selftests/net/forwarding/router_multipath.sh b/tools/testing/selftests/net/forwarding/router_multipath.sh index 55595305a604..3bc351008db6 100755 --- a/tools/testing/selftests/net/forwarding/router_multipath.sh +++ b/tools/testing/selftests/net/forwarding/router_multipath.sh @@ -235,6 +235,45 @@ multipath4_test() sysctl -q -w net.ipv4.fib_multipath_hash_policy=$hash_policy } +multipath6_l4_test() +{ + local desc="$1" + local weight_rp12=$2 + local weight_rp13=$3 + local t0_rp12 t0_rp13 t1_rp12 t1_rp13 + local packets_rp12 packets_rp13 + local hash_policy + + # Transmit multiple flows from h1 to h2 and make sure they are + # distributed between both multipath links (rp12 and rp13) + # according to the configured weights. + hash_policy=$(sysctl -n net.ipv6.fib_multipath_hash_policy) + sysctl -q -w net.ipv6.fib_multipath_hash_policy=1 + + ip route replace 2001:db8:2::/64 vrf vrf-r1 \ + nexthop via fe80:2::22 dev $rp12 weight $weight_rp12 \ + nexthop via fe80:3::23 dev $rp13 weight $weight_rp13 + + t0_rp12=$(link_stats_tx_packets_get $rp12) + t0_rp13=$(link_stats_tx_packets_get $rp13) + + $MZ $h1 -6 -q -p 64 -A 2001:db8:1::2 -B 2001:db8:2::2 \ + -d 1msec -t udp "sp=1024,dp=0-32768" + + t1_rp12=$(link_stats_tx_packets_get $rp12) + t1_rp13=$(link_stats_tx_packets_get $rp13) + + let "packets_rp12 = $t1_rp12 - $t0_rp12" + let "packets_rp13 = $t1_rp13 - $t0_rp13" + multipath_eval "$desc" $weight_rp12 $weight_rp13 $packets_rp12 $packets_rp13 + + ip route replace 2001:db8:2::/64 vrf vrf-r1 \ + nexthop via fe80:2::22 dev $rp12 \ + nexthop via fe80:3::23 dev $rp13 + + sysctl -q -w net.ipv6.fib_multipath_hash_policy=$hash_policy +} + multipath6_test() { local desc="$1" @@ -278,6 +317,11 @@ multipath_test() multipath6_test "ECMP" 1 1 multipath6_test "Weighted MP 2:1" 2 1 multipath6_test "Weighted MP 11:45" 11 45 + + log_info "Running IPv6 L4 hash multipath tests" + multipath6_l4_test "ECMP" 1 1 + multipath6_l4_test "Weighted MP 2:1" 2 1 + multipath6_l4_test "Weighted MP 11:45" 11 45 } setup_prepare() diff --git a/tools/testing/selftests/net/forwarding/tc_actions.sh b/tools/testing/selftests/net/forwarding/tc_actions.sh index 8ab5cf0a960b..3a6385ebd5d0 100755 --- a/tools/testing/selftests/net/forwarding/tc_actions.sh +++ b/tools/testing/selftests/net/forwarding/tc_actions.sh @@ -45,8 +45,10 @@ switch_destroy() simple_if_fini $swp1 192.0.2.2/24 } -mirred_egress_redirect_test() +mirred_egress_test() { + local action=$1 + RET=0 tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \ @@ -59,19 +61,19 @@ mirred_egress_redirect_test() check_fail $? "Matched without redirect rule inserted" tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \ - $tcflags dst_ip 192.0.2.2 action mirred egress redirect \ + $tcflags dst_ip 192.0.2.2 action mirred egress $action \ dev $swp2 $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ -t ip -q tc_check_packets "dev $h2 ingress" 101 1 - check_err $? "Did not match incoming redirected packet" + check_err $? "Did not match incoming $action packet" tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower - log_test "mirred egress redirect ($tcflags)" + log_test "mirred egress $action ($tcflags)" } gact_drop_and_ok_test() @@ -79,7 +81,7 @@ gact_drop_and_ok_test() RET=0 tc filter add dev $swp1 ingress protocol ip pref 2 handle 102 flower \ - skip_hw dst_ip 192.0.2.2 action drop + $tcflags dst_ip 192.0.2.2 action drop $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \ -t ip -q @@ -94,7 +96,10 @@ gact_drop_and_ok_test() -t ip -q tc_check_packets "dev $swp1 ingress" 101 1 - check_err $? "Did not see trapped packet" + check_err $? "Did not see passed packet" + + tc_check_packets "dev $swp1 ingress" 102 2 + check_fail $? "Packet was dropped and it should not reach here" tc filter del dev $swp1 ingress protocol ip pref 2 handle 102 flower tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower @@ -180,7 +185,8 @@ setup_prepare setup_wait gact_drop_and_ok_test -mirred_egress_redirect_test +mirred_egress_test "redirect" +mirred_egress_test "mirror" tc_offload_check if [[ $? -ne 0 ]]; then @@ -188,7 +194,8 @@ if [[ $? -ne 0 ]]; then else tcflags="skip_sw" gact_drop_and_ok_test - mirred_egress_redirect_test + mirred_egress_test "redirect" + mirred_egress_test "mirror" gact_trap_test fi diff --git a/tools/testing/selftests/net/in_netns.sh b/tools/testing/selftests/net/in_netns.sh index f57a2eaad069..88795b510b32 100755 --- a/tools/testing/selftests/net/in_netns.sh +++ b/tools/testing/selftests/net/in_netns.sh @@ -19,5 +19,5 @@ cleanup() { trap cleanup EXIT setup -"$@" +ip netns exec "${NETNS}" "$@" exit "$?" diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh new file mode 100755 index 000000000000..1e428781a625 --- /dev/null +++ b/tools/testing/selftests/net/pmtu.sh @@ -0,0 +1,471 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Check that route PMTU values match expectations, and that initial device MTU +# values are assigned correctly +# +# Tests currently implemented: +# +# - pmtu_vti4_exception +# Set up vti tunnel on top of veth, with xfrm states and policies, in two +# namespaces with matching endpoints. Check that route exception is not +# created if link layer MTU is not exceeded, then exceed it and check that +# exception is created with the expected PMTU. The approach described +# below for IPv6 doesn't apply here, because, on IPv4, administrative MTU +# changes alone won't affect PMTU +# +# - pmtu_vti6_exception +# Set up vti6 tunnel on top of veth, with xfrm states and policies, in two +# namespaces with matching endpoints. Check that route exception is +# created by exceeding link layer MTU with ping to other endpoint. Then +# decrease and increase MTU of tunnel, checking that route exception PMTU +# changes accordingly +# +# - pmtu_vti4_default_mtu +# Set up vti4 tunnel on top of veth, in two namespaces with matching +# endpoints. Check that MTU assigned to vti interface is the MTU of the +# lower layer (veth) minus additional lower layer headers (zero, for veth) +# minus IPv4 header length +# +# - pmtu_vti6_default_mtu +# Same as above, for IPv6 +# +# - pmtu_vti4_link_add_mtu +# Set up vti4 interface passing MTU value at link creation, check MTU is +# configured, and that link is not created with invalid MTU values +# +# - pmtu_vti6_link_add_mtu +# Same as above, for IPv6 +# +# - pmtu_vti6_link_change_mtu +# Set up two dummy interfaces with different MTUs, create a vti6 tunnel +# and check that configured MTU is used on link creation and changes, and +# that MTU is properly calculated instead when MTU is not configured from +# userspace + +tests=" + pmtu_vti6_exception vti6: PMTU exceptions + pmtu_vti4_exception vti4: PMTU exceptions + pmtu_vti4_default_mtu vti4: default MTU assignment + pmtu_vti6_default_mtu vti6: default MTU assignment + pmtu_vti4_link_add_mtu vti4: MTU setting on link creation + pmtu_vti6_link_add_mtu vti6: MTU setting on link creation + pmtu_vti6_link_change_mtu vti6: MTU changes on link changes" + +NS_A="ns-$(mktemp -u XXXXXX)" +NS_B="ns-$(mktemp -u XXXXXX)" +ns_a="ip netns exec ${NS_A}" +ns_b="ip netns exec ${NS_B}" + +veth4_a_addr="192.168.1.1" +veth4_b_addr="192.168.1.2" +veth4_mask="24" +veth6_a_addr="fd00:1::a" +veth6_b_addr="fd00:1::b" +veth6_mask="64" + +vti4_a_addr="192.168.2.1" +vti4_b_addr="192.168.2.2" +vti4_mask="24" +vti6_a_addr="fd00:2::a" +vti6_b_addr="fd00:2::b" +vti6_mask="64" + +dummy6_0_addr="fc00:1000::0" +dummy6_1_addr="fc00:1001::0" +dummy6_mask="64" + +cleanup_done=1 +err_buf= + +err() { + err_buf="${err_buf}${1} +" +} + +err_flush() { + echo -n "${err_buf}" + err_buf= +} + +setup_namespaces() { + ip netns add ${NS_A} || return 1 + ip netns add ${NS_B} +} + +setup_veth() { + ${ns_a} ip link add veth_a type veth peer name veth_b || return 1 + ${ns_a} ip link set veth_b netns ${NS_B} + + ${ns_a} ip addr add ${veth4_a_addr}/${veth4_mask} dev veth_a + ${ns_b} ip addr add ${veth4_b_addr}/${veth4_mask} dev veth_b + + ${ns_a} ip addr add ${veth6_a_addr}/${veth6_mask} dev veth_a + ${ns_b} ip addr add ${veth6_b_addr}/${veth6_mask} dev veth_b + + ${ns_a} ip link set veth_a up + ${ns_b} ip link set veth_b up +} + +setup_vti() { + proto=${1} + veth_a_addr="${2}" + veth_b_addr="${3}" + vti_a_addr="${4}" + vti_b_addr="${5}" + vti_mask=${6} + + [ ${proto} -eq 6 ] && vti_type="vti6" || vti_type="vti" + + ${ns_a} ip link add vti${proto}_a type ${vti_type} local ${veth_a_addr} remote ${veth_b_addr} key 10 || return 1 + ${ns_b} ip link add vti${proto}_b type ${vti_type} local ${veth_b_addr} remote ${veth_a_addr} key 10 + + ${ns_a} ip addr add ${vti_a_addr}/${vti_mask} dev vti${proto}_a + ${ns_b} ip addr add ${vti_b_addr}/${vti_mask} dev vti${proto}_b + + ${ns_a} ip link set vti${proto}_a up + ${ns_b} ip link set vti${proto}_b up + + sleep 1 +} + +setup_vti4() { + setup_vti 4 ${veth4_a_addr} ${veth4_b_addr} ${vti4_a_addr} ${vti4_b_addr} ${vti4_mask} +} + +setup_vti6() { + setup_vti 6 ${veth6_a_addr} ${veth6_b_addr} ${vti6_a_addr} ${vti6_b_addr} ${vti6_mask} +} + +setup_xfrm() { + proto=${1} + veth_a_addr="${2}" + veth_b_addr="${3}" + + ${ns_a} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel || return 1 + ${ns_a} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel + ${ns_a} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel + ${ns_a} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel + + ${ns_b} ip -${proto} xfrm state add src ${veth_a_addr} dst ${veth_b_addr} spi 0x1000 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel + ${ns_b} ip -${proto} xfrm state add src ${veth_b_addr} dst ${veth_a_addr} spi 0x1001 proto esp aead "rfc4106(gcm(aes))" 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 128 mode tunnel + ${ns_b} ip -${proto} xfrm policy add dir out mark 10 tmpl src ${veth_b_addr} dst ${veth_a_addr} proto esp mode tunnel + ${ns_b} ip -${proto} xfrm policy add dir in mark 10 tmpl src ${veth_a_addr} dst ${veth_b_addr} proto esp mode tunnel +} + +setup_xfrm4() { + setup_xfrm 4 ${veth4_a_addr} ${veth4_b_addr} +} + +setup_xfrm6() { + setup_xfrm 6 ${veth6_a_addr} ${veth6_b_addr} +} + +setup() { + [ "$(id -u)" -ne 0 ] && echo " need to run as root" && return 1 + + cleanup_done=0 + for arg do + eval setup_${arg} || { echo " ${arg} not supported"; return 1; } + done +} + +cleanup() { + [ ${cleanup_done} -eq 1 ] && return + ip netns del ${NS_A} 2 > /dev/null + ip netns del ${NS_B} 2 > /dev/null + cleanup_done=1 +} + +mtu() { + ns_cmd="${1}" + dev="${2}" + mtu="${3}" + + ${ns_cmd} ip link set dev ${dev} mtu ${mtu} +} + +mtu_parse() { + input="${1}" + + next=0 + for i in ${input}; do + [ ${next} -eq 1 ] && echo "${i}" && return + [ "${i}" = "mtu" ] && next=1 + done +} + +link_get() { + ns_cmd="${1}" + name="${2}" + + ${ns_cmd} ip link show dev "${name}" +} + +link_get_mtu() { + ns_cmd="${1}" + name="${2}" + + mtu_parse "$(link_get "${ns_cmd}" ${name})" +} + +route_get_dst_exception() { + ns_cmd="${1}" + dst="${2}" + + ${ns_cmd} ip route get "${dst}" +} + +route_get_dst_pmtu_from_exception() { + ns_cmd="${1}" + dst="${2}" + + mtu_parse "$(route_get_dst_exception "${ns_cmd}" ${dst})" +} + +test_pmtu_vti4_exception() { + setup namespaces veth vti4 xfrm4 || return 2 + + veth_mtu=1500 + vti_mtu=$((veth_mtu - 20)) + + # SPI SN IV ICV pad length next header + esp_payload_rfc4106=$((vti_mtu - 4 - 4 - 8 - 16 - 1 - 1)) + ping_payload=$((esp_payload_rfc4106 - 28)) + + mtu "${ns_a}" veth_a ${veth_mtu} + mtu "${ns_b}" veth_b ${veth_mtu} + mtu "${ns_a}" vti4_a ${vti_mtu} + mtu "${ns_b}" vti4_b ${vti_mtu} + + # Send DF packet without exceeding link layer MTU, check that no + # exception is created + ${ns_a} ping -q -M want -i 0.1 -w 2 -s ${ping_payload} ${vti4_b_addr} > /dev/null + pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti4_b_addr})" + if [ "${pmtu}" != "" ]; then + err " unexpected exception created with PMTU ${pmtu} for IP payload length ${esp_payload_rfc4106}" + return 1 + fi + + # Now exceed link layer MTU by one byte, check that exception is created + ${ns_a} ping -q -M want -i 0.1 -w 2 -s $((ping_payload + 1)) ${vti4_b_addr} > /dev/null + pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti4_b_addr})" + if [ "${pmtu}" = "" ]; then + err " exception not created for IP payload length $((esp_payload_rfc4106 + 1))" + return 1 + fi + + # ...with the right PMTU value + if [ ${pmtu} -ne ${esp_payload_rfc4106} ]; then + err " wrong PMTU ${pmtu} in exception, expected: ${esp_payload_rfc4106}" + return 1 + fi +} + +test_pmtu_vti6_exception() { + setup namespaces veth vti6 xfrm6 || return 2 + fail=0 + + # Create route exception by exceeding link layer MTU + mtu "${ns_a}" veth_a 4000 + mtu "${ns_b}" veth_b 4000 + mtu "${ns_a}" vti6_a 5000 + mtu "${ns_b}" vti6_b 5000 + ${ns_a} ping6 -q -i 0.1 -w 2 -s 60000 ${vti6_b_addr} > /dev/null + + # Check that exception was created + if [ "$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})" = "" ]; then + err " tunnel exceeding link layer MTU didn't create route exception" + return 1 + fi + + # Decrease tunnel MTU, check for PMTU decrease in route exception + mtu "${ns_a}" vti6_a 3000 + + if [ "$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})" -ne 3000 ]; then + err " decreasing tunnel MTU didn't decrease route exception PMTU" + fail=1 + fi + + # Increase tunnel MTU, check for PMTU increase in route exception + mtu "${ns_a}" vti6_a 9000 + if [ "$(route_get_dst_pmtu_from_exception "${ns_a}" ${vti6_b_addr})" -ne 9000 ]; then + err " increasing tunnel MTU didn't increase route exception PMTU" + fail=1 + fi + + return ${fail} +} + +test_pmtu_vti4_default_mtu() { + setup namespaces veth vti4 || return 2 + + # Check that MTU of vti device is MTU of veth minus IPv4 header length + veth_mtu="$(link_get_mtu "${ns_a}" veth_a)" + vti4_mtu="$(link_get_mtu "${ns_a}" vti4_a)" + if [ $((veth_mtu - vti4_mtu)) -ne 20 ]; then + err " vti MTU ${vti4_mtu} is not veth MTU ${veth_mtu} minus IPv4 header length" + return 1 + fi +} + +test_pmtu_vti6_default_mtu() { + setup namespaces veth vti6 || return 2 + + # Check that MTU of vti device is MTU of veth minus IPv6 header length + veth_mtu="$(link_get_mtu "${ns_a}" veth_a)" + vti6_mtu="$(link_get_mtu "${ns_a}" vti6_a)" + if [ $((veth_mtu - vti6_mtu)) -ne 40 ]; then + err " vti MTU ${vti6_mtu} is not veth MTU ${veth_mtu} minus IPv6 header length" + return 1 + fi +} + +test_pmtu_vti4_link_add_mtu() { + setup namespaces || return 2 + + ${ns_a} ip link add vti4_a type vti local ${veth4_a_addr} remote ${veth4_b_addr} key 10 + [ $? -ne 0 ] && err " vti not supported" && return 2 + ${ns_a} ip link del vti4_a + + fail=0 + + min=68 + max=$((65528 - 20)) + # Check invalid values first + for v in $((min - 1)) $((max + 1)); do + ${ns_a} ip link add vti4_a mtu ${v} type vti local ${veth4_a_addr} remote ${veth4_b_addr} key 10 2>/dev/null + # This can fail, or MTU can be adjusted to a proper value + [ $? -ne 0 ] && continue + mtu="$(link_get_mtu "${ns_a}" vti4_a)" + if [ ${mtu} -lt ${min} -o ${mtu} -gt ${max} ]; then + err " vti tunnel created with invalid MTU ${mtu}" + fail=1 + fi + ${ns_a} ip link del vti4_a + done + + # Now check valid values + for v in ${min} 1300 ${max}; do + ${ns_a} ip link add vti4_a mtu ${v} type vti local ${veth4_a_addr} remote ${veth4_b_addr} key 10 + mtu="$(link_get_mtu "${ns_a}" vti4_a)" + ${ns_a} ip link del vti4_a + if [ "${mtu}" != "${v}" ]; then + err " vti MTU ${mtu} doesn't match configured value ${v}" + fail=1 + fi + done + + return ${fail} +} + +test_pmtu_vti6_link_add_mtu() { + setup namespaces || return 2 + + ${ns_a} ip link add vti6_a type vti6 local ${veth6_a_addr} remote ${veth6_b_addr} key 10 + [ $? -ne 0 ] && err " vti6 not supported" && return 2 + ${ns_a} ip link del vti6_a + + fail=0 + + min=1280 + max=$((65535 - 40)) + # Check invalid values first + for v in $((min - 1)) $((max + 1)); do + ${ns_a} ip link add vti6_a mtu ${v} type vti6 local ${veth6_a_addr} remote ${veth6_b_addr} key 10 2>/dev/null + # This can fail, or MTU can be adjusted to a proper value + [ $? -ne 0 ] && continue + mtu="$(link_get_mtu "${ns_a}" vti6_a)" + if [ ${mtu} -lt ${min} -o ${mtu} -gt ${max} ]; then + err " vti6 tunnel created with invalid MTU ${v}" + fail=1 + fi + ${ns_a} ip link del vti6_a + done + + # Now check valid values + for v in 1280 1300 $((65535 - 40)); do + ${ns_a} ip link add vti6_a mtu ${v} type vti6 local ${veth6_a_addr} remote ${veth6_b_addr} key 10 + mtu="$(link_get_mtu "${ns_a}" vti6_a)" + ${ns_a} ip link del vti6_a + if [ "${mtu}" != "${v}" ]; then + err " vti6 MTU ${mtu} doesn't match configured value ${v}" + fail=1 + fi + done + + return ${fail} +} + +test_pmtu_vti6_link_change_mtu() { + setup namespaces || return 2 + + ${ns_a} ip link add dummy0 mtu 1500 type dummy + [ $? -ne 0 ] && err " dummy not supported" && return 2 + ${ns_a} ip link add dummy1 mtu 3000 type dummy + ${ns_a} ip link set dummy0 up + ${ns_a} ip link set dummy1 up + + ${ns_a} ip addr add ${dummy6_0_addr}/${dummy6_mask} dev dummy0 + ${ns_a} ip addr add ${dummy6_1_addr}/${dummy6_mask} dev dummy1 + + fail=0 + + # Create vti6 interface bound to device, passing MTU, check it + ${ns_a} ip link add vti6_a mtu 1300 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr} + mtu="$(link_get_mtu "${ns_a}" vti6_a)" + if [ ${mtu} -ne 1300 ]; then + err " vti6 MTU ${mtu} doesn't match configured value 1300" + fail=1 + fi + + # Move to another device with different MTU, without passing MTU, check + # MTU is adjusted + ${ns_a} ip link set vti6_a type vti6 remote ${dummy6_1_addr} local ${dummy6_1_addr} + mtu="$(link_get_mtu "${ns_a}" vti6_a)" + if [ ${mtu} -ne $((3000 - 40)) ]; then + err " vti MTU ${mtu} is not dummy MTU 3000 minus IPv6 header length" + fail=1 + fi + + # Move it back, passing MTU, check MTU is not overridden + ${ns_a} ip link set vti6_a mtu 1280 type vti6 remote ${dummy6_0_addr} local ${dummy6_0_addr} + mtu="$(link_get_mtu "${ns_a}" vti6_a)" + if [ ${mtu} -ne 1280 ]; then + err " vti6 MTU ${mtu} doesn't match configured value 1280" + fail=1 + fi + + return ${fail} +} + +trap cleanup EXIT + +exitcode=0 +desc=0 +IFS=" +" +for t in ${tests}; do + [ $desc -eq 0 ] && name="${t}" && desc=1 && continue || desc=0 + + ( + unset IFS + eval test_${name} + ret=$? + cleanup + + if [ $ret -eq 0 ]; then + printf "TEST: %-60s [ OK ]\n" "${t}" + elif [ $ret -eq 1 ]; then + printf "TEST: %-60s [FAIL]\n" "${t}" + err_flush + exit 1 + elif [ $ret -eq 2 ]; then + printf "TEST: %-60s [SKIP]\n" "${t}" + err_flush + fi + ) + [ $? -ne 0 ] && exitcode=1 +done + +exit ${exitcode} diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index a622eeecc3a6..e6f485235435 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -517,6 +517,7 @@ kci_test_gretap() ip link help gretap 2>&1 | grep -q "^Usage:" if [ $? -ne 0 ];then echo "SKIP: gretap: iproute2 too old" + ip netns del "$testns" return 1 fi @@ -543,6 +544,7 @@ kci_test_gretap() if [ $ret -ne 0 ]; then echo "FAIL: gretap" + ip netns del "$testns" return 1 fi echo "PASS: gretap" @@ -565,6 +567,7 @@ kci_test_ip6gretap() ip link help ip6gretap 2>&1 | grep -q "^Usage:" if [ $? -ne 0 ];then echo "SKIP: ip6gretap: iproute2 too old" + ip netns del "$testns" return 1 fi @@ -591,6 +594,7 @@ kci_test_ip6gretap() if [ $ret -ne 0 ]; then echo "FAIL: ip6gretap" + ip netns del "$testns" return 1 fi echo "PASS: ip6gretap" @@ -655,6 +659,7 @@ kci_test_erspan() if [ $ret -ne 0 ]; then echo "FAIL: erspan" + ip netns del "$testns" return 1 fi echo "PASS: erspan" @@ -720,6 +725,7 @@ kci_test_ip6erspan() if [ $ret -ne 0 ]; then echo "FAIL: ip6erspan" + ip netns del "$testns" return 1 fi echo "PASS: ip6erspan" diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c index 5df07047ca86..81a98a240456 100644 --- a/tools/testing/selftests/networking/timestamping/txtimestamp.c +++ b/tools/testing/selftests/networking/timestamping/txtimestamp.c @@ -68,9 +68,11 @@ static int cfg_num_pkts = 4; static int do_ipv4 = 1; static int do_ipv6 = 1; static int cfg_payload_len = 10; +static int cfg_poll_timeout = 100; static bool cfg_show_payload; static bool cfg_do_pktinfo; static bool cfg_loop_nodata; +static bool cfg_no_delay; static uint16_t dest_port = 9000; static struct sockaddr_in daddr; @@ -171,7 +173,7 @@ static void __poll(int fd) memset(&pollfd, 0, sizeof(pollfd)); pollfd.fd = fd; - ret = poll(&pollfd, 1, 100); + ret = poll(&pollfd, 1, cfg_poll_timeout); if (ret != 1) error(1, errno, "poll"); } @@ -371,7 +373,8 @@ static void do_test(int family, unsigned int opt) error(1, errno, "send"); /* wait for all errors to be queued, else ACKs arrive OOO */ - usleep(50 * 1000); + if (!cfg_no_delay) + usleep(50 * 1000); __poll(fd); @@ -392,6 +395,9 @@ static void __attribute__((noreturn)) usage(const char *filepath) " -4: only IPv4\n" " -6: only IPv6\n" " -h: show this message\n" + " -c N: number of packets for each test\n" + " -D: no delay between packets\n" + " -F: poll() waits forever for an event\n" " -I: request PKTINFO\n" " -l N: send N bytes at a time\n" " -n: set no-payload option\n" @@ -409,7 +415,7 @@ static void parse_opt(int argc, char **argv) int proto_count = 0; char c; - while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { + while ((c = getopt(argc, argv, "46c:DFhIl:np:rRux")) != -1) { switch (c) { case '4': do_ipv6 = 0; @@ -417,6 +423,15 @@ static void parse_opt(int argc, char **argv) case '6': do_ipv4 = 0; break; + case 'c': + cfg_num_pkts = strtoul(optarg, NULL, 10); + break; + case 'D': + cfg_no_delay = true; + break; + case 'F': + cfg_poll_timeout = -1; + break; case 'I': cfg_do_pktinfo = true; break; diff --git a/tools/testing/selftests/powerpc/mm/subpage_prot.c b/tools/testing/selftests/powerpc/mm/subpage_prot.c index 35ade7406dcd..3ae77ba93208 100644 --- a/tools/testing/selftests/powerpc/mm/subpage_prot.c +++ b/tools/testing/selftests/powerpc/mm/subpage_prot.c @@ -135,6 +135,16 @@ static int run_test(void *addr, unsigned long size) return 0; } +static int syscall_available(void) +{ + int rc; + + errno = 0; + rc = syscall(__NR_subpage_prot, 0, 0, 0); + + return rc == 0 || (errno != ENOENT && errno != ENOSYS); +} + int test_anon(void) { unsigned long align; @@ -145,6 +155,8 @@ int test_anon(void) void *mallocblock; unsigned long mallocsize; + SKIP_IF(!syscall_available()); + if (getpagesize() != 0x10000) { fprintf(stderr, "Kernel page size must be 64K!\n"); return 1; @@ -180,6 +192,8 @@ int test_file(void) off_t filesize; int fd; + SKIP_IF(!syscall_available()); + fd = open(file_name, O_RDWR); if (fd == -1) { perror("failed to open file"); diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index a23453943ad2..5c72ff978f27 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -16,7 +16,7 @@ $(OUTPUT)/tm-syscall: tm-syscall-asm.S $(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include $(OUTPUT)/tm-tmspr: CFLAGS += -pthread $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 -$(OUTPUT)/tm-resched-dscr: ../pmu/lib.o +$(OUTPUT)/tm-resched-dscr: ../pmu/lib.c $(OUTPUT)/tm-unavailable: CFLAGS += -O0 -pthread -m64 -Wno-error=uninitialized -mvsx $(OUTPUT)/tm-trap: CFLAGS += -O0 -pthread -m64 diff --git a/tools/testing/selftests/powerpc/tm/tm-trap.c b/tools/testing/selftests/powerpc/tm/tm-trap.c index 5d92c23ee6cb..179d592f0073 100644 --- a/tools/testing/selftests/powerpc/tm/tm-trap.c +++ b/tools/testing/selftests/powerpc/tm/tm-trap.c @@ -255,6 +255,8 @@ int tm_trap_test(void) struct sigaction trap_sa; + SKIP_IF(!have_htm()); + trap_sa.sa_flags = SA_SIGINFO; trap_sa.sa_sigaction = trap_signal_handler; sigaction(SIGTRAP, &trap_sa, NULL); diff --git a/tools/testing/selftests/pstore/config b/tools/testing/selftests/pstore/config index 6a8e5a9bfc10..d148f9f89fb6 100644 --- a/tools/testing/selftests/pstore/config +++ b/tools/testing/selftests/pstore/config @@ -2,3 +2,4 @@ CONFIG_MISC_FILESYSTEMS=y CONFIG_PSTORE=y CONFIG_PSTORE_PMSG=y CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=m diff --git a/tools/testing/selftests/sync/Makefile b/tools/testing/selftests/sync/Makefile index b3c8ba3cb668..d0121a8a3523 100644 --- a/tools/testing/selftests/sync/Makefile +++ b/tools/testing/selftests/sync/Makefile @@ -30,7 +30,7 @@ $(TEST_CUSTOM_PROGS): $(TESTS) $(OBJS) $(CC) -o $(TEST_CUSTOM_PROGS) $(OBJS) $(TESTS) $(CFLAGS) $(LDFLAGS) $(OBJS): $(OUTPUT)/%.o: %.c - $(CC) -c $^ -o $@ + $(CC) -c $^ -o $@ $(CFLAGS) $(TESTS): $(OUTPUT)/%.o: %.c $(CC) -c $^ -o $@ diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json new file mode 100644 index 000000000000..5b012f4981d4 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/bpf.json @@ -0,0 +1,289 @@ +[ + { + "id": "d959", + "name": "Add cBPF action with valid bytecode", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC action flush action bpf", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 100", + "expExitCode": "0", + "verifyCmd": "$TC action get action bpf index 100", + "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 100 ref", + "matchCount": "1", + "teardown": [ + "$TC action flush action bpf" + ] + }, + { + "id": "f84a", + "name": "Add cBPF action with invalid bytecode", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,31 0 1 2048,6 0 0 262144,6 0 0 0' index 100", + "expExitCode": "255", + "verifyCmd": "$TC action get action bpf index 100", + "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,31 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 100 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action bpf" + ] + }, + { + "id": "e939", + "name": "Add eBPF action with valid object-file", + "category": [ + "actions", + "bpf" + ], + "setup": [ + "printf '#include <linux/bpf.h>\nchar l[] __attribute__((section(\"license\"),used))=\"GPL\"; __attribute__((section(\"action\"),used)) int m(struct __sk_buff *s) { return 2; }' | clang -O2 -x c -c - -target bpf -o _b.o", + [ + "$TC action flush action bpf", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action add action bpf object-file _b.o index 667", + "expExitCode": "0", + "verifyCmd": "$TC action get action bpf index 667", + "matchPattern": "action order [0-9]*: bpf _b.o:\\[action\\] id [0-9]* tag 3b185187f1855c4c default-action pipe.*index 667 ref", + "matchCount": "1", + "teardown": [ + "$TC action flush action bpf", + "rm -f _b.o" + ] + }, + { + "id": "282d", + "name": "Add eBPF action with invalid object-file", + "category": [ + "actions", + "bpf" + ], + "setup": [ + "printf '#include <linux/bpf.h>\nchar l[] __attribute__((section(\"license\"),used))=\"GPL\"; __attribute__((section(\"action\"),used)) int m(struct __sk_buff *s) { s->data = 0x0; return 2; }' | clang -O2 -x c -c - -target bpf -o _c.o", + [ + "$TC action flush action bpf", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action add action bpf object-file _c.o index 667", + "expExitCode": "255", + "verifyCmd": "$TC action get action bpf index 667", + "matchPattern": "action order [0-9]*: bpf _b.o:\\[action\\] id [0-9].*index 667 ref", + "matchCount": "0", + "teardown": [ + "$TC action flush action bpf", + "rm -f _c.o" + ] + }, + { + "id": "d819", + "name": "Replace cBPF bytecode and action control", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ], + [ + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 555", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action replace action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' drop index 555", + "expExitCode": "0", + "verifyCmd": "$TC action get action bpf index 555", + "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' default-action drop.*index 555 ref", + "matchCount": "1", + "teardown": [ + "$TC action flush action bpf" + ] + }, + { + "id": "6ae3", + "name": "Delete cBPF action ", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ], + [ + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 444", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action delete action bpf index 444", + "expExitCode": "0", + "verifyCmd": "$TC action get action bpf index 444", + "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 444 ref", + "matchCount": "0", + "teardown": [ + "$TC action flush action bpf" + ] + }, + { + "id": "3e0d", + "name": "List cBPF actions", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC action flush action bpf", + 0, + 1, + 255 + ], + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' ok index 101", + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' drop index 102", + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 33024,6 0 0 262144,6 0 0 0' continue index 103" + ], + "cmdUnderTest": "$TC action list action bpf", + "expExitCode": "0", + "verifyCmd": "$TC action list action bpf", + "matchPattern": "action order [0-9]*: bpf bytecode", + "matchCount": "3", + "teardown": [ + "$TC actions flush action bpf" + ] + }, + { + "id": "55ce", + "name": "Flush BPF actions", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ], + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' ok index 101", + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' drop index 102", + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 33024,6 0 0 262144,6 0 0 0' continue index 103" + ], + "cmdUnderTest": "$TC action flush action bpf", + "expExitCode": "0", + "verifyCmd": "$TC action list action bpf", + "matchPattern": "action order [0-9]*: bpf bytecode", + "matchCount": "0", + "teardown": [ + "$TC actions flush action bpf" + ] + }, + { + "id": "ccc3", + "name": "Add cBPF action with duplicate index", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ], + "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 4294967295" + ], + "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' index 4294967295", + "expExitCode": "255", + "verifyCmd": "$TC action get action bpf index 4294967295", + "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*index 4294967295", + "matchCount": "1", + "teardown": [ + "$TC action flush action bpf" + ] + }, + { + "id": "89c7", + "name": "Add cBPF action with invalid index", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' index 4294967296 cookie 12345", + "expExitCode": "255", + "verifyCmd": "$TC action ls action bpf", + "matchPattern": "action order [0-9]*: bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' default-action pipe.*cookie 12345", + "matchCount": "0", + "teardown": [ + "$TC action flush action bpf" + ] + }, + { + "id": "7ab9", + "name": "Add cBPF action with cookie", + "category": [ + "actions", + "bpf" + ], + "setup": [ + [ + "$TC actions flush action bpf", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2054,6 0 0 262144,6 0 0 0' cookie d0d0d0d0d0d0d0d0", + "expExitCode": "0", + "verifyCmd": "$TC action list action bpf", + "matchPattern": "action order [0-9]*: bpf.*cookie d0d0d0d0d0d0d0", + "matchCount": "1", + "teardown": [ + "$TC action flush action bpf" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json new file mode 100644 index 000000000000..70952bd98ff9 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json @@ -0,0 +1,291 @@ +[ + { + "id": "2002", + "name": "Add valid connmark action with defaults", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark", + "expExitCode": "0", + "verifyCmd": "$TC actions list action connmark", + "matchPattern": "action order [0-9]+: connmark zone 0 pipe", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "56a5", + "name": "Add valid connmark action with control pass", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark pass index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action connmark index 1", + "matchPattern": "action order [0-9]+: connmark zone 0 pass.*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "7c66", + "name": "Add valid connmark action with control drop", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark drop index 100", + "expExitCode": "0", + "verifyCmd": "$TC actions get action connmark index 100", + "matchPattern": "action order [0-9]+: connmark zone 0 drop.*index 100 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "a913", + "name": "Add valid connmark action with control pipe", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark pipe index 455", + "expExitCode": "0", + "verifyCmd": "$TC actions get action connmark index 455", + "matchPattern": "action order [0-9]+: connmark zone 0 pipe.*index 455 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "bdd8", + "name": "Add valid connmark action with control reclassify", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark reclassify index 7", + "expExitCode": "0", + "verifyCmd": "$TC actions list action connmark", + "matchPattern": "action order [0-9]+: connmark zone 0 reclassify.*index 7 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "b8be", + "name": "Add valid connmark action with control continue", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark continue index 17", + "expExitCode": "0", + "verifyCmd": "$TC actions list action connmark", + "matchPattern": "action order [0-9]+: connmark zone 0 continue.*index 17 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "d8a6", + "name": "Add valid connmark action with control jump", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark jump 10 index 17", + "expExitCode": "0", + "verifyCmd": "$TC actions list action connmark", + "matchPattern": "action order [0-9]+: connmark zone 0 jump 10.*index 17 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "aae8", + "name": "Add valid connmark action with zone argument", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark zone 100 pipe index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action connmark index 1", + "matchPattern": "action order [0-9]+: connmark zone 100 pipe.*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "2f0b", + "name": "Add valid connmark action with invalid zone argument", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark zone 65536 reclassify index 21", + "expExitCode": "255", + "verifyCmd": "$TC actions get action connmark index 1", + "matchPattern": "action order [0-9]+: connmark zone 65536 reclassify.*index 21 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "9305", + "name": "Add connmark action with unsupported argument", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark zone 655 unsupp_arg pass index 2", + "expExitCode": "255", + "verifyCmd": "$TC actions get action connmark index 2", + "matchPattern": "action order [0-9]+: connmark zone 655 unsupp_arg pass.*index 2 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "71ca", + "name": "Add valid connmark action and replace it", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ], + "$TC actions add action connmark zone 777 pass index 555" + ], + "cmdUnderTest": "$TC actions replace action connmark zone 555 reclassify index 555", + "expExitCode": "0", + "verifyCmd": "$TC actions get action connmark index 555", + "matchPattern": "action order [0-9]+: connmark zone 555 reclassify.*index 555 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + }, + { + "id": "5f8f", + "name": "Add valid connmark action with cookie", + "category": [ + "actions", + "connmark" + ], + "setup": [ + [ + "$TC actions flush action connmark", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action connmark zone 555 pipe index 5 cookie aabbccddeeff112233445566778800a1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action connmark index 5", + "matchPattern": "action order [0-9]+: connmark zone 555 pipe.*index 5 ref.*cookie aabbccddeeff112233445566778800a1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action connmark" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json new file mode 100644 index 000000000000..93cf8fea8ae7 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json @@ -0,0 +1,410 @@ +[ + { + "id": "6d84", + "name": "Add csum iph action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum iph index 800", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 800", + "matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 800 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "1862", + "name": "Add csum ip4h action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum ip4h index 7", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 7", + "matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 7 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "15c6", + "name": "Add csum ipv4h action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum ipv4h index 1122", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 1122", + "matchPattern": "action order [0-9]*: csum \\(iph\\) action pass.*index 1122 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "bf47", + "name": "Add csum icmp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum icmp index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 1", + "matchPattern": "action order [0-9]*: csum \\(icmp\\) action pass.*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "cc1d", + "name": "Add csum igmp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum igmp index 999", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 999", + "matchPattern": "action order [0-9]*: csum \\(igmp\\) action pass.*index 999 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "bccc", + "name": "Add csum foobar action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum foobar index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action csum", + "matchPattern": "action order [0-9]*: csum \\(foobar\\) action pass.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "3bb4", + "name": "Add csum tcp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum tcp index 9999", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 9999", + "matchPattern": "action order [0-9]*: csum \\(tcp\\) action pass.*index 9999 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "759c", + "name": "Add csum udp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum udp index 334455", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 334455", + "matchPattern": "action order [0-9]*: csum \\(udp\\) action pass.*index 334455 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "bdb6", + "name": "Add csum udp xor iph action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum udp xor iph index 3", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action csum", + "matchPattern": "action order [0-9]*: csum \\(udp xor iph\\) action pass.*index 3 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "c220", + "name": "Add csum udplite action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum udplite continue index 3", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 3", + "matchPattern": "action order [0-9]*: csum \\(udplite\\) action continue.*index 3 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "8993", + "name": "Add csum sctp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum sctp index 777", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 777", + "matchPattern": "action order [0-9]*: csum \\(sctp\\) action pass.*index 777 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "b138", + "name": "Add csum ip & icmp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum ip and icmp pipe index 123", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 123", + "matchPattern": "action order [0-9]*: csum \\(iph, icmp\\) action pipe.*index 123 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "eeda", + "name": "Add csum ip & sctp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum ipv4h sctp continue index 2", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 2", + "matchPattern": "action order [0-9]*: csum \\(iph, sctp\\) action continue.*index 2 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "0017", + "name": "Add csum udp or tcp action", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum udp or tcp continue index 27", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 27", + "matchPattern": "action order [0-9]*: csum \\(tcp, udp\\) action continue.*index 27 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "ce92", + "name": "Add csum udp action with cookie", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum udp pipe index 7 cookie 12345678", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 7", + "matchPattern": "action order [0-9]*: csum \\(udp\\) action pipe.*index 7.*cookie 12345678", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "912f", + "name": "Add csum icmp action with large cookie", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action csum icmp pipe index 17 cookie aabbccddeeff1122", + "expExitCode": "0", + "verifyCmd": "$TC actions get action csum index 17", + "matchPattern": "action order [0-9]*: csum \\(icmp\\) action pipe.*index 17.*cookie aabbccddeeff1122", + "matchCount": "1", + "teardown": [ + "$TC actions flush action csum" + ] + }, + { + "id": "879b", + "name": "Add batch of 32 csum tcp actions", + "category": [ + "actions", + "csum" + ], + "setup": [ + [ + "$TC actions flush action csum", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "for i in `seq 1 32`; do cmd=\"action csum tcp continue index $i \"; args=\"$args$cmd\"; done && $TC actions add $args", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action csum", + "matchPattern": "^[ \t]+index [0-9]* ref", + "matchCount": "32", + "teardown": [ + "$TC actions flush action csum" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json b/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json index e2187b6e0b7a..68c91023cdb9 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json @@ -465,5 +465,76 @@ "teardown": [ "$TC actions flush action gact" ] + }, + { + "id": "1021", + "name": "Add batch of 32 gact pass actions", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action pass index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "^[ \t]+index [0-9]+ ref", + "matchCount": "32", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "da7a", + "name": "Add batch of 32 gact continue actions with cookie", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action continue index \\$i cookie aabbccddeeff112233445566778800a1 \\\"; args=\"\\$args\\$cmd\"; done && $TC actions add \\$args\"", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "^[ \t]+index [0-9]+ ref", + "matchCount": "32", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "8aa3", + "name": "Delete batch of 32 gact continue actions", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action continue index \\$i \\\"; args=\\\"\\$args\\$cmd\\\"; done && $TC actions add \\$args\"" + ], + "cmdUnderTest": "bash -c \"for i in \\`seq 1 32\\`; do cmd=\\\"action gact index \\$i \\\"; args=\"\\$args\\$cmd\"; done && $TC actions del \\$args\"", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "^[ \t]+index [0-9]+ ref", + "matchCount": "0", + "teardown": [] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json index 0fcccf18399b..443c9b3c8664 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json @@ -171,6 +171,198 @@ ] }, { + "id": "8917", + "name": "Add mirred mirror action with control pass", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pass index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 1", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pass.*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "1054", + "name": "Add mirred mirror action with control pipe", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pipe index 15", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 15", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pipe.*index 15 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "9887", + "name": "Add mirred mirror action with control continue", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo continue index 15", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 15", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) continue.*index 15 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "e4aa", + "name": "Add mirred mirror action with control reclassify", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo reclassify index 150", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 150", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) reclassify.*index 150 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "ece9", + "name": "Add mirred mirror action with control drop", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo drop index 99", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 99", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) drop.*index 99 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "0031", + "name": "Add mirred mirror action with control jump", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo jump 10 index 99", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 99", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) jump 10.*index 99 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "407c", + "name": "Add mirred mirror action with cookie", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo reclassify cookie aa11bb22cc33dd44ee55", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) reclassify.*cookie aa11bb22cc33dd44ee55", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "8b69", + "name": "Add mirred mirror action with maximum index", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred ingress mirror dev lo pipe index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 4294967295", + "matchPattern": "action order [0-9]*: mirred \\(Ingress Mirror to device lo\\) pipe.*index 4294967295", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { "id": "a70e", "name": "Delete mirred mirror action", "category": [ diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/police.json b/tools/testing/selftests/tc-testing/tc-tests/actions/police.json index 0e602a3f9393..38d85a1d7492 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/police.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/police.json @@ -265,6 +265,150 @@ ] }, { + "id": "ddd6", + "name": "Add police action with invalid rate value", + "category": [ + "actions", + "police" + ], + "setup": [ + [ + "$TC actions flush action police", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action police rate 3tb burst 250k conform-exceed pass/pipe index 5", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action police", + "matchPattern": "action order [0-9]*: police 0x5 rate 3Tb burst 250Kb mtu 2Kb action pass/pipe", + "matchCount": "0", + "teardown": [ + "$TC actions flush action police" + ] + }, + { + "id": "f61c", + "name": "Add police action with invalid burst value", + "category": [ + "actions", + "police" + ], + "setup": [ + [ + "$TC actions flush action police", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action police rate 3kbit burst 250P conform-exceed pass/pipe index 5", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action police", + "matchPattern": "action order [0-9]*: police 0x5 rate 3Kbit burst 250Pb mtu 2Kb action pass/pipe", + "matchCount": "0", + "teardown": [ + "$TC actions flush action police" + ] + }, + { + "id": "c26f", + "name": "Add police action with invalid peakrate value", + "category": [ + "actions", + "police" + ], + "setup": [ + [ + "$TC actions flush action police", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action police rate 90kbit burst 10k mtu 2kb peakrate 100T index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action police", + "matchPattern": "action order [0-9]*: police 0x1 rate 90Kbit burst 10Kb mtu 2Kb peakrate 100Tbit", + "matchCount": "0", + "teardown": [ + "$TC actions flush action police" + ] + }, + { + "id": "db04", + "name": "Add police action with invalid mtu value", + "category": [ + "actions", + "police" + ], + "setup": [ + [ + "$TC actions flush action police", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action police rate 10kbit burst 10k mtu 2Pbit index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions ls action police", + "matchPattern": "action order [0-9]*: police 0x1 rate 10Kbit burst 1Kb mtu 2Pb", + "matchCount": "0", + "teardown": [ + "$TC actions flush action police" + ] + }, + { + "id": "f3c9", + "name": "Add police action with cookie", + "category": [ + "actions", + "police" + ], + "setup": [ + [ + "$TC actions flush action police", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action police rate 10mbit burst 10k index 1 cookie a1b1c1d1e1f12233bb", + "expExitCode": "0", + "verifyCmd": "$TC actions get action police index 1", + "matchPattern": "action order [0-9]*: police 0x1 rate 10Mbit burst 10Kb mtu 2Kb.*cookie a1b1c1d1e1f12233bb", + "matchCount": "1", + "teardown": [ + "$TC actions flush action police" + ] + }, + { + "id": "d190", + "name": "Add police action with maximum index", + "category": [ + "actions", + "police" + ], + "setup": [ + [ + "$TC actions flush action police", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action police rate 10mbit burst 10k index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions get action mirred index 4294967295", + "matchPattern": "action order [0-9]*: police 0xffffffff rate 10Mbit burst 10Kb mtu 2Kb", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { "id": "336e", "name": "Delete police action", "category": [ diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json index 99635ea4722e..37ecc2716fee 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json @@ -216,6 +216,174 @@ ] }, { + "id": "464a", + "name": "Add skbedit action with control pipe", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype host pipe index 11", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 11", + "matchPattern": "action order [0-9]*: skbedit ptype host pipe.*index 11 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "212f", + "name": "Add skbedit action with control reclassify", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 56789 reclassify index 90", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 90", + "matchPattern": "action order [0-9]*: skbedit mark 56789 reclassify.*index 90 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "0651", + "name": "Add skbedit action with control pass", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit queue_mapping 3 pass index 271", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 271", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 pass.*index 271 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "cc53", + "name": "Add skbedit action with control drop", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit queue_mapping 3 drop index 271", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 271", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 3 drop.*index 271 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "ec16", + "name": "Add skbedit action with control jump", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit priority 8 jump 9 index 2", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 2", + "matchPattern": "action order [0-9]*: skbedit priority :8 jump 9.*index 2 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "db54", + "name": "Add skbedit action with control continue", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit priority 16 continue index 32", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 32", + "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "1055", + "name": "Add skbedit action with cookie", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit priority 16 continue index 32 cookie deadbeef", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbedit index 32", + "matchPattern": "action order [0-9]*: skbedit priority :16 continue.*index 32 ref.*cookie deadbeef", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { "id": "5172", "name": "List skbedit actions", "category": [ diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json index e34075059c26..fe3326e939c1 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json @@ -264,6 +264,30 @@ ] }, { + "id": "6046", + "name": "Add skbmod action with control reclassify and cookie", + "category": [ + "actions", + "skbmod" + ], + "setup": [ + [ + "$TC actions flush action skbmod", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbmod set smac 00:01:02:03:04:01 reclassify index 1 cookie ddeeffaabb11cc22", + "expExitCode": "0", + "verifyCmd": "$TC actions get action skbmod index 1", + "matchPattern": "action order [0-9]*: skbmod reclassify set smac 00:01:02:03:04:01.*index 1 ref.*cookie ddeeffaabb11cc22", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbmod" + ] + }, + { "id": "58cb", "name": "List skbmod actions", "category": [ @@ -315,7 +339,7 @@ "cmdUnderTest": "$TC actions ls action skbmod", "expExitCode": "0", "verifyCmd": "$TC actions get action skbmod index 4", - "matchPattern": "action order [0-9]*: skbmod pipe set etype 0x0031", + "matchPattern": "action order [0-9]*: skbmod pipe set etype 0x31", "matchCount": "1", "teardown": [ "$TC actions flush action skbmod" diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json new file mode 100644 index 000000000000..4510ddfa6e54 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json @@ -0,0 +1,410 @@ +[ + { + "id": "6f5a", + "name": "Add vlan pop action", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan pop index 8", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*pop.*index 8 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "ee6f", + "name": "Add vlan pop action with large index", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan pop index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*pop.*index 4294967295 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "b6b9", + "name": "Add vlan pop action with jump opcode", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan pop jump 10 index 8", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*jump 10.*index 8 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "87c3", + "name": "Add vlan pop action with trap opcode", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan pop trap index 8", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*pop trap.*index 8 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "2b91", + "name": "Add vlan invalid action", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan bad_mode", + "expExitCode": "255", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*bad_mode", + "matchCount": "0", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "57fc", + "name": "Add vlan action with invalid protocol type", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push protocol ABCD", + "expExitCode": "255", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*push", + "matchCount": "0", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "3989", + "name": "Add vlan push action with default protocol and priority", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push id 123 index 18", + "expExitCode": "0", + "verifyCmd": "$TC actions get action vlan index 18", + "matchPattern": "action order [0-9]+: vlan.*push id 123 protocol 802.1Q priority 0 pipe.*index 18 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "79dc", + "name": "Add vlan push action with protocol 802.1Q and priority 3", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push id 77 protocol 802.1Q priority 3 continue index 734", + "expExitCode": "0", + "verifyCmd": "$TC actions get action vlan index 734", + "matchPattern": "action order [0-9]+: vlan.*push id 77 protocol 802.1Q priority 3 continue.*index 734 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "4d73", + "name": "Add vlan push action with protocol 802.1AD", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push id 1024 protocol 802.1AD pass index 10000", + "expExitCode": "0", + "verifyCmd": "$TC actions get action vlan index 10000", + "matchPattern": "action order [0-9]+: vlan.*push id 1024 protocol 802.1ad priority 0 pass.*index 10000 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "1f7b", + "name": "Add vlan push action with invalid vlan ID", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push id 5678 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*push id 5678.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "5d02", + "name": "Add vlan push action with invalid IEEE 802.1p priority", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push id 5 priority 10 index 1", + "expExitCode": "255", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*push id 5.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "6812", + "name": "Add vlan modify action for protocol 802.1Q", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan modify protocol 802.1Q id 5 index 100", + "expExitCode": "0", + "verifyCmd": "$TC actions get action vlan index 100", + "matchPattern": "action order [0-9]+: vlan.*modify id 100 protocol 802.1Q priority 0 pipe.*index 100 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "5a31", + "name": "Add vlan modify action for protocol 802.1AD", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan modify protocol 802.1ad id 500 reclassify index 12", + "expExitCode": "0", + "verifyCmd": "$TC actions get action vlan index 12", + "matchPattern": "action order [0-9]+: vlan.*modify id 500 protocol 802.1ad priority 0 reclassify.*index 12 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + }, + { + "id": "83a4", + "name": "Delete vlan pop action", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ], + "$TC actions add action vlan pop index 44" + ], + "cmdUnderTest": "$TC actions del action vlan index 44", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*pop.*index 44 ref", + "matchCount": "0", + "teardown": [] + }, + { + "id": "ed1e", + "name": "Delete vlan push action for protocol 802.1Q", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ], + "$TC actions add action vlan push id 4094 protocol 802.1Q index 999" + ], + "cmdUnderTest": "$TC actions del action vlan index 999", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*push id 4094 protocol 802.1Q priority 0 pipe.*index 999 ref", + "matchCount": "0", + "teardown": [] + }, + { + "id": "a2a3", + "name": "Flush vlan actions", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ], + "$TC actions add action vlan push id 4 protocol 802.1ad index 10", + "$TC actions add action vlan push id 4 protocol 802.1ad index 11", + "$TC actions add action vlan push id 4 protocol 802.1ad index 12", + "$TC actions add action vlan push id 4 protocol 802.1ad index 13" + ], + "cmdUnderTest": "$TC actions flush action vlan", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*push id 4 protocol 802.1ad", + "matchCount": "0", + "teardown": [] + }, + { + "id": "1d78", + "name": "Add vlan action with cookie", + "category": [ + "actions", + "vlan" + ], + "setup": [ + [ + "$TC actions flush action vlan", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action vlan push id 4 cookie a0a0a0a0a0a0a0", + "expExitCode": "0", + "verifyCmd": "$TC actions list action vlan", + "matchPattern": "action order [0-9]+: vlan.*push id 4.*cookie a0a0a0a0a0a0a0", + "matchCount": "1", + "teardown": [ + "$TC actions flush action vlan" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py index 7b50775abaf6..44de4a272a11 100755 --- a/tools/testing/selftests/tc-testing/tdc.py +++ b/tools/testing/selftests/tc-testing/tdc.py @@ -177,6 +177,7 @@ def prepare_env(args, pm, stage, prefix, cmdlist, output = None): '"{}" did not complete successfully'.format(prefix)) def run_one_test(pm, args, index, tidx): + global NAMES result = True tresult = "" tap = "" @@ -184,6 +185,9 @@ def run_one_test(pm, args, index, tidx): print("\t====================\n=====> ", end="") print("Test " + tidx["id"] + ": " + tidx["name"]) + # populate NAMES with TESTID for this test + NAMES['TESTID'] = tidx['id'] + pm.call_pre_case(index, tidx['id']) prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"]) @@ -227,6 +231,8 @@ def run_one_test(pm, args, index, tidx): index += 1 + # remove TESTID from NAMES + del(NAMES['TESTID']) return tap def test_runner(pm, args, filtered_tests): @@ -241,12 +247,17 @@ def test_runner(pm, args, filtered_tests): testlist = filtered_tests tcount = len(testlist) index = 1 - tap = str(index) + ".." + str(tcount) + "\n" + tap = '' badtest = None stage = None emergency_exit = False emergency_exit_message = '' + if args.notap: + if args.verbose: + tap = 'notap requested: omitting test plan\n' + else: + tap = str(index) + ".." + str(tcount) + "\n" try: pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist]) except Exception as ee: @@ -303,15 +314,22 @@ def test_runner(pm, args, filtered_tests): # if we failed in setup or teardown, # fill in the remaining tests with ok-skipped count = index - tap += 'about to flush the tap output if tests need to be skipped\n' - if tcount + 1 != index: - for tidx in testlist[index - 1:]: - msg = 'skipped - previous {} failed'.format(stage) - tap += 'ok {} - {} # {} {} {}\n'.format( - count, tidx['id'], msg, index, badtest.get('id', '--Unknown--')) - count += 1 - - tap += 'done flushing skipped test tap output\n' + if not args.notap: + tap += 'about to flush the tap output if tests need to be skipped\n' + if tcount + 1 != index: + for tidx in testlist[index - 1:]: + msg = 'skipped - previous {} failed'.format(stage) + tap += 'ok {} - {} # {} {} {}\n'.format( + count, tidx['id'], msg, index, badtest.get('id', '--Unknown--')) + count += 1 + + tap += 'done flushing skipped test tap output\n' + + if args.pause: + print('Want to pause\nPress enter to continue ...') + if input(sys.stdin): + print('got something on stdin') + pm.call_post_suite(index) return tap @@ -389,8 +407,14 @@ def set_args(parser): parser.add_argument( '-v', '--verbose', action='count', default=0, help='Show the commands that are being run') + parser.add_argument( + '-N', '--notap', action='store_true', + help='Suppress tap results for command under test') parser.add_argument('-d', '--device', help='Execute the test case in flower category') + parser.add_argument( + '-P', '--pause', action='store_true', + help='Pause execution just before post-suite stage') return parser @@ -601,7 +625,10 @@ def set_operation_mode(pm, args): catresults = test_runner(pm, args, alltests) else: catresults = 'No tests found\n' - print('All test results: \n\n{}'.format(catresults)) + if args.notap: + print('Tap output suppression requested\n') + else: + print('All test results: \n\n{}'.format(catresults)) def main(): """ diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile index 3d5a62ff7d31..f5d7a7851e21 100644 --- a/tools/testing/selftests/vDSO/Makefile +++ b/tools/testing/selftests/vDSO/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +include ../lib.mk + ifndef CROSS_COMPILE CFLAGS := -std=gnu99 CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector @@ -6,16 +8,14 @@ ifeq ($(CONFIG_X86_32),y) LDLIBS += -lgcc_s endif -TEST_PROGS := vdso_test vdso_standalone_test_x86 +TEST_PROGS := $(OUTPUT)/vdso_test $(OUTPUT)/vdso_standalone_test_x86 all: $(TEST_PROGS) -vdso_test: parse_vdso.c vdso_test.c -vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c +$(OUTPUT)/vdso_test: parse_vdso.c vdso_test.c +$(OUTPUT)/vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ vdso_standalone_test_x86.c parse_vdso.c \ - -o vdso_standalone_test_x86 + -o $@ -include ../lib.mk -clean: - rm -fr $(TEST_PROGS) +EXTRA_CLEAN := $(TEST_PROGS) endif diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore index 63c94d776e89..342c7bc9dc8c 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore @@ -11,3 +11,4 @@ mlock-intersect-test mlock-random-test virtual_address_range gup_benchmark +va_128TBswitch diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests index d2561895a021..22d564673830 100755 --- a/tools/testing/selftests/vm/run_vmtests +++ b/tools/testing/selftests/vm/run_vmtests @@ -2,25 +2,33 @@ # SPDX-License-Identifier: GPL-2.0 #please run as root -#we need 256M, below is the size in kB -needmem=262144 mnt=./huge exitcode=0 -#get pagesize and freepages from /proc/meminfo +#get huge pagesize and freepages from /proc/meminfo while read name size unit; do if [ "$name" = "HugePages_Free:" ]; then freepgs=$size fi if [ "$name" = "Hugepagesize:" ]; then - pgsize=$size + hpgsize_KB=$size fi done < /proc/meminfo +# Simple hugetlbfs tests have a hardcoded minimum requirement of +# huge pages totaling 256MB (262144KB) in size. The userfaultfd +# hugetlb test requires a minimum of 2 * nr_cpus huge pages. Take +# both of these requirements into account and attempt to increase +# number of huge pages available. +nr_cpus=$(nproc) +hpgsize_MB=$((hpgsize_KB / 1024)) +half_ufd_size_MB=$((((nr_cpus * hpgsize_MB + 127) / 128) * 128)) +needmem_KB=$((half_ufd_size_MB * 2 * 1024)) + #set proper nr_hugepages -if [ -n "$freepgs" ] && [ -n "$pgsize" ]; then +if [ -n "$freepgs" ] && [ -n "$hpgsize_KB" ]; then nr_hugepgs=`cat /proc/sys/vm/nr_hugepages` - needpgs=`expr $needmem / $pgsize` + needpgs=$((needmem_KB / hpgsize_KB)) tries=2 while [ $tries -gt 0 ] && [ $freepgs -lt $needpgs ]; do lackpgs=$(( $needpgs - $freepgs )) @@ -107,8 +115,9 @@ fi echo "---------------------------" echo "running userfaultfd_hugetlb" echo "---------------------------" -# 256MB total huge pages == 128MB src and 128MB dst -./userfaultfd hugetlb 128 32 $mnt/ufd_test_file +# Test requires source and destination huge pages. Size of source +# (half_ufd_size_MB) is passed as argument to test. +./userfaultfd hugetlb $half_ufd_size_MB 32 $mnt/ufd_test_file if [ $? -ne 0 ]; then echo "[FAIL]" exitcode=1 diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c index 361466a2eaef..ade443a88421 100644 --- a/tools/testing/selftests/x86/entry_from_vm86.c +++ b/tools/testing/selftests/x86/entry_from_vm86.c @@ -95,6 +95,10 @@ asm ( "int3\n\t" "vmcode_int80:\n\t" "int $0x80\n\t" + "vmcode_popf_hlt:\n\t" + "push %ax\n\t" + "popf\n\t" + "hlt\n\t" "vmcode_umip:\n\t" /* addressing via displacements */ "smsw (2052)\n\t" @@ -124,8 +128,8 @@ asm ( extern unsigned char vmcode[], end_vmcode[]; extern unsigned char vmcode_bound[], vmcode_sysenter[], vmcode_syscall[], - vmcode_sti[], vmcode_int3[], vmcode_int80[], vmcode_umip[], - vmcode_umip_str[], vmcode_umip_sldt[]; + vmcode_sti[], vmcode_int3[], vmcode_int80[], vmcode_popf_hlt[], + vmcode_umip[], vmcode_umip_str[], vmcode_umip_sldt[]; /* Returns false if the test was skipped. */ static bool do_test(struct vm86plus_struct *v86, unsigned long eip, @@ -175,7 +179,7 @@ static bool do_test(struct vm86plus_struct *v86, unsigned long eip, (VM86_TYPE(ret) == rettype && VM86_ARG(ret) == retarg)) { printf("[OK]\tReturned correctly\n"); } else { - printf("[FAIL]\tIncorrect return reason\n"); + printf("[FAIL]\tIncorrect return reason (started at eip = 0x%lx, ended at eip = 0x%lx)\n", eip, v86->regs.eip); nerrs++; } @@ -264,6 +268,9 @@ int main(void) v86.regs.ds = load_addr / 16; v86.regs.es = load_addr / 16; + /* Use the end of the page as our stack. */ + v86.regs.esp = 4096; + assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */ /* #BR -- should deliver SIG??? */ @@ -295,6 +302,23 @@ int main(void) v86.regs.eflags &= ~X86_EFLAGS_IF; do_test(&v86, vmcode_sti - vmcode, VM86_STI, 0, "STI with VIP set"); + /* POPF with VIP set but IF clear: should not trap */ + v86.regs.eflags = X86_EFLAGS_VIP; + v86.regs.eax = 0; + do_test(&v86, vmcode_popf_hlt - vmcode, VM86_UNKNOWN, 0, "POPF with VIP set and IF clear"); + + /* POPF with VIP set and IF set: should trap */ + v86.regs.eflags = X86_EFLAGS_VIP; + v86.regs.eax = X86_EFLAGS_IF; + do_test(&v86, vmcode_popf_hlt - vmcode, VM86_STI, 0, "POPF with VIP and IF set"); + + /* POPF with VIP clear and IF set: should not trap */ + v86.regs.eflags = 0; + v86.regs.eax = X86_EFLAGS_IF; + do_test(&v86, vmcode_popf_hlt - vmcode, VM86_UNKNOWN, 0, "POPF with VIP clear and IF set"); + + v86.regs.eflags = 0; + /* INT3 -- should cause #BP */ do_test(&v86, vmcode_int3 - vmcode, VM86_TRAP, 3, "INT3"); @@ -318,7 +342,7 @@ int main(void) clearhandler(SIGSEGV); /* Make sure nothing explodes if we fork. */ - if (fork() > 0) + if (fork() == 0) return 0; return (nerrs == 0 ? 0 : 1); diff --git a/tools/testing/selftests/x86/test_vsyscall.c b/tools/testing/selftests/x86/test_vsyscall.c index be81621446f0..0b4f1cc2291c 100644 --- a/tools/testing/selftests/x86/test_vsyscall.c +++ b/tools/testing/selftests/x86/test_vsyscall.c @@ -450,7 +450,7 @@ static void sigtrap(int sig, siginfo_t *info, void *ctx_void) num_vsyscall_traps++; } -static int test_native_vsyscall(void) +static int test_emulation(void) { time_t tmp; bool is_native; @@ -458,7 +458,7 @@ static int test_native_vsyscall(void) if (!vtime) return 0; - printf("[RUN]\tchecking for native vsyscall\n"); + printf("[RUN]\tchecking that vsyscalls are emulated\n"); sethandler(SIGTRAP, sigtrap, 0); set_eflags(get_eflags() | X86_EFLAGS_TF); vtime(&tmp); @@ -474,11 +474,12 @@ static int test_native_vsyscall(void) */ is_native = (num_vsyscall_traps > 1); - printf("\tvsyscalls are %s (%d instructions in vsyscall page)\n", + printf("[%s]\tvsyscalls are %s (%d instructions in vsyscall page)\n", + (is_native ? "FAIL" : "OK"), (is_native ? "native" : "emulated"), (int)num_vsyscall_traps); - return 0; + return is_native; } #endif @@ -498,7 +499,7 @@ int main(int argc, char **argv) nerrs += test_vsys_r(); #ifdef __x86_64__ - nerrs += test_native_vsyscall(); + nerrs += test_emulation(); #endif return nerrs ? 1 : 0; |