mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
Merge branch 'linus' into sched/urgent, to resolve conflicts
Conflicts: arch/arm64/kernel/entry.S arch/x86/Kconfig include/linux/sched/mm.h kernel/fork.c Signed-off-by: Ingo Molnar <[email protected]>
This commit is contained in:
commit
8284507916
9071 changed files with 389883 additions and 253610 deletions
|
|
@ -9,6 +9,35 @@ MAKE = make
|
|||
CFLAGS += -Wall -O2
|
||||
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
||||
FEATURE_USER = .bpf
|
||||
FEATURE_TESTS = libbfd disassembler-four-args
|
||||
FEATURE_DISPLAY = libbfd disassembler-four-args
|
||||
|
||||
check_feat := 1
|
||||
NON_CHECK_FEAT_TARGETS := clean bpftool_clean
|
||||
ifdef MAKECMDGOALS
|
||||
ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
|
||||
check_feat := 0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(check_feat),1)
|
||||
ifeq ($(FEATURES_DUMP),)
|
||||
include $(srctree)/tools/build/Makefile.feature
|
||||
else
|
||||
include $(FEATURES_DUMP)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(feature-disassembler-four-args), 1)
|
||||
CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
|
||||
endif
|
||||
|
||||
%.yacc.c: %.y
|
||||
$(YACC) -o $@ -d $<
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,14 @@ static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
|
|||
|
||||
disassemble_init_for_target(&info);
|
||||
|
||||
#ifdef DISASM_FOUR_ARGS_SIGNATURE
|
||||
disassemble = disassembler(info.arch,
|
||||
bfd_big_endian(bfdf),
|
||||
info.mach,
|
||||
bfdf);
|
||||
#else
|
||||
disassemble = disassembler(bfdf);
|
||||
#endif
|
||||
assert(disassemble);
|
||||
|
||||
do {
|
||||
|
|
@ -165,7 +172,8 @@ static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
|
|||
{
|
||||
char *ptr, *pptr, *tmp;
|
||||
off_t off = 0;
|
||||
int ret, flen, proglen, pass, ulen = 0;
|
||||
unsigned int proglen;
|
||||
int ret, flen, pass, ulen = 0;
|
||||
regmatch_t pmatch[1];
|
||||
unsigned long base;
|
||||
regex_t regex;
|
||||
|
|
@ -192,7 +200,7 @@ static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
|
|||
}
|
||||
|
||||
ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
|
||||
ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
|
||||
ret = sscanf(ptr, "flen=%d proglen=%u pass=%d image=%lx",
|
||||
&flen, &proglen, &pass, &base);
|
||||
if (ret != 4) {
|
||||
regfree(®ex);
|
||||
|
|
@ -232,7 +240,7 @@ static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
|
|||
}
|
||||
|
||||
assert(ulen == proglen);
|
||||
printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
|
||||
printf("%u bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
|
||||
proglen, pass, flen);
|
||||
printf("%lx + <x>:\n", base);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@ include ../../../scripts/utilities.mak
|
|||
|
||||
INSTALL ?= install
|
||||
RM ?= rm -f
|
||||
RMDIR ?= rmdir --ignore-fail-on-non-empty
|
||||
|
||||
# Make the path relative to DESTDIR, not prefix
|
||||
ifndef DESTDIR
|
||||
prefix ?= /usr/local
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
mandir ?= $(prefix)/share/man
|
||||
|
||||
prefix ?= /usr/local
|
||||
mandir ?= $(prefix)/man
|
||||
man8dir = $(mandir)/man8
|
||||
|
||||
MAN8_RST = $(wildcard *.rst)
|
||||
|
|
@ -20,15 +24,21 @@ man: man8
|
|||
man8: $(DOC_MAN8)
|
||||
|
||||
$(OUTPUT)%.8: %.rst
|
||||
rst2man $< > $@
|
||||
$(QUIET_GEN)rst2man $< > $@
|
||||
|
||||
clean:
|
||||
$(call QUIET_CLEAN, Documentation) $(RM) $(DOC_MAN8)
|
||||
$(call QUIET_CLEAN, Documentation)
|
||||
$(Q)$(RM) $(DOC_MAN8)
|
||||
|
||||
install: man
|
||||
$(call QUIET_INSTALL, Documentation-man) \
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir); \
|
||||
$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir);
|
||||
$(call QUIET_INSTALL, Documentation-man)
|
||||
$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir)
|
||||
$(Q)$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir)
|
||||
|
||||
.PHONY: man man8 clean install
|
||||
uninstall:
|
||||
$(call QUIET_UNINST, Documentation-man)
|
||||
$(Q)$(RM) $(addprefix $(DESTDIR)$(man8dir)/,$(_DOC_MAN8))
|
||||
$(Q)$(RMDIR) $(DESTDIR)$(man8dir)
|
||||
|
||||
.PHONY: man man8 clean install uninstall
|
||||
.DEFAULT_GOAL := man
|
||||
|
|
|
|||
118
tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
Normal file
118
tools/bpf/bpftool/Documentation/bpftool-cgroup.rst
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
================
|
||||
bpftool-cgroup
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF progs
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **cgroup** *COMMAND*
|
||||
|
||||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **list** | **attach** | **detach** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **cgroup { show | list }** *CGROUP*
|
||||
| **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
|
||||
| **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
|
||||
| **bpftool** **cgroup help**
|
||||
|
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
|
||||
| *ATTACH_TYPE* := { *ingress* | *egress* | *sock_create* | *sock_ops* | *device* }
|
||||
| *ATTACH_FLAGS* := { *multi* | *override* }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool cgroup { show | list }** *CGROUP*
|
||||
List all programs attached to the cgroup *CGROUP*.
|
||||
|
||||
Output will start with program ID followed by attach type,
|
||||
attach flags and program name.
|
||||
|
||||
**bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
|
||||
Attach program *PROG* to the cgroup *CGROUP* with attach type
|
||||
*ATTACH_TYPE* and optional *ATTACH_FLAGS*.
|
||||
|
||||
*ATTACH_FLAGS* can be one of: **override** if a sub-cgroup installs
|
||||
some bpf program, the program in this cgroup yields to sub-cgroup
|
||||
program; **multi** if a sub-cgroup installs some bpf program,
|
||||
that cgroup program gets run in addition to the program in this
|
||||
cgroup.
|
||||
|
||||
Only one program is allowed to be attached to a cgroup with
|
||||
no attach flags or the **override** flag. Attaching another
|
||||
program will release old program and attach the new one.
|
||||
|
||||
Multiple programs are allowed to be attached to a cgroup with
|
||||
**multi**. They are executed in FIFO order (those that were
|
||||
attached first, run first).
|
||||
|
||||
Non-default *ATTACH_FLAGS* are supported by kernel version 4.14
|
||||
and later.
|
||||
|
||||
*ATTACH_TYPE* can be on of:
|
||||
**ingress** ingress path of the inet socket (since 4.10);
|
||||
**egress** egress path of the inet socket (since 4.10);
|
||||
**sock_create** opening of an inet socket (since 4.10);
|
||||
**sock_ops** various socket operations (since 4.12);
|
||||
**device** device access (since 4.15).
|
||||
|
||||
**bpftool cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
|
||||
Detach *PROG* from the cgroup *CGROUP* and attach type
|
||||
*ATTACH_TYPE*.
|
||||
|
||||
**bpftool prog help**
|
||||
Print short help message.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
-h, --help
|
||||
Print short generic help message (similar to **bpftool help**).
|
||||
|
||||
-v, --version
|
||||
Print version number (similar to **bpftool version**).
|
||||
|
||||
-j, --json
|
||||
Generate JSON output. For commands that cannot produce JSON, this
|
||||
option has no effect.
|
||||
|
||||
-p, --pretty
|
||||
Generate human-readable JSON output. Implies **-j**.
|
||||
|
||||
-f, --bpffs
|
||||
Show file names of pinned programs.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# mkdir /sys/fs/cgroup/test.slice**
|
||||
| **# bpftool prog load ./device_cgroup.o /sys/fs/bpf/prog**
|
||||
| **# bpftool cgroup attach /sys/fs/cgroup/test.slice/ device id 1 allow_multi**
|
||||
|
||||
**# bpftool cgroup list /sys/fs/cgroup/test.slice/**
|
||||
|
||||
::
|
||||
|
||||
ID AttachType AttachFlags Name
|
||||
1 device allow_multi bpf_prog1
|
||||
|
||||
|
|
||||
| **# bpftool cgroup detach /sys/fs/cgroup/test.slice/ device id 1**
|
||||
| **# bpftool cgroup list /sys/fs/cgroup/test.slice/**
|
||||
|
||||
::
|
||||
|
||||
ID AttachType AttachFlags Name
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-map**\ (8)
|
||||
|
|
@ -15,13 +15,13 @@ SYNOPSIS
|
|||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
| **pin** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **map show** [*MAP*]
|
||||
| **bpftool** **map { show | list }** [*MAP*]
|
||||
| **bpftool** **map dump** *MAP*
|
||||
| **bpftool** **map update** *MAP* **key** *BYTES* **value** *VALUE* [*UPDATE_FLAGS*]
|
||||
| **bpftool** **map lookup** *MAP* **key** *BYTES*
|
||||
|
|
@ -36,7 +36,7 @@ MAP COMMANDS
|
|||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool map show** [*MAP*]
|
||||
**bpftool map { show | list }** [*MAP*]
|
||||
Show information about loaded maps. If *MAP* is specified
|
||||
show information only about given map, otherwise list all
|
||||
maps currently loaded on the system.
|
||||
|
|
@ -128,4 +128,4 @@ EXAMPLES
|
|||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool**\ (8), **bpftool-prog**\ (8)
|
||||
**bpftool**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8)
|
||||
|
|
|
|||
|
|
@ -15,22 +15,23 @@ SYNOPSIS
|
|||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **dump xlated** | **dump jited** | **pin** | **help** }
|
||||
{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **prog show** [*PROG*]
|
||||
| **bpftool** **prog { show | list }** [*PROG*]
|
||||
| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}]
|
||||
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
|
||||
| **bpftool** **prog pin** *PROG* *FILE*
|
||||
| **bpftool** **prog load** *OBJ* *FILE*
|
||||
| **bpftool** **prog help**
|
||||
|
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool prog show** [*PROG*]
|
||||
**bpftool prog { show | list }** [*PROG*]
|
||||
Show information about loaded programs. If *PROG* is
|
||||
specified show information only about given program, otherwise
|
||||
list all programs currently loaded on the system.
|
||||
|
|
@ -57,6 +58,11 @@ DESCRIPTION
|
|||
|
||||
Note: *FILE* must be located in *bpffs* mount.
|
||||
|
||||
**bpftool prog load** *OBJ* *FILE*
|
||||
Load bpf program from binary *OBJ* and pin as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount.
|
||||
|
||||
**bpftool prog help**
|
||||
Print short help message.
|
||||
|
||||
|
|
@ -126,8 +132,10 @@ EXAMPLES
|
|||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# bpftool prog pin id 10 /sys/fs/bpf/prog**
|
||||
| **# bpftool prog load ./my_prog.o /sys/fs/bpf/prog2**
|
||||
| **# ls -l /sys/fs/bpf/**
|
||||
| -rw------- 1 root root 0 Jul 22 01:43 prog
|
||||
| -rw------- 1 root root 0 Jul 22 01:44 prog2
|
||||
|
||||
**# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes**
|
||||
|
||||
|
|
@ -147,4 +155,4 @@ EXAMPLES
|
|||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool**\ (8), **bpftool-map**\ (8)
|
||||
**bpftool**\ (8), **bpftool-map**\ (8), **bpftool-cgroup**\ (8)
|
||||
|
|
|
|||
|
|
@ -16,17 +16,19 @@ SYNOPSIS
|
|||
|
||||
**bpftool** **version**
|
||||
|
||||
*OBJECT* := { **map** | **program** }
|
||||
*OBJECT* := { **map** | **program** | **cgroup** }
|
||||
|
||||
*OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }
|
||||
| { **-j** | **--json** } [{ **-p** | **--pretty** }] }
|
||||
|
||||
*MAP-COMMANDS* :=
|
||||
{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
{ **show** | **list** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
| **pin** | **help** }
|
||||
|
||||
*PROG-COMMANDS* := { **show** | **dump jited** | **dump xlated** | **pin**
|
||||
| **help** }
|
||||
*PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin**
|
||||
| **load** | **help** }
|
||||
|
||||
*CGROUP-COMMANDS* := { **show** | **list** | **attach** | **detach** | **help** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
|
@ -53,4 +55,4 @@ OPTIONS
|
|||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool-map**\ (8), **bpftool-prog**\ (8)
|
||||
**bpftool-map**\ (8), **bpftool-prog**\ (8), **bpftool-cgroup**\ (8)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,10 @@
|
|||
include ../../scripts/Makefile.include
|
||||
|
||||
include ../../scripts/utilities.mak
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
#$(info Determined 'srctree' to be $(srctree))
|
||||
endif
|
||||
|
||||
ifneq ($(objtree),)
|
||||
#$(info Determined 'objtree' to be $(objtree))
|
||||
endif
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
#$(info Determined 'OUTPUT' to be $(OUTPUT))
|
||||
# Adding $(OUTPUT) as a directory to look for source files,
|
||||
# because use generated output files as sources dependency
|
||||
# for flex/bison parsers.
|
||||
VPATH += $(OUTPUT)
|
||||
export VPATH
|
||||
endif
|
||||
|
||||
ifeq ($(V),1)
|
||||
|
|
@ -28,16 +13,18 @@ else
|
|||
Q = @
|
||||
endif
|
||||
|
||||
BPF_DIR = $(srctree)/tools/lib/bpf/
|
||||
BPF_DIR = $(srctree)/tools/lib/bpf/
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
BPF_PATH=$(OUTPUT)
|
||||
BPF_PATH = $(OUTPUT)
|
||||
else
|
||||
BPF_PATH=$(BPF_DIR)
|
||||
BPF_PATH = $(BPF_DIR)
|
||||
endif
|
||||
|
||||
LIBBPF = $(BPF_PATH)libbpf.a
|
||||
|
||||
BPFTOOL_VERSION=$(shell make --no-print-directory -sC ../../.. kernelversion)
|
||||
|
||||
$(LIBBPF): FORCE
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT)
|
||||
|
||||
|
|
@ -45,22 +32,50 @@ $(LIBBPF)-clean:
|
|||
$(call QUIET_CLEAN, libbpf)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null
|
||||
|
||||
prefix = /usr/local
|
||||
prefix ?= /usr/local
|
||||
bash_compdir ?= /usr/share/bash-completion/completions
|
||||
|
||||
CC = gcc
|
||||
|
||||
CFLAGS += -O2
|
||||
CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow
|
||||
CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/
|
||||
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)
|
||||
|
||||
INSTALL ?= install
|
||||
RM ?= rm -f
|
||||
|
||||
FEATURE_USER = .bpftool
|
||||
FEATURE_TESTS = libbfd disassembler-four-args
|
||||
FEATURE_DISPLAY = libbfd disassembler-four-args
|
||||
|
||||
check_feat := 1
|
||||
NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall
|
||||
ifdef MAKECMDGOALS
|
||||
ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
|
||||
check_feat := 0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(check_feat),1)
|
||||
ifeq ($(FEATURES_DUMP),)
|
||||
include $(srctree)/tools/build/Makefile.feature
|
||||
else
|
||||
include $(FEATURES_DUMP)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(feature-disassembler-four-args), 1)
|
||||
CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
|
||||
endif
|
||||
|
||||
include $(wildcard *.d)
|
||||
|
||||
all: $(OUTPUT)bpftool
|
||||
|
||||
SRCS=$(wildcard *.c)
|
||||
OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
|
||||
SRCS = $(wildcard *.c)
|
||||
OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
|
||||
|
||||
$(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
|
||||
$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<
|
||||
|
|
@ -73,21 +88,34 @@ $(OUTPUT)%.o: %.c
|
|||
|
||||
clean: $(LIBBPF)-clean
|
||||
$(call QUIET_CLEAN, bpftool)
|
||||
$(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
|
||||
$(Q)$(RM) $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
|
||||
|
||||
install:
|
||||
install -m 0755 -d $(prefix)/sbin
|
||||
install $(OUTPUT)bpftool $(prefix)/sbin/bpftool
|
||||
install -m 0755 -d $(bash_compdir)
|
||||
install -m 0644 bash-completion/bpftool $(bash_compdir)
|
||||
install: $(OUTPUT)bpftool
|
||||
$(call QUIET_INSTALL, bpftool)
|
||||
$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/sbin
|
||||
$(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/sbin/bpftool
|
||||
$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(bash_compdir)
|
||||
$(Q)$(INSTALL) -m 0644 bash-completion/bpftool $(DESTDIR)$(bash_compdir)
|
||||
|
||||
uninstall:
|
||||
$(call QUIET_UNINST, bpftool)
|
||||
$(Q)$(RM) $(DESTDIR)$(prefix)/sbin/bpftool
|
||||
$(Q)$(RM) $(DESTDIR)$(bash_compdir)/bpftool
|
||||
|
||||
doc:
|
||||
$(Q)$(MAKE) -C Documentation/
|
||||
$(call descend,Documentation)
|
||||
|
||||
doc-clean:
|
||||
$(call descend,Documentation,clean)
|
||||
|
||||
doc-install:
|
||||
$(Q)$(MAKE) -C Documentation/ install
|
||||
$(call descend,Documentation,install)
|
||||
|
||||
doc-uninstall:
|
||||
$(call descend,Documentation,uninstall)
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: all clean FORCE install doc doc-install
|
||||
.PHONY: all FORCE clean install uninstall
|
||||
.PHONY: doc doc-clean doc-install doc-uninstall
|
||||
.DEFAULT_GOAL := all
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ _bpftool()
|
|||
|
||||
local PROG_TYPE='id pinned tag'
|
||||
case $command in
|
||||
show)
|
||||
show|list)
|
||||
[[ $prev != "$command" ]] && return 0
|
||||
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
|
||||
return 0
|
||||
|
|
@ -232,7 +232,7 @@ _bpftool()
|
|||
;;
|
||||
*)
|
||||
[[ $prev == $object ]] && \
|
||||
COMPREPLY=( $( compgen -W 'dump help pin show' -- \
|
||||
COMPREPLY=( $( compgen -W 'dump help pin show list' -- \
|
||||
"$cur" ) )
|
||||
;;
|
||||
esac
|
||||
|
|
@ -240,7 +240,7 @@ _bpftool()
|
|||
map)
|
||||
local MAP_TYPE='id pinned'
|
||||
case $command in
|
||||
show|dump)
|
||||
show|list|dump)
|
||||
case $prev in
|
||||
$command)
|
||||
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
|
||||
|
|
@ -343,7 +343,7 @@ _bpftool()
|
|||
*)
|
||||
[[ $prev == $object ]] && \
|
||||
COMPREPLY=( $( compgen -W 'delete dump getnext help \
|
||||
lookup pin show update' -- "$cur" ) )
|
||||
lookup pin show list update' -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
|
|
|||
308
tools/bpf/bpftool/cgroup.c
Normal file
308
tools/bpf/bpftool/cgroup.c
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// Copyright (C) 2017 Facebook
|
||||
// Author: Roman Gushchin <[email protected]>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <bpf.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#define HELP_SPEC_ATTACH_FLAGS \
|
||||
"ATTACH_FLAGS := { multi | override }"
|
||||
|
||||
#define HELP_SPEC_ATTACH_TYPES \
|
||||
"ATTACH_TYPE := { ingress | egress | sock_create | sock_ops | device }"
|
||||
|
||||
static const char * const attach_type_strings[] = {
|
||||
[BPF_CGROUP_INET_INGRESS] = "ingress",
|
||||
[BPF_CGROUP_INET_EGRESS] = "egress",
|
||||
[BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
|
||||
[BPF_CGROUP_SOCK_OPS] = "sock_ops",
|
||||
[BPF_CGROUP_DEVICE] = "device",
|
||||
[__MAX_BPF_ATTACH_TYPE] = NULL,
|
||||
};
|
||||
|
||||
static enum bpf_attach_type parse_attach_type(const char *str)
|
||||
{
|
||||
enum bpf_attach_type type;
|
||||
|
||||
for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
|
||||
if (attach_type_strings[type] &&
|
||||
is_prefix(str, attach_type_strings[type]))
|
||||
return type;
|
||||
}
|
||||
|
||||
return __MAX_BPF_ATTACH_TYPE;
|
||||
}
|
||||
|
||||
static int show_bpf_prog(int id, const char *attach_type_str,
|
||||
const char *attach_flags_str)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
int prog_fd;
|
||||
|
||||
prog_fd = bpf_prog_get_fd_by_id(id);
|
||||
if (prog_fd < 0)
|
||||
return -1;
|
||||
|
||||
if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
|
||||
close(prog_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_uint_field(json_wtr, "id", info.id);
|
||||
jsonw_string_field(json_wtr, "attach_type",
|
||||
attach_type_str);
|
||||
jsonw_string_field(json_wtr, "attach_flags",
|
||||
attach_flags_str);
|
||||
jsonw_string_field(json_wtr, "name", info.name);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
printf("%-8u %-15s %-15s %-15s\n", info.id,
|
||||
attach_type_str,
|
||||
attach_flags_str,
|
||||
info.name);
|
||||
}
|
||||
|
||||
close(prog_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
|
||||
{
|
||||
__u32 prog_ids[1024] = {0};
|
||||
char *attach_flags_str;
|
||||
__u32 prog_cnt, iter;
|
||||
__u32 attach_flags;
|
||||
char buf[32];
|
||||
int ret;
|
||||
|
||||
prog_cnt = ARRAY_SIZE(prog_ids);
|
||||
ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
|
||||
&prog_cnt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (prog_cnt == 0)
|
||||
return 0;
|
||||
|
||||
switch (attach_flags) {
|
||||
case BPF_F_ALLOW_MULTI:
|
||||
attach_flags_str = "multi";
|
||||
break;
|
||||
case BPF_F_ALLOW_OVERRIDE:
|
||||
attach_flags_str = "override";
|
||||
break;
|
||||
case 0:
|
||||
attach_flags_str = "";
|
||||
break;
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
|
||||
attach_flags_str = buf;
|
||||
}
|
||||
|
||||
for (iter = 0; iter < prog_cnt; iter++)
|
||||
show_bpf_prog(prog_ids[iter], attach_type_strings[type],
|
||||
attach_flags_str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{
|
||||
enum bpf_attach_type type;
|
||||
int cgroup_fd;
|
||||
int ret = -1;
|
||||
|
||||
if (argc < 1) {
|
||||
p_err("too few parameters for cgroup show");
|
||||
goto exit;
|
||||
} else if (argc > 1) {
|
||||
p_err("too many parameters for cgroup show");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
cgroup_fd = open(argv[0], O_RDONLY);
|
||||
if (cgroup_fd < 0) {
|
||||
p_err("can't open cgroup %s", argv[1]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
else
|
||||
printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
|
||||
"AttachFlags", "Name");
|
||||
|
||||
for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
|
||||
/*
|
||||
* Not all attach types may be supported, so it's expected,
|
||||
* that some requests will fail.
|
||||
* If we were able to get the show for at least one
|
||||
* attach type, let's return 0.
|
||||
*/
|
||||
if (show_attached_bpf_progs(cgroup_fd, type) == 0)
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
close(cgroup_fd);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_attach(int argc, char **argv)
|
||||
{
|
||||
enum bpf_attach_type attach_type;
|
||||
int cgroup_fd, prog_fd;
|
||||
int attach_flags = 0;
|
||||
int ret = -1;
|
||||
int i;
|
||||
|
||||
if (argc < 4) {
|
||||
p_err("too few parameters for cgroup attach");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
cgroup_fd = open(argv[0], O_RDONLY);
|
||||
if (cgroup_fd < 0) {
|
||||
p_err("can't open cgroup %s", argv[1]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
attach_type = parse_attach_type(argv[1]);
|
||||
if (attach_type == __MAX_BPF_ATTACH_TYPE) {
|
||||
p_err("invalid attach type");
|
||||
goto exit_cgroup;
|
||||
}
|
||||
|
||||
argc -= 2;
|
||||
argv = &argv[2];
|
||||
prog_fd = prog_parse_fd(&argc, &argv);
|
||||
if (prog_fd < 0)
|
||||
goto exit_cgroup;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (is_prefix(argv[i], "multi")) {
|
||||
attach_flags |= BPF_F_ALLOW_MULTI;
|
||||
} else if (is_prefix(argv[i], "override")) {
|
||||
attach_flags |= BPF_F_ALLOW_OVERRIDE;
|
||||
} else {
|
||||
p_err("unknown option: %s", argv[i]);
|
||||
goto exit_cgroup;
|
||||
}
|
||||
}
|
||||
|
||||
if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
|
||||
p_err("failed to attach program");
|
||||
goto exit_prog;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_null(json_wtr);
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit_prog:
|
||||
close(prog_fd);
|
||||
exit_cgroup:
|
||||
close(cgroup_fd);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_detach(int argc, char **argv)
|
||||
{
|
||||
enum bpf_attach_type attach_type;
|
||||
int prog_fd, cgroup_fd;
|
||||
int ret = -1;
|
||||
|
||||
if (argc < 4) {
|
||||
p_err("too few parameters for cgroup detach");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
cgroup_fd = open(argv[0], O_RDONLY);
|
||||
if (cgroup_fd < 0) {
|
||||
p_err("can't open cgroup %s", argv[1]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
attach_type = parse_attach_type(argv[1]);
|
||||
if (attach_type == __MAX_BPF_ATTACH_TYPE) {
|
||||
p_err("invalid attach type");
|
||||
goto exit_cgroup;
|
||||
}
|
||||
|
||||
argc -= 2;
|
||||
argv = &argv[2];
|
||||
prog_fd = prog_parse_fd(&argc, &argv);
|
||||
if (prog_fd < 0)
|
||||
goto exit_cgroup;
|
||||
|
||||
if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
|
||||
p_err("failed to detach program");
|
||||
goto exit_prog;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_null(json_wtr);
|
||||
|
||||
ret = 0;
|
||||
|
||||
exit_prog:
|
||||
close(prog_fd);
|
||||
exit_cgroup:
|
||||
close(cgroup_fd);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s { show | list } CGROUP\n"
|
||||
" %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
|
||||
" %s %s detach CGROUP ATTACH_TYPE PROG\n"
|
||||
" %s %s help\n"
|
||||
"\n"
|
||||
" " HELP_SPEC_ATTACH_TYPES "\n"
|
||||
" " HELP_SPEC_ATTACH_FLAGS "\n"
|
||||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, argv[-2], bin_name, argv[-2],
|
||||
bin_name, argv[-2], bin_name, argv[-2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "show", do_show },
|
||||
{ "list", do_show },
|
||||
{ "attach", do_attach },
|
||||
{ "detach", do_detach },
|
||||
{ "help", do_help },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
int do_cgroup(int argc, char **argv)
|
||||
{
|
||||
return cmd_select(cmds, argc, argv, do_help);
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
/* Author: Jakub Kicinski <[email protected]> */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fts.h>
|
||||
#include <libgen.h>
|
||||
#include <mntent.h>
|
||||
|
|
@ -44,7 +45,9 @@
|
|||
#include <unistd.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/magic.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
|
|
@ -163,13 +166,49 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
|
|||
return fd;
|
||||
}
|
||||
|
||||
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
||||
int do_pin_fd(int fd, const char *name)
|
||||
{
|
||||
char err_str[ERR_MAX_LEN];
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
char *file;
|
||||
char *dir;
|
||||
int err = 0;
|
||||
|
||||
err = bpf_obj_pin(fd, name);
|
||||
if (!err)
|
||||
goto out;
|
||||
|
||||
file = malloc(strlen(name) + 1);
|
||||
strcpy(file, name);
|
||||
dir = dirname(file);
|
||||
|
||||
if (errno != EPERM || is_bpffs(dir)) {
|
||||
p_err("can't pin the object (%s): %s", name, strerror(errno));
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Attempt to mount bpffs, then retry pinning. */
|
||||
err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
|
||||
if (!err) {
|
||||
err = bpf_obj_pin(fd, name);
|
||||
if (err)
|
||||
p_err("can't pin the object (%s): %s", name,
|
||||
strerror(errno));
|
||||
} else {
|
||||
err_str[ERR_MAX_LEN - 1] = '\0';
|
||||
p_err("can't mount BPF file system to pin the object (%s): %s",
|
||||
name, err_str);
|
||||
}
|
||||
|
||||
out_free:
|
||||
free(file);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
||||
{
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
|
|
@ -195,35 +234,8 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
|||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_obj_pin(fd, *argv);
|
||||
if (!err)
|
||||
goto out_close;
|
||||
err = do_pin_fd(fd, *argv);
|
||||
|
||||
file = malloc(strlen(*argv) + 1);
|
||||
strcpy(file, *argv);
|
||||
dir = dirname(file);
|
||||
|
||||
if (errno != EPERM || is_bpffs(dir)) {
|
||||
p_err("can't pin the object (%s): %s", *argv, strerror(errno));
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Attempt to mount bpffs, then retry pinning. */
|
||||
err = mnt_bpffs(dir, err_str, ERR_MAX_LEN);
|
||||
if (!err) {
|
||||
err = bpf_obj_pin(fd, *argv);
|
||||
if (err)
|
||||
p_err("can't pin the object (%s): %s", *argv,
|
||||
strerror(errno));
|
||||
} else {
|
||||
err_str[ERR_MAX_LEN - 1] = '\0';
|
||||
p_err("can't mount BPF file system to pin the object (%s): %s",
|
||||
*argv, err_str);
|
||||
}
|
||||
|
||||
out_free:
|
||||
free(file);
|
||||
out_close:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -403,3 +415,124 @@ void delete_pinned_obj_table(struct pinned_obj_table *tab)
|
|||
free(obj);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
ifindex_to_name_ns(__u32 ifindex, __u32 ns_dev, __u32 ns_ino, char *buf)
|
||||
{
|
||||
struct stat st;
|
||||
int err;
|
||||
|
||||
err = stat("/proc/self/ns/net", &st);
|
||||
if (err) {
|
||||
p_err("Can't stat /proc/self: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (st.st_dev != ns_dev || st.st_ino != ns_ino)
|
||||
return NULL;
|
||||
|
||||
return if_indextoname(ifindex, buf);
|
||||
}
|
||||
|
||||
static int read_sysfs_hex_int(char *path)
|
||||
{
|
||||
char vendor_id_buf[8];
|
||||
int len;
|
||||
int fd;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
p_err("Can't open %s: %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = read(fd, vendor_id_buf, sizeof(vendor_id_buf));
|
||||
close(fd);
|
||||
if (len < 0) {
|
||||
p_err("Can't read %s: %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (len >= (int)sizeof(vendor_id_buf)) {
|
||||
p_err("Value in %s too long", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vendor_id_buf[len] = 0;
|
||||
|
||||
return strtol(vendor_id_buf, NULL, 0);
|
||||
}
|
||||
|
||||
static int read_sysfs_netdev_hex_int(char *devname, const char *entry_name)
|
||||
{
|
||||
char full_path[64];
|
||||
|
||||
snprintf(full_path, sizeof(full_path), "/sys/class/net/%s/device/%s",
|
||||
devname, entry_name);
|
||||
|
||||
return read_sysfs_hex_int(full_path);
|
||||
}
|
||||
|
||||
const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino)
|
||||
{
|
||||
char devname[IF_NAMESIZE];
|
||||
int vendor_id;
|
||||
int device_id;
|
||||
|
||||
if (!ifindex_to_name_ns(ifindex, ns_dev, ns_ino, devname)) {
|
||||
p_err("Can't get net device name for ifindex %d: %s", ifindex,
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vendor_id = read_sysfs_netdev_hex_int(devname, "vendor");
|
||||
if (vendor_id < 0) {
|
||||
p_err("Can't get device vendor id for %s", devname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (vendor_id) {
|
||||
case 0x19ee:
|
||||
device_id = read_sysfs_netdev_hex_int(devname, "device");
|
||||
if (device_id != 0x4000 &&
|
||||
device_id != 0x6000 &&
|
||||
device_id != 0x6003)
|
||||
p_info("Unknown NFP device ID, assuming it is NFP-6xxx arch");
|
||||
return "NFP-6xxx";
|
||||
default:
|
||||
p_err("Can't get bfd arch name for device vendor id 0x%04x",
|
||||
vendor_id);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
|
||||
{
|
||||
char name[IF_NAMESIZE];
|
||||
|
||||
if (!ifindex)
|
||||
return;
|
||||
|
||||
printf(" dev ");
|
||||
if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
|
||||
printf("%s", name);
|
||||
else
|
||||
printf("ifindex %u ns_dev %llu ns_ino %llu",
|
||||
ifindex, ns_dev, ns_inode);
|
||||
}
|
||||
|
||||
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode)
|
||||
{
|
||||
char name[IF_NAMESIZE];
|
||||
|
||||
if (!ifindex)
|
||||
return;
|
||||
|
||||
jsonw_name(json_wtr, "dev");
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_uint_field(json_wtr, "ifindex", ifindex);
|
||||
jsonw_uint_field(json_wtr, "ns_dev", ns_dev);
|
||||
jsonw_uint_field(json_wtr, "ns_inode", ns_inode);
|
||||
if (ifindex_to_name_ns(ifindex, ns_dev, ns_inode, name))
|
||||
jsonw_string_field(json_wtr, "ifname", name);
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@ static int fprintf_json(void *out, const char *fmt, ...)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
||||
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
|
||||
const char *arch)
|
||||
{
|
||||
disassembler_ftype disassemble;
|
||||
struct disassemble_info info;
|
||||
|
|
@ -100,6 +101,19 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
|||
else
|
||||
init_disassemble_info(&info, stdout,
|
||||
(fprintf_ftype) fprintf);
|
||||
|
||||
/* Update architecture info for offload. */
|
||||
if (arch) {
|
||||
const bfd_arch_info_type *inf = bfd_scan_arch(arch);
|
||||
|
||||
if (inf) {
|
||||
bfdf->arch_info = inf;
|
||||
} else {
|
||||
p_err("No libfd support for %s", arch);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info.arch = bfd_get_arch(bfdf);
|
||||
info.mach = bfd_get_mach(bfdf);
|
||||
info.buffer = image;
|
||||
|
|
@ -107,7 +121,14 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
|||
|
||||
disassemble_init_for_target(&info);
|
||||
|
||||
#ifdef DISASM_FOUR_ARGS_SIGNATURE
|
||||
disassemble = disassembler(info.arch,
|
||||
bfd_big_endian(bfdf),
|
||||
info.mach,
|
||||
bfdf);
|
||||
#else
|
||||
disassemble = disassembler(bfdf);
|
||||
#endif
|
||||
assert(disassemble);
|
||||
|
||||
if (json_output)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/version.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -85,7 +84,7 @@ static int do_help(int argc, char **argv)
|
|||
" %s batch file FILE\n"
|
||||
" %s version\n"
|
||||
"\n"
|
||||
" OBJECT := { prog | map }\n"
|
||||
" OBJECT := { prog | map | cgroup }\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, bin_name, bin_name);
|
||||
|
|
@ -95,21 +94,13 @@ static int do_help(int argc, char **argv)
|
|||
|
||||
static int do_version(int argc, char **argv)
|
||||
{
|
||||
unsigned int version[3];
|
||||
|
||||
version[0] = LINUX_VERSION_CODE >> 16;
|
||||
version[1] = LINUX_VERSION_CODE >> 8 & 0xf;
|
||||
version[2] = LINUX_VERSION_CODE & 0xf;
|
||||
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "version");
|
||||
jsonw_printf(json_wtr, "\"%u.%u.%u\"",
|
||||
version[0], version[1], version[2]);
|
||||
jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
printf("%s v%u.%u.%u\n", bin_name,
|
||||
version[0], version[1], version[2]);
|
||||
printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -173,6 +164,7 @@ static const struct cmd cmds[] = {
|
|||
{ "batch", do_batch },
|
||||
{ "prog", do_prog },
|
||||
{ "map", do_map },
|
||||
{ "cgroup", do_cgroup },
|
||||
{ "version", do_version },
|
||||
{ 0 }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ struct pinned_obj {
|
|||
int build_pinned_obj_table(struct pinned_obj_table *table,
|
||||
enum bpf_obj_type type);
|
||||
void delete_pinned_obj_table(struct pinned_obj_table *tab);
|
||||
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
|
||||
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
|
||||
|
||||
struct cmd {
|
||||
const char *cmd;
|
||||
|
|
@ -111,13 +113,18 @@ char *get_fdinfo(int fd, const char *key);
|
|||
int open_obj_pinned(char *path);
|
||||
int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);
|
||||
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));
|
||||
int do_pin_fd(int fd, const char *name);
|
||||
|
||||
int do_prog(int argc, char **arg);
|
||||
int do_map(int argc, char **arg);
|
||||
int do_cgroup(int argc, char **arg);
|
||||
|
||||
int prog_parse_fd(int *argc, char ***argv);
|
||||
|
||||
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes);
|
||||
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
|
||||
const char *arch);
|
||||
void print_hex_data_json(uint8_t *data, size_t len);
|
||||
|
||||
const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ static const char * const map_type_name[] = {
|
|||
[BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps",
|
||||
[BPF_MAP_TYPE_DEVMAP] = "devmap",
|
||||
[BPF_MAP_TYPE_SOCKMAP] = "sockmap",
|
||||
[BPF_MAP_TYPE_CPUMAP] = "cpumap",
|
||||
};
|
||||
|
||||
static unsigned int get_possible_cpus(void)
|
||||
|
|
@ -428,6 +429,9 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
|
|||
|
||||
jsonw_name(json_wtr, "flags");
|
||||
jsonw_printf(json_wtr, "%#x", info->map_flags);
|
||||
|
||||
print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
|
||||
|
||||
jsonw_uint_field(json_wtr, "bytes_key", info->key_size);
|
||||
jsonw_uint_field(json_wtr, "bytes_value", info->value_size);
|
||||
jsonw_uint_field(json_wtr, "max_entries", info->max_entries);
|
||||
|
|
@ -469,7 +473,9 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
|
|||
if (*info->name)
|
||||
printf("name %s ", info->name);
|
||||
|
||||
printf("flags 0x%x\n", info->map_flags);
|
||||
printf("flags 0x%x", info->map_flags);
|
||||
print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
|
||||
printf("\n");
|
||||
printf("\tkey %uB value %uB max_entries %u",
|
||||
info->key_size, info->value_size, info->max_entries);
|
||||
|
||||
|
|
@ -861,7 +867,7 @@ static int do_help(int argc, char **argv)
|
|||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s show [MAP]\n"
|
||||
"Usage: %s %s { show | list } [MAP]\n"
|
||||
" %s %s dump MAP\n"
|
||||
" %s %s update MAP key BYTES value VALUE [UPDATE_FLAGS]\n"
|
||||
" %s %s lookup MAP key BYTES\n"
|
||||
|
|
@ -885,6 +891,7 @@ static int do_help(int argc, char **argv)
|
|||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "show", do_show },
|
||||
{ "list", do_show },
|
||||
{ "help", do_help },
|
||||
{ "dump", do_dump },
|
||||
{ "update", do_update },
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
#include <bpf.h>
|
||||
#include <libbpf.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "disasm.h"
|
||||
|
|
@ -65,6 +66,7 @@ static const char * const prog_type_name[] = {
|
|||
[BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit",
|
||||
[BPF_PROG_TYPE_SOCK_OPS] = "sock_ops",
|
||||
[BPF_PROG_TYPE_SK_SKB] = "sk_skb",
|
||||
[BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device",
|
||||
};
|
||||
|
||||
static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
|
||||
|
|
@ -229,6 +231,8 @@ static void print_prog_json(struct bpf_prog_info *info, int fd)
|
|||
info->tag[0], info->tag[1], info->tag[2], info->tag[3],
|
||||
info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
|
||||
|
||||
print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
|
||||
|
||||
if (info->load_time) {
|
||||
char buf[32];
|
||||
|
||||
|
|
@ -286,6 +290,7 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)
|
|||
|
||||
printf("tag ");
|
||||
fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
|
||||
print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
|
||||
printf("\n");
|
||||
|
||||
if (info->load_time) {
|
||||
|
|
@ -402,6 +407,88 @@ 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;
|
||||
|
|
@ -411,8 +498,71 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes)
|
||||
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;
|
||||
|
|
@ -426,7 +576,7 @@ static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes)
|
|||
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
|
||||
|
||||
printf("% 4d: ", i);
|
||||
print_bpf_insn(print_insn, NULL, insn + i, true);
|
||||
print_bpf_insn(&cbs, NULL, insn + i, true);
|
||||
|
||||
if (opcodes) {
|
||||
printf(" ");
|
||||
|
|
@ -455,8 +605,15 @@ static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...)
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)
|
||||
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;
|
||||
|
|
@ -471,7 +628,7 @@ static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)
|
|||
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "disasm");
|
||||
print_bpf_insn(print_insn_json, NULL, insn + i, true);
|
||||
print_bpf_insn(&cbs, NULL, insn + i, true);
|
||||
|
||||
if (opcodes) {
|
||||
jsonw_name(json_wtr, "opcodes");
|
||||
|
|
@ -506,6 +663,7 @@ static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)
|
|||
static int do_dump(int argc, char **argv)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
struct dump_data dd = {};
|
||||
__u32 len = sizeof(info);
|
||||
unsigned int buf_size;
|
||||
char *filepath = NULL;
|
||||
|
|
@ -593,6 +751,14 @@ static int do_dump(int argc, char **argv)
|
|||
goto err_free;
|
||||
}
|
||||
|
||||
if ((member_len == &info.jited_prog_len &&
|
||||
info.jited_prog_insns == 0) ||
|
||||
(member_len == &info.xlated_prog_len &&
|
||||
info.xlated_prog_insns == 0)) {
|
||||
p_err("error retrieving insn dump: kernel.kptr_restrict set?");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (filepath) {
|
||||
fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
if (fd < 0) {
|
||||
|
|
@ -609,17 +775,29 @@ static int do_dump(int argc, char **argv)
|
|||
goto err_free;
|
||||
}
|
||||
} else {
|
||||
if (member_len == &info.jited_prog_len)
|
||||
disasm_print_insn(buf, *member_len, opcodes);
|
||||
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(buf, *member_len, opcodes);
|
||||
dump_xlated_json(&dd, buf, *member_len, opcodes);
|
||||
else
|
||||
dump_xlated_plain(buf, *member_len, opcodes);
|
||||
dump_xlated_plain(&dd, buf, *member_len, opcodes);
|
||||
kernel_syms_destroy(&dd);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
|
|
@ -637,6 +815,30 @@ static int do_pin(int argc, char **argv)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int do_load(int argc, char **argv)
|
||||
{
|
||||
struct bpf_object *obj;
|
||||
int prog_fd;
|
||||
|
||||
if (argc != 2)
|
||||
usage();
|
||||
|
||||
if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
|
||||
p_err("failed to load program");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (do_pin_fd(prog_fd, argv[1])) {
|
||||
p_err("failed to pin program");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_null(json_wtr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
|
|
@ -645,26 +847,29 @@ static int do_help(int argc, char **argv)
|
|||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s show [PROG]\n"
|
||||
"Usage: %s %s { show | list } [PROG]\n"
|
||||
" %s %s dump xlated PROG [{ file FILE | opcodes }]\n"
|
||||
" %s %s dump jited PROG [{ file FILE | opcodes }]\n"
|
||||
" %s %s pin PROG FILE\n"
|
||||
" %s %s load OBJ FILE\n"
|
||||
" %s %s help\n"
|
||||
"\n"
|
||||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||
bin_name, argv[-2], bin_name, argv[-2]);
|
||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "show", do_show },
|
||||
{ "list", do_show },
|
||||
{ "help", do_help },
|
||||
{ "dump", do_dump },
|
||||
{ "pin", do_pin },
|
||||
{ "load", do_load },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ FILES= \
|
|||
test-hello.bin \
|
||||
test-libaudit.bin \
|
||||
test-libbfd.bin \
|
||||
test-disassembler-four-args.bin \
|
||||
test-liberty.bin \
|
||||
test-liberty-z.bin \
|
||||
test-cplus-demangle.bin \
|
||||
|
|
@ -197,6 +198,9 @@ $(OUTPUT)test-libpython-version.bin:
|
|||
$(OUTPUT)test-libbfd.bin:
|
||||
$(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl
|
||||
|
||||
$(OUTPUT)test-disassembler-four-args.bin:
|
||||
$(BUILD) -DPACKAGE='"perf"' -lbfd -lopcodes
|
||||
|
||||
$(OUTPUT)test-liberty.bin:
|
||||
$(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty
|
||||
|
||||
|
|
|
|||
15
tools/build/feature/test-disassembler-four-args.c
Normal file
15
tools/build/feature/test-disassembler-four-args.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <bfd.h>
|
||||
#include <dis-asm.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bfd *abfd = bfd_openr(NULL, NULL);
|
||||
|
||||
disassembler(bfd_get_arch(abfd),
|
||||
bfd_big_endian(abfd),
|
||||
bfd_get_mach(abfd),
|
||||
abfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -23,12 +24,13 @@
|
|||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
int monitor_device(const char *device_name,
|
||||
unsigned int line,
|
||||
u_int32_t handleflags,
|
||||
u_int32_t eventflags,
|
||||
uint32_t handleflags,
|
||||
uint32_t eventflags,
|
||||
unsigned int loops)
|
||||
{
|
||||
struct gpioevent_request req;
|
||||
|
|
@ -145,8 +147,8 @@ int main(int argc, char **argv)
|
|||
const char *device_name = NULL;
|
||||
unsigned int line = -1;
|
||||
unsigned int loops = 0;
|
||||
u_int32_t handleflags = GPIOHANDLE_REQUEST_INPUT;
|
||||
u_int32_t eventflags = 0;
|
||||
uint32_t handleflags = GPIOHANDLE_REQUEST_INPUT;
|
||||
uint32_t eventflags = 0;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,30 @@ CFLAGS = $(WARNINGS) -g $(shell getconf LFS_CFLAGS)
|
|||
|
||||
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
||||
|
||||
all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
|
||||
sbindir ?= /usr/sbin
|
||||
libexecdir ?= /usr/libexec
|
||||
sharedstatedir ?= /var/lib
|
||||
|
||||
ALL_PROGRAMS := hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
|
||||
|
||||
ALL_SCRIPTS := hv_get_dhcp_info.sh hv_get_dns_info.sh hv_set_ifconfig.sh
|
||||
|
||||
all: $(ALL_PROGRAMS)
|
||||
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
|
||||
|
||||
install: all
|
||||
install -d -m 755 $(DESTDIR)$(sbindir); \
|
||||
install -d -m 755 $(DESTDIR)$(libexecdir)/hypervkvpd; \
|
||||
install -d -m 755 $(DESTDIR)$(sharedstatedir); \
|
||||
for program in $(ALL_PROGRAMS); do \
|
||||
install $$program -m 755 $(DESTDIR)$(sbindir); \
|
||||
done; \
|
||||
install -m 755 lsvmbus $(DESTDIR)$(sbindir); \
|
||||
for script in $(ALL_SCRIPTS); do \
|
||||
install $$script -m 755 $(DESTDIR)$(libexecdir)/hypervkvpd/$${script%.sh}; \
|
||||
done
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
#define BPF_ALU64 0x07 /* alu mode in double word width */
|
||||
|
||||
/* ld/ldx fields */
|
||||
#define BPF_DW 0x18 /* double word */
|
||||
#define BPF_DW 0x18 /* double word (64-bit) */
|
||||
#define BPF_XADD 0xc0 /* exclusive add */
|
||||
|
||||
/* alu/jmp fields */
|
||||
|
|
@ -197,8 +197,14 @@ enum bpf_attach_type {
|
|||
*/
|
||||
#define BPF_F_STRICT_ALIGNMENT (1U << 0)
|
||||
|
||||
/* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */
|
||||
#define BPF_PSEUDO_MAP_FD 1
|
||||
|
||||
/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
|
||||
* offset to another bpf function
|
||||
*/
|
||||
#define BPF_PSEUDO_CALL 1
|
||||
|
||||
/* flags for BPF_MAP_UPDATE_ELEM command */
|
||||
#define BPF_ANY 0 /* create new element or update existing */
|
||||
#define BPF_NOEXIST 1 /* create new element if it didn't exist */
|
||||
|
|
@ -239,6 +245,7 @@ union bpf_attr {
|
|||
* BPF_F_NUMA_NODE is set).
|
||||
*/
|
||||
char map_name[BPF_OBJ_NAME_LEN];
|
||||
__u32 map_ifindex; /* ifindex of netdev to create on */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
|
||||
|
|
@ -635,6 +642,14 @@ union bpf_attr {
|
|||
* @optlen: length of optval in bytes
|
||||
* Return: 0 or negative error
|
||||
*
|
||||
* int bpf_sock_ops_cb_flags_set(bpf_sock_ops, flags)
|
||||
* Set callback flags for sock_ops
|
||||
* @bpf_sock_ops: pointer to bpf_sock_ops_kern struct
|
||||
* @flags: flags value
|
||||
* Return: 0 for no error
|
||||
* -EINVAL if there is no full tcp socket
|
||||
* bits in flags that are not supported by current kernel
|
||||
*
|
||||
* int bpf_skb_adjust_room(skb, len_diff, mode, flags)
|
||||
* Grow or shrink room in sk_buff.
|
||||
* @skb: pointer to skb
|
||||
|
|
@ -677,6 +692,10 @@ union bpf_attr {
|
|||
* @buf: buf to fill
|
||||
* @buf_size: size of the buf
|
||||
* Return : 0 on success or negative error code
|
||||
*
|
||||
* int bpf_override_return(pt_regs, rc)
|
||||
* @pt_regs: pointer to struct pt_regs
|
||||
* @rc: the return value to set
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
|
|
@ -736,7 +755,9 @@ union bpf_attr {
|
|||
FN(xdp_adjust_meta), \
|
||||
FN(perf_event_read_value), \
|
||||
FN(perf_prog_read_value), \
|
||||
FN(getsockopt),
|
||||
FN(getsockopt), \
|
||||
FN(override_return), \
|
||||
FN(sock_ops_cb_flags_set),
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
* function eBPF program intends to call
|
||||
|
|
@ -888,6 +909,9 @@ struct xdp_md {
|
|||
__u32 data;
|
||||
__u32 data_end;
|
||||
__u32 data_meta;
|
||||
/* Below access go through struct xdp_rxq_info */
|
||||
__u32 ingress_ifindex; /* rxq->dev->ifindex */
|
||||
__u32 rx_queue_index; /* rxq->queue_index */
|
||||
};
|
||||
|
||||
enum sk_action {
|
||||
|
|
@ -910,6 +934,9 @@ struct bpf_prog_info {
|
|||
__u32 nr_map_ids;
|
||||
__aligned_u64 map_ids;
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
__u32 ifindex;
|
||||
__u64 netns_dev;
|
||||
__u64 netns_ino;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_map_info {
|
||||
|
|
@ -920,6 +947,9 @@ struct bpf_map_info {
|
|||
__u32 max_entries;
|
||||
__u32 map_flags;
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
__u32 ifindex;
|
||||
__u64 netns_dev;
|
||||
__u64 netns_ino;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* User bpf_sock_ops struct to access socket values and specify request ops
|
||||
|
|
@ -931,8 +961,9 @@ struct bpf_map_info {
|
|||
struct bpf_sock_ops {
|
||||
__u32 op;
|
||||
union {
|
||||
__u32 reply;
|
||||
__u32 replylong[4];
|
||||
__u32 args[4]; /* Optionally passed to bpf program */
|
||||
__u32 reply; /* Returned by bpf program */
|
||||
__u32 replylong[4]; /* Optionally returned by bpf prog */
|
||||
};
|
||||
__u32 family;
|
||||
__u32 remote_ip4; /* Stored in network byte order */
|
||||
|
|
@ -941,8 +972,45 @@ struct bpf_sock_ops {
|
|||
__u32 local_ip6[4]; /* Stored in network byte order */
|
||||
__u32 remote_port; /* Stored in network byte order */
|
||||
__u32 local_port; /* stored in host byte order */
|
||||
__u32 is_fullsock; /* Some TCP fields are only valid if
|
||||
* there is a full socket. If not, the
|
||||
* fields read as zero.
|
||||
*/
|
||||
__u32 snd_cwnd;
|
||||
__u32 srtt_us; /* Averaged RTT << 3 in usecs */
|
||||
__u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */
|
||||
__u32 state;
|
||||
__u32 rtt_min;
|
||||
__u32 snd_ssthresh;
|
||||
__u32 rcv_nxt;
|
||||
__u32 snd_nxt;
|
||||
__u32 snd_una;
|
||||
__u32 mss_cache;
|
||||
__u32 ecn_flags;
|
||||
__u32 rate_delivered;
|
||||
__u32 rate_interval_us;
|
||||
__u32 packets_out;
|
||||
__u32 retrans_out;
|
||||
__u32 total_retrans;
|
||||
__u32 segs_in;
|
||||
__u32 data_segs_in;
|
||||
__u32 segs_out;
|
||||
__u32 data_segs_out;
|
||||
__u32 lost_out;
|
||||
__u32 sacked_out;
|
||||
__u32 sk_txhash;
|
||||
__u64 bytes_received;
|
||||
__u64 bytes_acked;
|
||||
};
|
||||
|
||||
/* Definitions for bpf_sock_ops_cb_flags */
|
||||
#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0)
|
||||
#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1)
|
||||
#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2)
|
||||
#define BPF_SOCK_OPS_ALL_CB_FLAGS 0x7 /* Mask of all currently
|
||||
* supported cb flags
|
||||
*/
|
||||
|
||||
/* List of known BPF sock_ops operators.
|
||||
* New entries can only be added at the end
|
||||
*/
|
||||
|
|
@ -976,6 +1044,43 @@ enum {
|
|||
* a congestion threshold. RTTs above
|
||||
* this indicate congestion
|
||||
*/
|
||||
BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered.
|
||||
* Arg1: value of icsk_retransmits
|
||||
* Arg2: value of icsk_rto
|
||||
* Arg3: whether RTO has expired
|
||||
*/
|
||||
BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted.
|
||||
* Arg1: sequence number of 1st byte
|
||||
* Arg2: # segments
|
||||
* Arg3: return value of
|
||||
* tcp_transmit_skb (0 => success)
|
||||
*/
|
||||
BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state.
|
||||
* Arg1: old_state
|
||||
* Arg2: new_state
|
||||
*/
|
||||
};
|
||||
|
||||
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
|
||||
* changes between the TCP and BPF versions. Ideally this should never happen.
|
||||
* If it does, we need to add code to convert them before calling
|
||||
* the BPF sock_ops function.
|
||||
*/
|
||||
enum {
|
||||
BPF_TCP_ESTABLISHED = 1,
|
||||
BPF_TCP_SYN_SENT,
|
||||
BPF_TCP_SYN_RECV,
|
||||
BPF_TCP_FIN_WAIT1,
|
||||
BPF_TCP_FIN_WAIT2,
|
||||
BPF_TCP_TIME_WAIT,
|
||||
BPF_TCP_CLOSE,
|
||||
BPF_TCP_CLOSE_WAIT,
|
||||
BPF_TCP_LAST_ACK,
|
||||
BPF_TCP_LISTEN,
|
||||
BPF_TCP_CLOSING, /* Now a valid state */
|
||||
BPF_TCP_NEW_SYN_RECV,
|
||||
|
||||
BPF_TCP_MAX_STATES /* Leave at the end! */
|
||||
};
|
||||
|
||||
#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
|
||||
|
|
@ -995,7 +1100,8 @@ struct bpf_perf_event_value {
|
|||
#define BPF_DEVCG_DEV_CHAR (1ULL << 1)
|
||||
|
||||
struct bpf_cgroup_dev_ctx {
|
||||
__u32 access_type; /* (access << 16) | type */
|
||||
/* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */
|
||||
__u32 access_type;
|
||||
__u32 major;
|
||||
__u32 minor;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -418,6 +418,27 @@ struct perf_event_attr {
|
|||
__u16 __reserved_2; /* align to __u64 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure used by below PERF_EVENT_IOC_QUERY_BPF command
|
||||
* to query bpf programs attached to the same perf tracepoint
|
||||
* as the given perf event.
|
||||
*/
|
||||
struct perf_event_query_bpf {
|
||||
/*
|
||||
* The below ids array length
|
||||
*/
|
||||
__u32 ids_len;
|
||||
/*
|
||||
* Set by the kernel to indicate the number of
|
||||
* available programs
|
||||
*/
|
||||
__u32 prog_cnt;
|
||||
/*
|
||||
* User provided buffer to store program ids
|
||||
*/
|
||||
__u32 ids[0];
|
||||
};
|
||||
|
||||
#define perf_flags(attr) (*(&(attr)->read_format + 1))
|
||||
|
||||
/*
|
||||
|
|
@ -433,6 +454,7 @@ struct perf_event_attr {
|
|||
#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
|
||||
#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
|
||||
#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
|
||||
#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
|
||||
|
||||
enum perf_event_ioc_flags {
|
||||
PERF_IOC_FLAG_GROUP = 1U << 0,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ export prefix libdir src obj
|
|||
# Shell quotes
|
||||
libdir_SQ = $(subst ','\'',$(libdir))
|
||||
libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
|
||||
plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
|
||||
|
||||
LIB_FILE = libbpf.a libbpf.so
|
||||
|
||||
|
|
@ -150,7 +149,7 @@ CMD_TARGETS = $(LIB_FILE)
|
|||
|
||||
TARGETS = $(CMD_TARGETS)
|
||||
|
||||
all: fixdep $(VERSION_FILES) all_cmd
|
||||
all: fixdep all_cmd
|
||||
|
||||
all_cmd: $(CMD_TARGETS)
|
||||
|
||||
|
|
@ -169,21 +168,11 @@ $(OUTPUT)libbpf.so: $(BPF_IN)
|
|||
$(OUTPUT)libbpf.a: $(BPF_IN)
|
||||
$(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
|
||||
|
||||
define update_dir
|
||||
(echo $1 > $@.tmp; \
|
||||
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
|
||||
rm -f $@.tmp; \
|
||||
else \
|
||||
echo ' UPDATE $@'; \
|
||||
mv -f $@.tmp $@; \
|
||||
fi);
|
||||
endef
|
||||
|
||||
define do_install
|
||||
if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
|
||||
fi; \
|
||||
$(INSTALL) $1 '$(DESTDIR_SQ)$2'
|
||||
$(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2'
|
||||
endef
|
||||
|
||||
install_lib: all_cmd
|
||||
|
|
@ -192,7 +181,8 @@ install_lib: all_cmd
|
|||
|
||||
install_headers:
|
||||
$(call QUIET_INSTALL, headers) \
|
||||
$(call do_install,bpf.h,$(prefix)/include/bpf,644)
|
||||
$(call do_install,bpf.h,$(prefix)/include/bpf,644); \
|
||||
$(call do_install,libbpf.h,$(prefix)/include/bpf,644);
|
||||
|
||||
install: install_lib
|
||||
|
||||
|
|
@ -203,7 +193,7 @@ config-clean:
|
|||
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
|
||||
|
||||
clean:
|
||||
$(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d .*.cmd \
|
||||
$(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so .*.d .*.cmd \
|
||||
$(RM) LIBBPF-CFLAGS
|
||||
$(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf
|
||||
|
||||
|
|
@ -213,10 +203,10 @@ PHONY += force elfdep bpfdep
|
|||
force:
|
||||
|
||||
elfdep:
|
||||
@if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit -1 ; fi
|
||||
@if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit 1 ; fi
|
||||
|
||||
bpfdep:
|
||||
@if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit -1 ; fi
|
||||
@if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit 1 ; fi
|
||||
|
||||
# Declare the contents of the .PHONY variable as phony. We keep that
|
||||
# information in a variable so we can use it in if_changed and friends.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
|||
__u32 map_flags);
|
||||
|
||||
/* Recommend log buffer size */
|
||||
#define BPF_LOG_BUF_SIZE 65536
|
||||
#define BPF_LOG_BUF_SIZE (256 * 1024)
|
||||
int bpf_load_program_name(enum bpf_prog_type type, const char *name,
|
||||
const struct bpf_insn *insns,
|
||||
size_t insns_cnt, const char *license,
|
||||
|
|
|
|||
|
|
@ -174,12 +174,19 @@ struct bpf_program {
|
|||
char *name;
|
||||
char *section_name;
|
||||
struct bpf_insn *insns;
|
||||
size_t insns_cnt;
|
||||
size_t insns_cnt, main_prog_cnt;
|
||||
enum bpf_prog_type type;
|
||||
|
||||
struct {
|
||||
struct reloc_desc {
|
||||
enum {
|
||||
RELO_LD64,
|
||||
RELO_CALL,
|
||||
} type;
|
||||
int insn_idx;
|
||||
int map_idx;
|
||||
union {
|
||||
int map_idx;
|
||||
int text_off;
|
||||
};
|
||||
} *reloc_desc;
|
||||
int nr_reloc;
|
||||
|
||||
|
|
@ -234,6 +241,7 @@ struct bpf_object {
|
|||
} *reloc;
|
||||
int nr_reloc;
|
||||
int maps_shndx;
|
||||
int text_shndx;
|
||||
} efile;
|
||||
/*
|
||||
* All loaded bpf_object is linked in a list, which is
|
||||
|
|
@ -375,9 +383,13 @@ bpf_object__init_prog_names(struct bpf_object *obj)
|
|||
size_t pi, si;
|
||||
|
||||
for (pi = 0; pi < obj->nr_programs; pi++) {
|
||||
char *name = NULL;
|
||||
const char *name = NULL;
|
||||
|
||||
prog = &obj->programs[pi];
|
||||
if (prog->idx == obj->efile.text_shndx) {
|
||||
name = ".text";
|
||||
goto skip_search;
|
||||
}
|
||||
|
||||
for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name;
|
||||
si++) {
|
||||
|
|
@ -387,6 +399,8 @@ bpf_object__init_prog_names(struct bpf_object *obj)
|
|||
continue;
|
||||
if (sym.st_shndx != prog->idx)
|
||||
continue;
|
||||
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL)
|
||||
continue;
|
||||
|
||||
name = elf_strptr(obj->efile.elf,
|
||||
obj->efile.strtabidx,
|
||||
|
|
@ -403,7 +417,7 @@ bpf_object__init_prog_names(struct bpf_object *obj)
|
|||
prog->section_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skip_search:
|
||||
prog->name = strdup(name);
|
||||
if (!prog->name) {
|
||||
pr_warning("failed to allocate memory for prog sym %s\n",
|
||||
|
|
@ -793,6 +807,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
|
|||
} else if ((sh.sh_type == SHT_PROGBITS) &&
|
||||
(sh.sh_flags & SHF_EXECINSTR) &&
|
||||
(data->d_size > 0)) {
|
||||
if (strcmp(name, ".text") == 0)
|
||||
obj->efile.text_shndx = idx;
|
||||
err = bpf_object__add_program(obj, data->d_buf,
|
||||
data->d_size, name, idx);
|
||||
if (err) {
|
||||
|
|
@ -854,11 +870,14 @@ bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
|
|||
}
|
||||
|
||||
static int
|
||||
bpf_program__collect_reloc(struct bpf_program *prog,
|
||||
size_t nr_maps, GElf_Shdr *shdr,
|
||||
Elf_Data *data, Elf_Data *symbols,
|
||||
int maps_shndx, struct bpf_map *maps)
|
||||
bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
|
||||
Elf_Data *data, struct bpf_object *obj)
|
||||
{
|
||||
Elf_Data *symbols = obj->efile.symbols;
|
||||
int text_shndx = obj->efile.text_shndx;
|
||||
int maps_shndx = obj->efile.maps_shndx;
|
||||
struct bpf_map *maps = obj->maps;
|
||||
size_t nr_maps = obj->nr_maps;
|
||||
int i, nrels;
|
||||
|
||||
pr_debug("collecting relocating info for: '%s'\n",
|
||||
|
|
@ -891,8 +910,11 @@ bpf_program__collect_reloc(struct bpf_program *prog,
|
|||
GELF_R_SYM(rel.r_info));
|
||||
return -LIBBPF_ERRNO__FORMAT;
|
||||
}
|
||||
pr_debug("relo for %lld value %lld name %d\n",
|
||||
(long long) (rel.r_info >> 32),
|
||||
(long long) sym.st_value, sym.st_name);
|
||||
|
||||
if (sym.st_shndx != maps_shndx) {
|
||||
if (sym.st_shndx != maps_shndx && sym.st_shndx != text_shndx) {
|
||||
pr_warning("Program '%s' contains non-map related relo data pointing to section %u\n",
|
||||
prog->section_name, sym.st_shndx);
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
|
|
@ -901,6 +923,17 @@ bpf_program__collect_reloc(struct bpf_program *prog,
|
|||
insn_idx = rel.r_offset / sizeof(struct bpf_insn);
|
||||
pr_debug("relocation: insn_idx=%u\n", insn_idx);
|
||||
|
||||
if (insns[insn_idx].code == (BPF_JMP | BPF_CALL)) {
|
||||
if (insns[insn_idx].src_reg != BPF_PSEUDO_CALL) {
|
||||
pr_warning("incorrect bpf_call opcode\n");
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
prog->reloc_desc[i].type = RELO_CALL;
|
||||
prog->reloc_desc[i].insn_idx = insn_idx;
|
||||
prog->reloc_desc[i].text_off = sym.st_value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
|
||||
pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
|
||||
insn_idx, insns[insn_idx].code);
|
||||
|
|
@ -922,6 +955,7 @@ bpf_program__collect_reloc(struct bpf_program *prog,
|
|||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
|
||||
prog->reloc_desc[i].type = RELO_LD64;
|
||||
prog->reloc_desc[i].insn_idx = insn_idx;
|
||||
prog->reloc_desc[i].map_idx = map_idx;
|
||||
}
|
||||
|
|
@ -960,28 +994,77 @@ bpf_object__create_maps(struct bpf_object *obj)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
|
||||
struct reloc_desc *relo)
|
||||
{
|
||||
struct bpf_insn *insn, *new_insn;
|
||||
struct bpf_program *text;
|
||||
size_t new_cnt;
|
||||
|
||||
if (relo->type != RELO_CALL)
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
|
||||
if (prog->idx == obj->efile.text_shndx) {
|
||||
pr_warning("relo in .text insn %d into off %d\n",
|
||||
relo->insn_idx, relo->text_off);
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
|
||||
if (prog->main_prog_cnt == 0) {
|
||||
text = bpf_object__find_prog_by_idx(obj, obj->efile.text_shndx);
|
||||
if (!text) {
|
||||
pr_warning("no .text section found yet relo into text exist\n");
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
new_cnt = prog->insns_cnt + text->insns_cnt;
|
||||
new_insn = realloc(prog->insns, new_cnt * sizeof(*insn));
|
||||
if (!new_insn) {
|
||||
pr_warning("oom in prog realloc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(new_insn + prog->insns_cnt, text->insns,
|
||||
text->insns_cnt * sizeof(*insn));
|
||||
prog->insns = new_insn;
|
||||
prog->main_prog_cnt = prog->insns_cnt;
|
||||
prog->insns_cnt = new_cnt;
|
||||
}
|
||||
insn = &prog->insns[relo->insn_idx];
|
||||
insn->imm += prog->main_prog_cnt - relo->insn_idx;
|
||||
pr_debug("added %zd insn from %s to prog %s\n",
|
||||
text->insns_cnt, text->section_name, prog->section_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
|
||||
{
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
if (!prog || !prog->reloc_desc)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < prog->nr_reloc; i++) {
|
||||
int insn_idx, map_idx;
|
||||
struct bpf_insn *insns = prog->insns;
|
||||
if (prog->reloc_desc[i].type == RELO_LD64) {
|
||||
struct bpf_insn *insns = prog->insns;
|
||||
int insn_idx, map_idx;
|
||||
|
||||
insn_idx = prog->reloc_desc[i].insn_idx;
|
||||
map_idx = prog->reloc_desc[i].map_idx;
|
||||
insn_idx = prog->reloc_desc[i].insn_idx;
|
||||
map_idx = prog->reloc_desc[i].map_idx;
|
||||
|
||||
if (insn_idx >= (int)prog->insns_cnt) {
|
||||
pr_warning("relocation out of range: '%s'\n",
|
||||
prog->section_name);
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
if (insn_idx >= (int)prog->insns_cnt) {
|
||||
pr_warning("relocation out of range: '%s'\n",
|
||||
prog->section_name);
|
||||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
|
||||
insns[insn_idx].imm = obj->maps[map_idx].fd;
|
||||
} else {
|
||||
err = bpf_program__reloc_text(prog, obj,
|
||||
&prog->reloc_desc[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
|
||||
insns[insn_idx].imm = obj->maps[map_idx].fd;
|
||||
}
|
||||
|
||||
zfree(&prog->reloc_desc);
|
||||
|
|
@ -1024,7 +1107,6 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
|
|||
Elf_Data *data = obj->efile.reloc[i].data;
|
||||
int idx = shdr->sh_info;
|
||||
struct bpf_program *prog;
|
||||
size_t nr_maps = obj->nr_maps;
|
||||
|
||||
if (shdr->sh_type != SHT_REL) {
|
||||
pr_warning("internal error at %d\n", __LINE__);
|
||||
|
|
@ -1038,11 +1120,9 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
|
|||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
|
||||
err = bpf_program__collect_reloc(prog, nr_maps,
|
||||
err = bpf_program__collect_reloc(prog,
|
||||
shdr, data,
|
||||
obj->efile.symbols,
|
||||
obj->efile.maps_shndx,
|
||||
obj->maps);
|
||||
obj);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1195,6 +1275,8 @@ bpf_object__load_progs(struct bpf_object *obj)
|
|||
int err;
|
||||
|
||||
for (i = 0; i < obj->nr_programs; i++) {
|
||||
if (obj->programs[i].idx == obj->efile.text_shndx)
|
||||
continue;
|
||||
err = bpf_program__load(&obj->programs[i],
|
||||
obj->license,
|
||||
obj->kern_version);
|
||||
|
|
@ -1721,6 +1803,45 @@ BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
|
|||
BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
|
||||
BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
|
||||
|
||||
#define BPF_PROG_SEC(string, type) { string, sizeof(string) - 1, type }
|
||||
static const struct {
|
||||
const char *sec;
|
||||
size_t len;
|
||||
enum bpf_prog_type prog_type;
|
||||
} section_names[] = {
|
||||
BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER),
|
||||
BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE),
|
||||
BPF_PROG_SEC("kretprobe/", BPF_PROG_TYPE_KPROBE),
|
||||
BPF_PROG_SEC("tracepoint/", BPF_PROG_TYPE_TRACEPOINT),
|
||||
BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP),
|
||||
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
|
||||
BPF_PROG_SEC("cgroup/skb", BPF_PROG_TYPE_CGROUP_SKB),
|
||||
BPF_PROG_SEC("cgroup/sock", BPF_PROG_TYPE_CGROUP_SOCK),
|
||||
BPF_PROG_SEC("cgroup/dev", BPF_PROG_TYPE_CGROUP_DEVICE),
|
||||
BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS),
|
||||
BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB),
|
||||
};
|
||||
#undef BPF_PROG_SEC
|
||||
|
||||
static enum bpf_prog_type bpf_program__guess_type(struct bpf_program *prog)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!prog->section_name)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(section_names); i++)
|
||||
if (strncmp(prog->section_name, section_names[i].sec,
|
||||
section_names[i].len) == 0)
|
||||
return section_names[i].prog_type;
|
||||
|
||||
err:
|
||||
pr_warning("failed to guess program type based on section name %s\n",
|
||||
prog->section_name);
|
||||
|
||||
return BPF_PROG_TYPE_UNSPEC;
|
||||
}
|
||||
|
||||
int bpf_map__fd(struct bpf_map *map)
|
||||
{
|
||||
return map ? map->fd : -EINVAL;
|
||||
|
|
@ -1818,7 +1939,7 @@ long libbpf_get_error(const void *ptr)
|
|||
int bpf_prog_load(const char *file, enum bpf_prog_type type,
|
||||
struct bpf_object **pobj, int *prog_fd)
|
||||
{
|
||||
struct bpf_program *prog;
|
||||
struct bpf_program *prog, *first_prog = NULL;
|
||||
struct bpf_object *obj;
|
||||
int err;
|
||||
|
||||
|
|
@ -1826,13 +1947,30 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
|
|||
if (IS_ERR(obj))
|
||||
return -ENOENT;
|
||||
|
||||
prog = bpf_program__next(NULL, obj);
|
||||
if (!prog) {
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
/*
|
||||
* If type is not specified, try to guess it based on
|
||||
* section name.
|
||||
*/
|
||||
if (type == BPF_PROG_TYPE_UNSPEC) {
|
||||
type = bpf_program__guess_type(prog);
|
||||
if (type == BPF_PROG_TYPE_UNSPEC) {
|
||||
bpf_object__close(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
bpf_program__set_type(prog, type);
|
||||
if (prog->idx != obj->efile.text_shndx && !first_prog)
|
||||
first_prog = prog;
|
||||
}
|
||||
|
||||
if (!first_prog) {
|
||||
pr_warning("object file doesn't contain bpf program\n");
|
||||
bpf_object__close(obj);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
bpf_program__set_type(prog, type);
|
||||
err = bpf_object__load(obj);
|
||||
if (err) {
|
||||
bpf_object__close(obj);
|
||||
|
|
@ -1840,6 +1978,6 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
|
|||
}
|
||||
|
||||
*pobj = obj;
|
||||
*prog_fd = bpf_program__fd(prog);
|
||||
*prog_fd = bpf_program__fd(first_prog);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
|
|||
"__reiserfs_panic",
|
||||
"lbug_with_loc",
|
||||
"fortify_panic",
|
||||
"usercopy_abort",
|
||||
};
|
||||
|
||||
if (func->bind == STB_WEAK)
|
||||
|
|
@ -543,18 +544,14 @@ static int add_call_destinations(struct objtool_file *file)
|
|||
dest_off = insn->offset + insn->len + insn->immediate;
|
||||
insn->call_dest = find_symbol_by_offset(insn->sec,
|
||||
dest_off);
|
||||
/*
|
||||
* FIXME: Thanks to retpolines, it's now considered
|
||||
* normal for a function to call within itself. So
|
||||
* disable this warning for now.
|
||||
*/
|
||||
#if 0
|
||||
if (!insn->call_dest) {
|
||||
WARN_FUNC("can't find call dest symbol at offset 0x%lx",
|
||||
insn->sec, insn->offset, dest_off);
|
||||
|
||||
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.");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
} else if (rela->sym->type == STT_SECTION) {
|
||||
insn->call_dest = find_symbol_by_offset(rela->sym->sec,
|
||||
rela->addend+4);
|
||||
|
|
@ -598,7 +595,7 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
struct instruction *orig_insn,
|
||||
struct instruction **new_insn)
|
||||
{
|
||||
struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump;
|
||||
struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL;
|
||||
unsigned long dest_off;
|
||||
|
||||
last_orig_insn = NULL;
|
||||
|
|
@ -614,28 +611,30 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
last_orig_insn = insn;
|
||||
}
|
||||
|
||||
if (!next_insn_same_sec(file, last_orig_insn)) {
|
||||
WARN("%s: don't know how to handle alternatives at end of section",
|
||||
special_alt->orig_sec->name);
|
||||
return -1;
|
||||
}
|
||||
if (next_insn_same_sec(file, last_orig_insn)) {
|
||||
fake_jump = malloc(sizeof(*fake_jump));
|
||||
if (!fake_jump) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
memset(fake_jump, 0, sizeof(*fake_jump));
|
||||
INIT_LIST_HEAD(&fake_jump->alts);
|
||||
clear_insn_state(&fake_jump->state);
|
||||
|
||||
fake_jump = malloc(sizeof(*fake_jump));
|
||||
if (!fake_jump) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
fake_jump->sec = special_alt->new_sec;
|
||||
fake_jump->offset = -1;
|
||||
fake_jump->type = INSN_JUMP_UNCONDITIONAL;
|
||||
fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
|
||||
fake_jump->ignore = true;
|
||||
}
|
||||
memset(fake_jump, 0, sizeof(*fake_jump));
|
||||
INIT_LIST_HEAD(&fake_jump->alts);
|
||||
clear_insn_state(&fake_jump->state);
|
||||
|
||||
fake_jump->sec = special_alt->new_sec;
|
||||
fake_jump->offset = -1;
|
||||
fake_jump->type = INSN_JUMP_UNCONDITIONAL;
|
||||
fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
|
||||
fake_jump->ignore = true;
|
||||
|
||||
if (!special_alt->new_len) {
|
||||
if (!fake_jump) {
|
||||
WARN("%s: empty alternative at end of section",
|
||||
special_alt->orig_sec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*new_insn = fake_jump;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -648,6 +647,8 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
|
||||
last_new_insn = insn;
|
||||
|
||||
insn->ignore = orig_insn->ignore_alts;
|
||||
|
||||
if (insn->type != INSN_JUMP_CONDITIONAL &&
|
||||
insn->type != INSN_JUMP_UNCONDITIONAL)
|
||||
continue;
|
||||
|
|
@ -656,8 +657,14 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
continue;
|
||||
|
||||
dest_off = insn->offset + insn->len + insn->immediate;
|
||||
if (dest_off == special_alt->new_off + special_alt->new_len)
|
||||
if (dest_off == special_alt->new_off + special_alt->new_len) {
|
||||
if (!fake_jump) {
|
||||
WARN("%s: alternative jump to end of section",
|
||||
special_alt->orig_sec->name);
|
||||
return -1;
|
||||
}
|
||||
insn->jump_dest = fake_jump;
|
||||
}
|
||||
|
||||
if (!insn->jump_dest) {
|
||||
WARN_FUNC("can't find alternative jump destination",
|
||||
|
|
@ -672,7 +679,8 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
return -1;
|
||||
}
|
||||
|
||||
list_add(&fake_jump->list, &last_new_insn->list);
|
||||
if (fake_jump)
|
||||
list_add(&fake_jump->list, &last_new_insn->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -729,10 +737,6 @@ static int add_special_section_alts(struct objtool_file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* Ignore retpoline alternatives. */
|
||||
if (orig_insn->ignore_alts)
|
||||
continue;
|
||||
|
||||
new_insn = NULL;
|
||||
if (!special_alt->group || special_alt->new_len) {
|
||||
new_insn = find_insn(file, special_alt->new_sec,
|
||||
|
|
@ -1089,11 +1093,11 @@ static int decode_sections(struct objtool_file *file)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = add_call_destinations(file);
|
||||
ret = add_special_section_alts(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = add_special_section_alts(file);
|
||||
ret = add_call_destinations(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1720,10 +1724,12 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
|||
|
||||
insn->visited = true;
|
||||
|
||||
list_for_each_entry(alt, &insn->alts, list) {
|
||||
ret = validate_branch(file, alt->insn, state);
|
||||
if (ret)
|
||||
return 1;
|
||||
if (!insn->ignore_alts) {
|
||||
list_for_each_entry(alt, &insn->alts, list) {
|
||||
ret = validate_branch(file, alt->insn, state);
|
||||
if (ret)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (insn->type) {
|
||||
|
|
|
|||
|
|
@ -98,6 +98,11 @@ static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
|
|||
struct orc_entry *orc;
|
||||
struct rela *rela;
|
||||
|
||||
if (!insn_sec->sym) {
|
||||
WARN("missing symbol for section %s", insn_sec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* populate ORC data */
|
||||
orc = (struct orc_entry *)u_sec->data->d_buf + idx;
|
||||
memcpy(orc, o, sizeof(*orc));
|
||||
|
|
|
|||
|
|
@ -99,5 +99,6 @@ ifneq ($(silent),1)
|
|||
|
||||
QUIET_CLEAN = @printf ' CLEAN %s\n' $1;
|
||||
QUIET_INSTALL = @printf ' INSTALL %s\n' $1;
|
||||
QUIET_UNINST = @printf ' UNINST %s\n' $1;
|
||||
endif
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
|
|||
|
||||
nfit-y := $(ACPI_SRC)/core.o
|
||||
nfit-$(CONFIG_X86_MCE) += $(ACPI_SRC)/mce.o
|
||||
nfit-y += acpi_nfit_test.o
|
||||
nfit-y += config_check.o
|
||||
|
||||
nd_pmem-y := $(NVDIMM_SRC)/pmem.o
|
||||
nd_pmem-y += pmem-dax.o
|
||||
nd_pmem-y += pmem_test.o
|
||||
nd_pmem-y += config_check.o
|
||||
|
||||
nd_btt-y := $(NVDIMM_SRC)/btt.o
|
||||
|
|
@ -57,6 +59,7 @@ dax-y += config_check.o
|
|||
|
||||
device_dax-y := $(DAX_SRC)/device.o
|
||||
device_dax-y += dax-dev.o
|
||||
device_dax-y += device_dax_test.o
|
||||
device_dax-y += config_check.o
|
||||
|
||||
dax_pmem-y := $(DAX_SRC)/pmem.o
|
||||
|
|
@ -75,6 +78,7 @@ libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o
|
|||
libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o
|
||||
libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o
|
||||
libnvdimm-$(CONFIG_NVDIMM_DAX) += $(NVDIMM_SRC)/dax_devs.o
|
||||
libnvdimm-y += libnvdimm_test.o
|
||||
libnvdimm-y += config_check.o
|
||||
|
||||
obj-m += test/
|
||||
|
|
|
|||
8
tools/testing/nvdimm/acpi_nfit_test.c
Normal file
8
tools/testing/nvdimm/acpi_nfit_test.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include "watermark.h"
|
||||
|
||||
nfit_test_watermark(acpi_nfit);
|
||||
8
tools/testing/nvdimm/device_dax_test.c
Normal file
8
tools/testing/nvdimm/device_dax_test.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include "watermark.h"
|
||||
|
||||
nfit_test_watermark(device_dax);
|
||||
8
tools/testing/nvdimm/libnvdimm_test.c
Normal file
8
tools/testing/nvdimm/libnvdimm_test.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include "watermark.h"
|
||||
|
||||
nfit_test_watermark(libnvdimm);
|
||||
8
tools/testing/nvdimm/pmem_test.c
Normal file
8
tools/testing/nvdimm/pmem_test.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include "watermark.h"
|
||||
|
||||
nfit_test_watermark(pmem);
|
||||
|
|
@ -104,15 +104,14 @@ void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
|
|||
}
|
||||
EXPORT_SYMBOL(__wrap_devm_memremap);
|
||||
|
||||
void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
|
||||
struct percpu_ref *ref, struct vmem_altmap *altmap)
|
||||
void *__wrap_devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
|
||||
{
|
||||
resource_size_t offset = res->start;
|
||||
resource_size_t offset = pgmap->res.start;
|
||||
struct nfit_test_resource *nfit_res = get_nfit_res(offset);
|
||||
|
||||
if (nfit_res)
|
||||
return nfit_res->buf + offset - nfit_res->res.start;
|
||||
return devm_memremap_pages(dev, res, ref, altmap);
|
||||
return devm_memremap_pages(dev, pgmap);
|
||||
}
|
||||
EXPORT_SYMBOL(__wrap_devm_memremap_pages);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <nfit.h>
|
||||
#include <nd.h>
|
||||
#include "nfit_test.h"
|
||||
#include "../watermark.h"
|
||||
|
||||
/*
|
||||
* Generate an NFIT table to describe the following topology:
|
||||
|
|
@ -137,6 +138,14 @@ static u32 handle[] = {
|
|||
|
||||
static unsigned long dimm_fail_cmd_flags[NUM_DCR];
|
||||
|
||||
struct nfit_test_fw {
|
||||
enum intel_fw_update_state state;
|
||||
u32 context;
|
||||
u64 version;
|
||||
u32 size_received;
|
||||
u64 end_time;
|
||||
};
|
||||
|
||||
struct nfit_test {
|
||||
struct acpi_nfit_desc acpi_desc;
|
||||
struct platform_device pdev;
|
||||
|
|
@ -168,8 +177,11 @@ struct nfit_test {
|
|||
spinlock_t lock;
|
||||
} ars_state;
|
||||
struct device *dimm_dev[NUM_DCR];
|
||||
struct nd_intel_smart *smart;
|
||||
struct nd_intel_smart_threshold *smart_threshold;
|
||||
struct badrange badrange;
|
||||
struct work_struct work;
|
||||
struct nfit_test_fw *fw;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *nfit_wq;
|
||||
|
|
@ -181,6 +193,226 @@ static struct nfit_test *to_nfit_test(struct device *dev)
|
|||
return container_of(pdev, struct nfit_test, pdev);
|
||||
}
|
||||
|
||||
static int nd_intel_test_get_fw_info(struct nfit_test *t,
|
||||
struct nd_intel_fw_info *nd_cmd, unsigned int buf_len,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = &t->pdev.dev;
|
||||
struct nfit_test_fw *fw = &t->fw[idx];
|
||||
|
||||
dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p, buf_len: %u, idx: %d\n",
|
||||
__func__, t, nd_cmd, buf_len, idx);
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
nd_cmd->status = 0;
|
||||
nd_cmd->storage_size = INTEL_FW_STORAGE_SIZE;
|
||||
nd_cmd->max_send_len = INTEL_FW_MAX_SEND_LEN;
|
||||
nd_cmd->query_interval = INTEL_FW_QUERY_INTERVAL;
|
||||
nd_cmd->max_query_time = INTEL_FW_QUERY_MAX_TIME;
|
||||
nd_cmd->update_cap = 0;
|
||||
nd_cmd->fis_version = INTEL_FW_FIS_VERSION;
|
||||
nd_cmd->run_version = 0;
|
||||
nd_cmd->updated_version = fw->version;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_start_update(struct nfit_test *t,
|
||||
struct nd_intel_fw_start *nd_cmd, unsigned int buf_len,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = &t->pdev.dev;
|
||||
struct nfit_test_fw *fw = &t->fw[idx];
|
||||
|
||||
dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
|
||||
__func__, t, nd_cmd, buf_len, idx);
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
if (fw->state != FW_STATE_NEW) {
|
||||
/* extended status, FW update in progress */
|
||||
nd_cmd->status = 0x10007;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fw->state = FW_STATE_IN_PROGRESS;
|
||||
fw->context++;
|
||||
fw->size_received = 0;
|
||||
nd_cmd->status = 0;
|
||||
nd_cmd->context = fw->context;
|
||||
|
||||
dev_dbg(dev, "%s: context issued: %#x\n", __func__, nd_cmd->context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_send_data(struct nfit_test *t,
|
||||
struct nd_intel_fw_send_data *nd_cmd, unsigned int buf_len,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = &t->pdev.dev;
|
||||
struct nfit_test_fw *fw = &t->fw[idx];
|
||||
u32 *status = (u32 *)&nd_cmd->data[nd_cmd->length];
|
||||
|
||||
dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
|
||||
__func__, t, nd_cmd, buf_len, idx);
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
dev_dbg(dev, "%s: cmd->status: %#x\n", __func__, *status);
|
||||
dev_dbg(dev, "%s: cmd->data[0]: %#x\n", __func__, nd_cmd->data[0]);
|
||||
dev_dbg(dev, "%s: cmd->data[%u]: %#x\n", __func__, nd_cmd->length-1,
|
||||
nd_cmd->data[nd_cmd->length-1]);
|
||||
|
||||
if (fw->state != FW_STATE_IN_PROGRESS) {
|
||||
dev_dbg(dev, "%s: not in IN_PROGRESS state\n", __func__);
|
||||
*status = 0x5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nd_cmd->context != fw->context) {
|
||||
dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n",
|
||||
__func__, nd_cmd->context, fw->context);
|
||||
*status = 0x10007;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check offset + len > size of fw storage
|
||||
* check length is > max send length
|
||||
*/
|
||||
if (nd_cmd->offset + nd_cmd->length > INTEL_FW_STORAGE_SIZE ||
|
||||
nd_cmd->length > INTEL_FW_MAX_SEND_LEN) {
|
||||
*status = 0x3;
|
||||
dev_dbg(dev, "%s: buffer boundary violation\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fw->size_received += nd_cmd->length;
|
||||
dev_dbg(dev, "%s: copying %u bytes, %u bytes so far\n",
|
||||
__func__, nd_cmd->length, fw->size_received);
|
||||
*status = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_finish_fw(struct nfit_test *t,
|
||||
struct nd_intel_fw_finish_update *nd_cmd,
|
||||
unsigned int buf_len, int idx)
|
||||
{
|
||||
struct device *dev = &t->pdev.dev;
|
||||
struct nfit_test_fw *fw = &t->fw[idx];
|
||||
|
||||
dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
|
||||
__func__, t, nd_cmd, buf_len, idx);
|
||||
|
||||
if (fw->state == FW_STATE_UPDATED) {
|
||||
/* update already done, need cold boot */
|
||||
nd_cmd->status = 0x20007;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: context: %#x ctrl_flags: %#x\n",
|
||||
__func__, nd_cmd->context, nd_cmd->ctrl_flags);
|
||||
|
||||
switch (nd_cmd->ctrl_flags) {
|
||||
case 0: /* finish */
|
||||
if (nd_cmd->context != fw->context) {
|
||||
dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n",
|
||||
__func__, nd_cmd->context,
|
||||
fw->context);
|
||||
nd_cmd->status = 0x10007;
|
||||
return 0;
|
||||
}
|
||||
nd_cmd->status = 0;
|
||||
fw->state = FW_STATE_VERIFY;
|
||||
/* set 1 second of time for firmware "update" */
|
||||
fw->end_time = jiffies + HZ;
|
||||
break;
|
||||
|
||||
case 1: /* abort */
|
||||
fw->size_received = 0;
|
||||
/* successfully aborted status */
|
||||
nd_cmd->status = 0x40007;
|
||||
fw->state = FW_STATE_NEW;
|
||||
dev_dbg(dev, "%s: abort successful\n", __func__);
|
||||
break;
|
||||
|
||||
default: /* bad control flag */
|
||||
dev_warn(dev, "%s: unknown control flag: %#x\n",
|
||||
__func__, nd_cmd->ctrl_flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_finish_query(struct nfit_test *t,
|
||||
struct nd_intel_fw_finish_query *nd_cmd,
|
||||
unsigned int buf_len, int idx)
|
||||
{
|
||||
struct device *dev = &t->pdev.dev;
|
||||
struct nfit_test_fw *fw = &t->fw[idx];
|
||||
|
||||
dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
|
||||
__func__, t, nd_cmd, buf_len, idx);
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
if (nd_cmd->context != fw->context) {
|
||||
dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n",
|
||||
__func__, nd_cmd->context, fw->context);
|
||||
nd_cmd->status = 0x10007;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s context: %#x\n", __func__, nd_cmd->context);
|
||||
|
||||
switch (fw->state) {
|
||||
case FW_STATE_NEW:
|
||||
nd_cmd->updated_fw_rev = 0;
|
||||
nd_cmd->status = 0;
|
||||
dev_dbg(dev, "%s: new state\n", __func__);
|
||||
break;
|
||||
|
||||
case FW_STATE_IN_PROGRESS:
|
||||
/* sequencing error */
|
||||
nd_cmd->status = 0x40007;
|
||||
nd_cmd->updated_fw_rev = 0;
|
||||
dev_dbg(dev, "%s: sequence error\n", __func__);
|
||||
break;
|
||||
|
||||
case FW_STATE_VERIFY:
|
||||
if (time_is_after_jiffies64(fw->end_time)) {
|
||||
nd_cmd->updated_fw_rev = 0;
|
||||
nd_cmd->status = 0x20007;
|
||||
dev_dbg(dev, "%s: still verifying\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: transition out verify\n", __func__);
|
||||
fw->state = FW_STATE_UPDATED;
|
||||
/* we are going to fall through if it's "done" */
|
||||
case FW_STATE_UPDATED:
|
||||
nd_cmd->status = 0;
|
||||
/* bogus test version */
|
||||
fw->version = nd_cmd->updated_fw_rev =
|
||||
INTEL_FW_FAKE_VERSION;
|
||||
dev_dbg(dev, "%s: updated\n", __func__);
|
||||
break;
|
||||
|
||||
default: /* we should never get here */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
|
|
@ -440,39 +672,66 @@ static int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_smart(struct nd_cmd_smart *smart, unsigned int buf_len)
|
||||
static int nfit_test_cmd_smart(struct nd_intel_smart *smart, unsigned int buf_len,
|
||||
struct nd_intel_smart *smart_data)
|
||||
{
|
||||
static const struct nd_smart_payload smart_data = {
|
||||
.flags = ND_SMART_HEALTH_VALID | ND_SMART_TEMP_VALID
|
||||
| ND_SMART_SPARES_VALID | ND_SMART_ALARM_VALID
|
||||
| ND_SMART_USED_VALID | ND_SMART_SHUTDOWN_VALID,
|
||||
.health = ND_SMART_NON_CRITICAL_HEALTH,
|
||||
.temperature = 23 * 16,
|
||||
.spares = 75,
|
||||
.alarm_flags = ND_SMART_SPARE_TRIP | ND_SMART_TEMP_TRIP,
|
||||
.life_used = 5,
|
||||
.shutdown_state = 0,
|
||||
.vendor_size = 0,
|
||||
};
|
||||
|
||||
if (buf_len < sizeof(*smart))
|
||||
return -EINVAL;
|
||||
memcpy(smart->data, &smart_data, sizeof(smart_data));
|
||||
memcpy(smart, smart_data, sizeof(*smart));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_smart_threshold(struct nd_cmd_smart_threshold *smart_t,
|
||||
unsigned int buf_len)
|
||||
static int nfit_test_cmd_smart_threshold(
|
||||
struct nd_intel_smart_threshold *out,
|
||||
unsigned int buf_len,
|
||||
struct nd_intel_smart_threshold *smart_t)
|
||||
{
|
||||
static const struct nd_smart_threshold_payload smart_t_data = {
|
||||
.alarm_control = ND_SMART_SPARE_TRIP | ND_SMART_TEMP_TRIP,
|
||||
.temperature = 40 * 16,
|
||||
.spares = 5,
|
||||
};
|
||||
|
||||
if (buf_len < sizeof(*smart_t))
|
||||
return -EINVAL;
|
||||
memcpy(smart_t->data, &smart_t_data, sizeof(smart_t_data));
|
||||
memcpy(out, smart_t, sizeof(*smart_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smart_notify(struct device *bus_dev,
|
||||
struct device *dimm_dev, struct nd_intel_smart *smart,
|
||||
struct nd_intel_smart_threshold *thresh)
|
||||
{
|
||||
dev_dbg(dimm_dev, "%s: alarm: %#x spares: %d (%d) mtemp: %d (%d) ctemp: %d (%d)\n",
|
||||
__func__, thresh->alarm_control, thresh->spares,
|
||||
smart->spares, thresh->media_temperature,
|
||||
smart->media_temperature, thresh->ctrl_temperature,
|
||||
smart->ctrl_temperature);
|
||||
if (((thresh->alarm_control & ND_INTEL_SMART_SPARE_TRIP)
|
||||
&& smart->spares
|
||||
<= thresh->spares)
|
||||
|| ((thresh->alarm_control & ND_INTEL_SMART_TEMP_TRIP)
|
||||
&& smart->media_temperature
|
||||
>= thresh->media_temperature)
|
||||
|| ((thresh->alarm_control & ND_INTEL_SMART_CTEMP_TRIP)
|
||||
&& smart->ctrl_temperature
|
||||
>= thresh->ctrl_temperature)) {
|
||||
device_lock(bus_dev);
|
||||
__acpi_nvdimm_notify(dimm_dev, 0x81);
|
||||
device_unlock(bus_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_smart_set_threshold(
|
||||
struct nd_intel_smart_set_threshold *in,
|
||||
unsigned int buf_len,
|
||||
struct nd_intel_smart_threshold *thresh,
|
||||
struct nd_intel_smart *smart,
|
||||
struct device *bus_dev, struct device *dimm_dev)
|
||||
{
|
||||
unsigned int size;
|
||||
|
||||
size = sizeof(*in) - 4;
|
||||
if (buf_len < size)
|
||||
return -EINVAL;
|
||||
memcpy(thresh->data, in, size);
|
||||
in->status = 0;
|
||||
smart_notify(bus_dev, dimm_dev, smart, thresh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -563,6 +822,52 @@ static int nfit_test_cmd_ars_inject_status(struct nfit_test *t,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nd_intel_test_cmd_set_lss_status(struct nfit_test *t,
|
||||
struct nd_intel_lss *nd_cmd, unsigned int buf_len)
|
||||
{
|
||||
struct device *dev = &t->pdev.dev;
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
switch (nd_cmd->enable) {
|
||||
case 0:
|
||||
nd_cmd->status = 0;
|
||||
dev_dbg(dev, "%s: Latch System Shutdown Status disabled\n",
|
||||
__func__);
|
||||
break;
|
||||
case 1:
|
||||
nd_cmd->status = 0;
|
||||
dev_dbg(dev, "%s: Latch System Shutdown Status enabled\n",
|
||||
__func__);
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Unknown enable value: %#x\n", nd_cmd->enable);
|
||||
nd_cmd->status = 0x3;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* lookup per-dimm data */
|
||||
for (i = 0; i < ARRAY_SIZE(handle); i++)
|
||||
if (__to_nfit_memdev(nfit_mem)->device_handle == handle[i])
|
||||
break;
|
||||
if (i >= ARRAY_SIZE(handle))
|
||||
return -ENXIO;
|
||||
|
||||
if ((1 << func) & dimm_fail_cmd_flags[i])
|
||||
return -EIO;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
||||
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
|
||||
unsigned int buf_len, int *cmd_rc)
|
||||
|
|
@ -591,22 +896,57 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
func = call_pkg->nd_command;
|
||||
if (call_pkg->nd_family != nfit_mem->family)
|
||||
return -ENOTTY;
|
||||
|
||||
i = get_dimm(nfit_mem, func);
|
||||
if (i < 0)
|
||||
return i;
|
||||
|
||||
switch (func) {
|
||||
case ND_INTEL_ENABLE_LSS_STATUS:
|
||||
return nd_intel_test_cmd_set_lss_status(t,
|
||||
buf, buf_len);
|
||||
case ND_INTEL_FW_GET_INFO:
|
||||
return nd_intel_test_get_fw_info(t, buf,
|
||||
buf_len, i - t->dcr_idx);
|
||||
case ND_INTEL_FW_START_UPDATE:
|
||||
return nd_intel_test_start_update(t, buf,
|
||||
buf_len, i - t->dcr_idx);
|
||||
case ND_INTEL_FW_SEND_DATA:
|
||||
return nd_intel_test_send_data(t, buf,
|
||||
buf_len, i - t->dcr_idx);
|
||||
case ND_INTEL_FW_FINISH_UPDATE:
|
||||
return nd_intel_test_finish_fw(t, buf,
|
||||
buf_len, i - t->dcr_idx);
|
||||
case ND_INTEL_FW_FINISH_QUERY:
|
||||
return nd_intel_test_finish_query(t, buf,
|
||||
buf_len, i - t->dcr_idx);
|
||||
case ND_INTEL_SMART:
|
||||
return nfit_test_cmd_smart(buf, buf_len,
|
||||
&t->smart[i - t->dcr_idx]);
|
||||
case ND_INTEL_SMART_THRESHOLD:
|
||||
return nfit_test_cmd_smart_threshold(buf,
|
||||
buf_len,
|
||||
&t->smart_threshold[i -
|
||||
t->dcr_idx]);
|
||||
case ND_INTEL_SMART_SET_THRESHOLD:
|
||||
return nfit_test_cmd_smart_set_threshold(buf,
|
||||
buf_len,
|
||||
&t->smart_threshold[i -
|
||||
t->dcr_idx],
|
||||
&t->smart[i - t->dcr_idx],
|
||||
&t->pdev.dev, t->dimm_dev[i]);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_bit(cmd, &cmd_mask)
|
||||
|| !test_bit(func, &nfit_mem->dsm_mask))
|
||||
return -ENOTTY;
|
||||
|
||||
/* lookup label space for the given dimm */
|
||||
for (i = 0; i < ARRAY_SIZE(handle); i++)
|
||||
if (__to_nfit_memdev(nfit_mem)->device_handle ==
|
||||
handle[i])
|
||||
break;
|
||||
if (i >= ARRAY_SIZE(handle))
|
||||
return -ENXIO;
|
||||
|
||||
if ((1 << func) & dimm_fail_cmd_flags[i])
|
||||
return -EIO;
|
||||
i = get_dimm(nfit_mem, func);
|
||||
if (i < 0)
|
||||
return i;
|
||||
|
||||
switch (func) {
|
||||
case ND_CMD_GET_CONFIG_SIZE:
|
||||
|
|
@ -620,15 +960,6 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
rc = nfit_test_cmd_set_config_data(buf, buf_len,
|
||||
t->label[i - t->dcr_idx]);
|
||||
break;
|
||||
case ND_CMD_SMART:
|
||||
rc = nfit_test_cmd_smart(buf, buf_len);
|
||||
break;
|
||||
case ND_CMD_SMART_THRESHOLD:
|
||||
rc = nfit_test_cmd_smart_threshold(buf, buf_len);
|
||||
device_lock(&t->pdev.dev);
|
||||
__acpi_nvdimm_notify(t->dimm_dev[i], 0x81);
|
||||
device_unlock(&t->pdev.dev);
|
||||
break;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
|
@ -872,6 +1203,44 @@ static const struct attribute_group *nfit_test_dimm_attribute_groups[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static void smart_init(struct nfit_test *t)
|
||||
{
|
||||
int i;
|
||||
const struct nd_intel_smart_threshold smart_t_data = {
|
||||
.alarm_control = ND_INTEL_SMART_SPARE_TRIP
|
||||
| ND_INTEL_SMART_TEMP_TRIP,
|
||||
.media_temperature = 40 * 16,
|
||||
.ctrl_temperature = 30 * 16,
|
||||
.spares = 5,
|
||||
};
|
||||
const struct nd_intel_smart smart_data = {
|
||||
.flags = ND_INTEL_SMART_HEALTH_VALID
|
||||
| ND_INTEL_SMART_SPARES_VALID
|
||||
| ND_INTEL_SMART_ALARM_VALID
|
||||
| ND_INTEL_SMART_USED_VALID
|
||||
| ND_INTEL_SMART_SHUTDOWN_VALID
|
||||
| ND_INTEL_SMART_MTEMP_VALID,
|
||||
.health = ND_INTEL_SMART_NON_CRITICAL_HEALTH,
|
||||
.media_temperature = 23 * 16,
|
||||
.ctrl_temperature = 30 * 16,
|
||||
.pmic_temperature = 40 * 16,
|
||||
.spares = 75,
|
||||
.alarm_flags = ND_INTEL_SMART_SPARE_TRIP
|
||||
| ND_INTEL_SMART_TEMP_TRIP,
|
||||
.ait_status = 1,
|
||||
.life_used = 5,
|
||||
.shutdown_state = 0,
|
||||
.vendor_size = 0,
|
||||
.shutdown_count = 100,
|
||||
};
|
||||
|
||||
for (i = 0; i < t->num_dcr; i++) {
|
||||
memcpy(&t->smart[i], &smart_data, sizeof(smart_data));
|
||||
memcpy(&t->smart_threshold[i], &smart_t_data,
|
||||
sizeof(smart_t_data));
|
||||
}
|
||||
}
|
||||
|
||||
static int nfit_test0_alloc(struct nfit_test *t)
|
||||
{
|
||||
size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA
|
||||
|
|
@ -881,7 +1250,8 @@ static int nfit_test0_alloc(struct nfit_test *t)
|
|||
window_size) * NUM_DCR
|
||||
+ sizeof(struct acpi_nfit_data_region) * NUM_BDW
|
||||
+ (sizeof(struct acpi_nfit_flush_address)
|
||||
+ sizeof(u64) * NUM_HINTS) * NUM_DCR;
|
||||
+ sizeof(u64) * NUM_HINTS) * NUM_DCR
|
||||
+ sizeof(struct acpi_nfit_capabilities);
|
||||
int i;
|
||||
|
||||
t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma);
|
||||
|
|
@ -939,6 +1309,7 @@ static int nfit_test0_alloc(struct nfit_test *t)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
smart_init(t);
|
||||
return ars_state_init(&t->pdev.dev, &t->ars_state);
|
||||
}
|
||||
|
||||
|
|
@ -969,6 +1340,7 @@ static int nfit_test1_alloc(struct nfit_test *t)
|
|||
if (!t->spa_set[1])
|
||||
return -ENOMEM;
|
||||
|
||||
smart_init(t);
|
||||
return ars_state_init(&t->pdev.dev, &t->ars_state);
|
||||
}
|
||||
|
||||
|
|
@ -993,6 +1365,7 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||
struct acpi_nfit_control_region *dcr;
|
||||
struct acpi_nfit_data_region *bdw;
|
||||
struct acpi_nfit_flush_address *flush;
|
||||
struct acpi_nfit_capabilities *pcap;
|
||||
unsigned int offset, i;
|
||||
|
||||
/*
|
||||
|
|
@ -1500,8 +1873,16 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||
for (i = 0; i < NUM_HINTS; i++)
|
||||
flush->hint_address[i] = t->flush_dma[3] + i * sizeof(u64);
|
||||
|
||||
/* platform capabilities */
|
||||
pcap = nfit_buf + offset + flush_hint_size * 4;
|
||||
pcap->header.type = ACPI_NFIT_TYPE_CAPABILITIES;
|
||||
pcap->header.length = sizeof(*pcap);
|
||||
pcap->highest_capability = 1;
|
||||
pcap->capabilities = ACPI_NFIT_CAPABILITY_CACHE_FLUSH |
|
||||
ACPI_NFIT_CAPABILITY_MEM_FLUSH;
|
||||
|
||||
if (t->setup_hotplug) {
|
||||
offset = offset + flush_hint_size * 4;
|
||||
offset = offset + flush_hint_size * 4 + sizeof(*pcap);
|
||||
/* dcr-descriptor4: blk */
|
||||
dcr = nfit_buf + offset;
|
||||
dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
|
||||
|
|
@ -1642,17 +2023,24 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||
set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_CMD_SMART, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_SMART, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_SMART_SET_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_CALL, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(NFIT_CMD_TRANSLATE_SPA, &acpi_desc->bus_nfit_cmd_force_en);
|
||||
set_bit(NFIT_CMD_ARS_INJECT_SET, &acpi_desc->bus_nfit_cmd_force_en);
|
||||
set_bit(NFIT_CMD_ARS_INJECT_CLEAR, &acpi_desc->bus_nfit_cmd_force_en);
|
||||
set_bit(NFIT_CMD_ARS_INJECT_GET, &acpi_desc->bus_nfit_cmd_force_en);
|
||||
set_bit(ND_INTEL_FW_GET_INFO, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_FW_START_UPDATE, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_FW_SEND_DATA, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_FW_FINISH_UPDATE, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_FW_FINISH_QUERY, &acpi_desc->dimm_cmd_force_en);
|
||||
set_bit(ND_INTEL_ENABLE_LSS_STATUS, &acpi_desc->dimm_cmd_force_en);
|
||||
}
|
||||
|
||||
static void nfit_test1_setup(struct nfit_test *t)
|
||||
|
|
@ -1750,6 +2138,7 @@ static void nfit_test1_setup(struct nfit_test *t)
|
|||
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
|
||||
set_bit(ND_INTEL_ENABLE_LSS_STATUS, &acpi_desc->dimm_cmd_force_en);
|
||||
}
|
||||
|
||||
static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,
|
||||
|
|
@ -2054,10 +2443,18 @@ static int nfit_test_probe(struct platform_device *pdev)
|
|||
sizeof(struct nfit_test_dcr *), GFP_KERNEL);
|
||||
nfit_test->dcr_dma = devm_kcalloc(dev, num,
|
||||
sizeof(dma_addr_t), GFP_KERNEL);
|
||||
nfit_test->smart = devm_kcalloc(dev, num,
|
||||
sizeof(struct nd_intel_smart), GFP_KERNEL);
|
||||
nfit_test->smart_threshold = devm_kcalloc(dev, num,
|
||||
sizeof(struct nd_intel_smart_threshold),
|
||||
GFP_KERNEL);
|
||||
nfit_test->fw = devm_kcalloc(dev, num,
|
||||
sizeof(struct nfit_test_fw), GFP_KERNEL);
|
||||
if (nfit_test->dimm && nfit_test->dimm_dma && nfit_test->label
|
||||
&& nfit_test->label_dma && nfit_test->dcr
|
||||
&& nfit_test->dcr_dma && nfit_test->flush
|
||||
&& nfit_test->flush_dma)
|
||||
&& nfit_test->flush_dma
|
||||
&& nfit_test->fw)
|
||||
/* pass */;
|
||||
else
|
||||
return -ENOMEM;
|
||||
|
|
@ -2159,6 +2556,11 @@ static __init int nfit_test_init(void)
|
|||
{
|
||||
int rc, i;
|
||||
|
||||
pmem_test();
|
||||
libnvdimm_test();
|
||||
acpi_nfit_test();
|
||||
device_dax_test();
|
||||
|
||||
nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm);
|
||||
|
||||
nfit_wq = create_singlethread_workqueue("nfit");
|
||||
|
|
|
|||
|
|
@ -84,6 +84,140 @@ struct nd_cmd_ars_err_inj_stat {
|
|||
} __packed record[0];
|
||||
} __packed;
|
||||
|
||||
#define ND_INTEL_SMART 1
|
||||
#define ND_INTEL_SMART_THRESHOLD 2
|
||||
#define ND_INTEL_ENABLE_LSS_STATUS 10
|
||||
#define ND_INTEL_FW_GET_INFO 12
|
||||
#define ND_INTEL_FW_START_UPDATE 13
|
||||
#define ND_INTEL_FW_SEND_DATA 14
|
||||
#define ND_INTEL_FW_FINISH_UPDATE 15
|
||||
#define ND_INTEL_FW_FINISH_QUERY 16
|
||||
#define ND_INTEL_SMART_SET_THRESHOLD 17
|
||||
|
||||
#define ND_INTEL_SMART_HEALTH_VALID (1 << 0)
|
||||
#define ND_INTEL_SMART_SPARES_VALID (1 << 1)
|
||||
#define ND_INTEL_SMART_USED_VALID (1 << 2)
|
||||
#define ND_INTEL_SMART_MTEMP_VALID (1 << 3)
|
||||
#define ND_INTEL_SMART_CTEMP_VALID (1 << 4)
|
||||
#define ND_INTEL_SMART_SHUTDOWN_COUNT_VALID (1 << 5)
|
||||
#define ND_INTEL_SMART_AIT_STATUS_VALID (1 << 6)
|
||||
#define ND_INTEL_SMART_PTEMP_VALID (1 << 7)
|
||||
#define ND_INTEL_SMART_ALARM_VALID (1 << 9)
|
||||
#define ND_INTEL_SMART_SHUTDOWN_VALID (1 << 10)
|
||||
#define ND_INTEL_SMART_VENDOR_VALID (1 << 11)
|
||||
#define ND_INTEL_SMART_SPARE_TRIP (1 << 0)
|
||||
#define ND_INTEL_SMART_TEMP_TRIP (1 << 1)
|
||||
#define ND_INTEL_SMART_CTEMP_TRIP (1 << 2)
|
||||
#define ND_INTEL_SMART_NON_CRITICAL_HEALTH (1 << 0)
|
||||
#define ND_INTEL_SMART_CRITICAL_HEALTH (1 << 1)
|
||||
#define ND_INTEL_SMART_FATAL_HEALTH (1 << 2)
|
||||
|
||||
struct nd_intel_smart {
|
||||
__u32 status;
|
||||
union {
|
||||
struct {
|
||||
__u32 flags;
|
||||
__u8 reserved0[4];
|
||||
__u8 health;
|
||||
__u8 spares;
|
||||
__u8 life_used;
|
||||
__u8 alarm_flags;
|
||||
__u16 media_temperature;
|
||||
__u16 ctrl_temperature;
|
||||
__u32 shutdown_count;
|
||||
__u8 ait_status;
|
||||
__u16 pmic_temperature;
|
||||
__u8 reserved1[8];
|
||||
__u8 shutdown_state;
|
||||
__u32 vendor_size;
|
||||
__u8 vendor_data[92];
|
||||
} __packed;
|
||||
__u8 data[128];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct nd_intel_smart_threshold {
|
||||
__u32 status;
|
||||
union {
|
||||
struct {
|
||||
__u16 alarm_control;
|
||||
__u8 spares;
|
||||
__u16 media_temperature;
|
||||
__u16 ctrl_temperature;
|
||||
__u8 reserved[1];
|
||||
} __packed;
|
||||
__u8 data[8];
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct nd_intel_smart_set_threshold {
|
||||
__u16 alarm_control;
|
||||
__u8 spares;
|
||||
__u16 media_temperature;
|
||||
__u16 ctrl_temperature;
|
||||
__u32 status;
|
||||
} __packed;
|
||||
|
||||
#define INTEL_FW_STORAGE_SIZE 0x100000
|
||||
#define INTEL_FW_MAX_SEND_LEN 0xFFEC
|
||||
#define INTEL_FW_QUERY_INTERVAL 250000
|
||||
#define INTEL_FW_QUERY_MAX_TIME 3000000
|
||||
#define INTEL_FW_FIS_VERSION 0x0105
|
||||
#define INTEL_FW_FAKE_VERSION 0xffffffffabcd
|
||||
|
||||
enum intel_fw_update_state {
|
||||
FW_STATE_NEW = 0,
|
||||
FW_STATE_IN_PROGRESS,
|
||||
FW_STATE_VERIFY,
|
||||
FW_STATE_UPDATED,
|
||||
};
|
||||
|
||||
struct nd_intel_fw_info {
|
||||
__u32 status;
|
||||
__u32 storage_size;
|
||||
__u32 max_send_len;
|
||||
__u32 query_interval;
|
||||
__u32 max_query_time;
|
||||
__u8 update_cap;
|
||||
__u8 reserved[3];
|
||||
__u32 fis_version;
|
||||
__u64 run_version;
|
||||
__u64 updated_version;
|
||||
} __packed;
|
||||
|
||||
struct nd_intel_fw_start {
|
||||
__u32 status;
|
||||
__u32 context;
|
||||
} __packed;
|
||||
|
||||
/* this one has the output first because the variable input data size */
|
||||
struct nd_intel_fw_send_data {
|
||||
__u32 context;
|
||||
__u32 offset;
|
||||
__u32 length;
|
||||
__u8 data[0];
|
||||
/* this field is not declared due ot variable data from input */
|
||||
/* __u32 status; */
|
||||
} __packed;
|
||||
|
||||
struct nd_intel_fw_finish_update {
|
||||
__u8 ctrl_flags;
|
||||
__u8 reserved[3];
|
||||
__u32 context;
|
||||
__u32 status;
|
||||
} __packed;
|
||||
|
||||
struct nd_intel_fw_finish_query {
|
||||
__u32 context;
|
||||
__u32 status;
|
||||
__u64 updated_fw_rev;
|
||||
} __packed;
|
||||
|
||||
struct nd_intel_lss {
|
||||
__u8 enable;
|
||||
__u32 status;
|
||||
} __packed;
|
||||
|
||||
union acpi_object;
|
||||
typedef void *acpi_handle;
|
||||
|
||||
|
|
|
|||
21
tools/testing/nvdimm/watermark.h
Normal file
21
tools/testing/nvdimm/watermark.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright(c) 2018 Intel Corporation. All rights reserved.
|
||||
#ifndef _TEST_NVDIMM_WATERMARK_H_
|
||||
#define _TEST_NVDIMM_WATERMARK_H_
|
||||
int pmem_test(void);
|
||||
int libnvdimm_test(void);
|
||||
int acpi_nfit_test(void);
|
||||
int device_dax_test(void);
|
||||
|
||||
/*
|
||||
* dummy routine for nfit_test to validate it is linking to the properly
|
||||
* mocked module and not the standard one from the base tree.
|
||||
*/
|
||||
#define nfit_test_watermark(x) \
|
||||
int x##_test(void) \
|
||||
{ \
|
||||
pr_debug("%s for nfit_test\n", KBUILD_MODNAME); \
|
||||
return 0; \
|
||||
} \
|
||||
EXPORT_SYMBOL(x##_test)
|
||||
#endif /* _TEST_NVDIMM_WATERMARK_H_ */
|
||||
|
|
@ -116,8 +116,15 @@ ifdef INSTALL_PATH
|
|||
|
||||
@# Ask all targets to emit their test scripts
|
||||
echo "#!/bin/sh" > $(ALL_SCRIPT)
|
||||
echo "cd \$$(dirname \$$0)" >> $(ALL_SCRIPT)
|
||||
echo "BASE_DIR=\$$(realpath \$$(dirname \$$0))" >> $(ALL_SCRIPT)
|
||||
echo "cd \$$BASE_DIR" >> $(ALL_SCRIPT)
|
||||
echo "ROOT=\$$PWD" >> $(ALL_SCRIPT)
|
||||
echo "if [ \"\$$1\" = \"--summary\" ]; then" >> $(ALL_SCRIPT)
|
||||
echo " OUTPUT=\$$BASE_DIR/output.log" >> $(ALL_SCRIPT)
|
||||
echo " cat /dev/null > \$$OUTPUT" >> $(ALL_SCRIPT)
|
||||
echo "else" >> $(ALL_SCRIPT)
|
||||
echo " OUTPUT=/dev/stdout" >> $(ALL_SCRIPT)
|
||||
echo "fi" >> $(ALL_SCRIPT)
|
||||
|
||||
for TARGET in $(TARGETS); do \
|
||||
BUILD_TARGET=$$BUILD/$$TARGET; \
|
||||
|
|
|
|||
7
tools/testing/selftests/bpf/.gitignore
vendored
7
tools/testing/selftests/bpf/.gitignore
vendored
|
|
@ -3,3 +3,10 @@ test_maps
|
|||
test_lru_map
|
||||
test_lpm_map
|
||||
test_tag
|
||||
FEATURE-DUMP.libbpf
|
||||
fixdep
|
||||
test_align
|
||||
test_dev_cgroup
|
||||
test_progs
|
||||
test_verifier_log
|
||||
feature
|
||||
|
|
|
|||
|
|
@ -11,16 +11,19 @@ ifneq ($(wildcard $(GENHDR)),)
|
|||
endif
|
||||
|
||||
CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
|
||||
LDLIBS += -lcap -lelf -lrt
|
||||
LDLIBS += -lcap -lelf -lrt -lpthread
|
||||
|
||||
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_align test_verifier_log test_dev_cgroup test_tcpbpf_user
|
||||
|
||||
TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
|
||||
test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
|
||||
sockmap_verdict_prog.o dev_cgroup.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
|
||||
|
||||
TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh
|
||||
TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \
|
||||
test_offload.py
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
|
|
@ -49,8 +52,13 @@ else
|
|||
CPU ?= generic
|
||||
endif
|
||||
|
||||
CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \
|
||||
-Wno-compare-distinct-pointer-types
|
||||
|
||||
$(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline
|
||||
$(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline
|
||||
|
||||
%.o: %.c
|
||||
$(CLANG) -I. -I./include/uapi -I../../../include/uapi \
|
||||
-Wno-compare-distinct-pointer-types \
|
||||
$(CLANG) $(CLANG_FLAGS) \
|
||||
-O2 -target bpf -emit-llvm -c $< -o - | \
|
||||
$(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,
|
|||
static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval,
|
||||
int optlen) =
|
||||
(void *) BPF_FUNC_getsockopt;
|
||||
static int (*bpf_sock_ops_cb_flags_set)(void *ctx, int flags) =
|
||||
(void *) BPF_FUNC_sock_ops_cb_flags_set;
|
||||
static int (*bpf_sk_redirect_map)(void *ctx, void *map, int key, int flags) =
|
||||
(void *) BPF_FUNC_sk_redirect_map;
|
||||
static int (*bpf_sock_map_update)(void *map, void *key, void *value,
|
||||
|
|
@ -82,7 +84,8 @@ static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,
|
|||
static int (*bpf_perf_prog_read_value)(void *ctx, void *buf,
|
||||
unsigned int buf_size) =
|
||||
(void *) BPF_FUNC_perf_prog_read_value;
|
||||
|
||||
static int (*bpf_override_return)(void *ctx, unsigned long rc) =
|
||||
(void *) BPF_FUNC_override_return;
|
||||
|
||||
/* llvm builtin functions that eBPF C program may use to
|
||||
* emit BPF_LD_ABS and BPF_LD_IND instructions
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ CONFIG_BPF_SYSCALL=y
|
|||
CONFIG_NET_CLS_BPF=m
|
||||
CONFIG_BPF_EVENTS=y
|
||||
CONFIG_TEST_BPF=m
|
||||
CONFIG_CGROUP_BPF=y
|
||||
CONFIG_NETDEVSIM=m
|
||||
|
|
|
|||
34
tools/testing/selftests/bpf/sample_map_ret0.c
Normal file
34
tools/testing/selftests/bpf/sample_map_ret0.c
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
struct bpf_map_def SEC("maps") htab = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(long),
|
||||
.max_entries = 2,
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") array = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(long),
|
||||
.max_entries = 2,
|
||||
};
|
||||
|
||||
/* Sample program which should always load for testing control paths. */
|
||||
SEC(".text") int func()
|
||||
{
|
||||
__u64 key64 = 0;
|
||||
__u32 key = 0;
|
||||
long *value;
|
||||
|
||||
value = bpf_map_lookup_elem(&htab, &key);
|
||||
if (!value)
|
||||
return 1;
|
||||
value = bpf_map_lookup_elem(&array, &key64);
|
||||
if (!value)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
7
tools/testing/selftests/bpf/sample_ret0.c
Normal file
7
tools/testing/selftests/bpf/sample_ret0.c
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
|
||||
|
||||
/* Sample program which should always load for testing control paths. */
|
||||
int func()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
51
tools/testing/selftests/bpf/tcp_client.py
Executable file
51
tools/testing/selftests/bpf/tcp_client.py
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python2
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
import sys, os, os.path, getopt
|
||||
import socket, time
|
||||
import subprocess
|
||||
import select
|
||||
|
||||
def read(sock, n):
|
||||
buf = ''
|
||||
while len(buf) < n:
|
||||
rem = n - len(buf)
|
||||
try: s = sock.recv(rem)
|
||||
except (socket.error), e: return ''
|
||||
buf += s
|
||||
return buf
|
||||
|
||||
def send(sock, s):
|
||||
total = len(s)
|
||||
count = 0
|
||||
while count < total:
|
||||
try: n = sock.send(s)
|
||||
except (socket.error), e: n = 0
|
||||
if n == 0:
|
||||
return count;
|
||||
count += n
|
||||
return count
|
||||
|
||||
|
||||
serverPort = int(sys.argv[1])
|
||||
HostName = socket.gethostname()
|
||||
|
||||
# create active socket
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
try:
|
||||
sock.connect((HostName, serverPort))
|
||||
except socket.error as e:
|
||||
sys.exit(1)
|
||||
|
||||
buf = ''
|
||||
n = 0
|
||||
while n < 1000:
|
||||
buf += '+'
|
||||
n += 1
|
||||
|
||||
sock.settimeout(1);
|
||||
n = send(sock, buf)
|
||||
n = read(sock, 500)
|
||||
sys.exit(0)
|
||||
83
tools/testing/selftests/bpf/tcp_server.py
Executable file
83
tools/testing/selftests/bpf/tcp_server.py
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python2
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
import sys, os, os.path, getopt
|
||||
import socket, time
|
||||
import subprocess
|
||||
import select
|
||||
|
||||
def read(sock, n):
|
||||
buf = ''
|
||||
while len(buf) < n:
|
||||
rem = n - len(buf)
|
||||
try: s = sock.recv(rem)
|
||||
except (socket.error), e: return ''
|
||||
buf += s
|
||||
return buf
|
||||
|
||||
def send(sock, s):
|
||||
total = len(s)
|
||||
count = 0
|
||||
while count < total:
|
||||
try: n = sock.send(s)
|
||||
except (socket.error), e: n = 0
|
||||
if n == 0:
|
||||
return count;
|
||||
count += n
|
||||
return count
|
||||
|
||||
|
||||
SERVER_PORT = 12877
|
||||
MAX_PORTS = 2
|
||||
|
||||
serverPort = SERVER_PORT
|
||||
serverSocket = None
|
||||
|
||||
HostName = socket.gethostname()
|
||||
|
||||
# create passive socket
|
||||
serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
host = socket.gethostname()
|
||||
|
||||
try: serverSocket.bind((host, 0))
|
||||
except socket.error as msg:
|
||||
print 'bind fails: ', msg
|
||||
|
||||
sn = serverSocket.getsockname()
|
||||
serverPort = sn[1]
|
||||
|
||||
cmdStr = ("./tcp_client.py %d &") % (serverPort)
|
||||
os.system(cmdStr)
|
||||
|
||||
buf = ''
|
||||
n = 0
|
||||
while n < 500:
|
||||
buf += '.'
|
||||
n += 1
|
||||
|
||||
serverSocket.listen(MAX_PORTS)
|
||||
readList = [serverSocket]
|
||||
|
||||
while True:
|
||||
readyRead, readyWrite, inError = \
|
||||
select.select(readList, [], [], 2)
|
||||
|
||||
if len(readyRead) > 0:
|
||||
waitCount = 0
|
||||
for sock in readyRead:
|
||||
if sock == serverSocket:
|
||||
(clientSocket, address) = serverSocket.accept()
|
||||
address = str(address[0])
|
||||
readList.append(clientSocket)
|
||||
else:
|
||||
sock.settimeout(1);
|
||||
s = read(sock, 1000)
|
||||
n = send(sock, buf)
|
||||
sock.close()
|
||||
serverSocket.close()
|
||||
sys.exit(0)
|
||||
else:
|
||||
print 'Select timeout!'
|
||||
sys.exit(1)
|
||||
|
|
@ -64,11 +64,11 @@ static struct bpf_align_test tests[] = {
|
|||
.matches = {
|
||||
{1, "R1=ctx(id=0,off=0,imm=0)"},
|
||||
{1, "R10=fp0"},
|
||||
{1, "R3=inv2"},
|
||||
{2, "R3=inv4"},
|
||||
{3, "R3=inv8"},
|
||||
{4, "R3=inv16"},
|
||||
{5, "R3=inv32"},
|
||||
{1, "R3_w=inv2"},
|
||||
{2, "R3_w=inv4"},
|
||||
{3, "R3_w=inv8"},
|
||||
{4, "R3_w=inv16"},
|
||||
{5, "R3_w=inv32"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -92,17 +92,17 @@ static struct bpf_align_test tests[] = {
|
|||
.matches = {
|
||||
{1, "R1=ctx(id=0,off=0,imm=0)"},
|
||||
{1, "R10=fp0"},
|
||||
{1, "R3=inv1"},
|
||||
{2, "R3=inv2"},
|
||||
{3, "R3=inv4"},
|
||||
{4, "R3=inv8"},
|
||||
{5, "R3=inv16"},
|
||||
{6, "R3=inv1"},
|
||||
{7, "R4=inv32"},
|
||||
{8, "R4=inv16"},
|
||||
{9, "R4=inv8"},
|
||||
{10, "R4=inv4"},
|
||||
{11, "R4=inv2"},
|
||||
{1, "R3_w=inv1"},
|
||||
{2, "R3_w=inv2"},
|
||||
{3, "R3_w=inv4"},
|
||||
{4, "R3_w=inv8"},
|
||||
{5, "R3_w=inv16"},
|
||||
{6, "R3_w=inv1"},
|
||||
{7, "R4_w=inv32"},
|
||||
{8, "R4_w=inv16"},
|
||||
{9, "R4_w=inv8"},
|
||||
{10, "R4_w=inv4"},
|
||||
{11, "R4_w=inv2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -121,12 +121,12 @@ static struct bpf_align_test tests[] = {
|
|||
.matches = {
|
||||
{1, "R1=ctx(id=0,off=0,imm=0)"},
|
||||
{1, "R10=fp0"},
|
||||
{1, "R3=inv4"},
|
||||
{2, "R3=inv8"},
|
||||
{3, "R3=inv10"},
|
||||
{4, "R4=inv8"},
|
||||
{5, "R4=inv12"},
|
||||
{6, "R4=inv14"},
|
||||
{1, "R3_w=inv4"},
|
||||
{2, "R3_w=inv8"},
|
||||
{3, "R3_w=inv10"},
|
||||
{4, "R4_w=inv8"},
|
||||
{5, "R4_w=inv12"},
|
||||
{6, "R4_w=inv14"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -143,10 +143,10 @@ static struct bpf_align_test tests[] = {
|
|||
.matches = {
|
||||
{1, "R1=ctx(id=0,off=0,imm=0)"},
|
||||
{1, "R10=fp0"},
|
||||
{1, "R3=inv7"},
|
||||
{2, "R3=inv7"},
|
||||
{3, "R3=inv14"},
|
||||
{4, "R3=inv56"},
|
||||
{1, "R3_w=inv7"},
|
||||
{2, "R3_w=inv7"},
|
||||
{3, "R3_w=inv14"},
|
||||
{4, "R3_w=inv56"},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -185,18 +185,18 @@ static struct bpf_align_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
.matches = {
|
||||
{7, "R0=pkt(id=0,off=8,r=8,imm=0)"},
|
||||
{7, "R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{8, "R3=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||
{9, "R3=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{10, "R3=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
|
||||
{11, "R3=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
|
||||
{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||
{9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{10, "R3_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
|
||||
{11, "R3_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
|
||||
{18, "R3=pkt_end(id=0,off=0,imm=0)"},
|
||||
{18, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{19, "R4=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"},
|
||||
{20, "R4=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
|
||||
{21, "R4=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
|
||||
{22, "R4=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{23, "R4=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||
{18, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{19, "R4_w=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"},
|
||||
{20, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
|
||||
{21, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
|
||||
{22, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{23, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -217,16 +217,16 @@ static struct bpf_align_test tests[] = {
|
|||
},
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
.matches = {
|
||||
{7, "R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{8, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{9, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{10, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{11, "R4=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||
{12, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{13, "R4=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{14, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{15, "R4=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
|
||||
{16, "R4=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
|
||||
{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{8, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{9, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{11, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||
{12, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{13, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{14, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{15, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
|
||||
{16, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -257,14 +257,14 @@ static struct bpf_align_test tests[] = {
|
|||
},
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
.matches = {
|
||||
{4, "R5=pkt(id=0,off=0,r=0,imm=0)"},
|
||||
{5, "R5=pkt(id=0,off=14,r=0,imm=0)"},
|
||||
{6, "R4=pkt(id=0,off=14,r=0,imm=0)"},
|
||||
{4, "R5_w=pkt(id=0,off=0,r=0,imm=0)"},
|
||||
{5, "R5_w=pkt(id=0,off=14,r=0,imm=0)"},
|
||||
{6, "R4_w=pkt(id=0,off=14,r=0,imm=0)"},
|
||||
{10, "R2=pkt(id=0,off=0,r=18,imm=0)"},
|
||||
{10, "R5=pkt(id=0,off=14,r=18,imm=0)"},
|
||||
{10, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{14, "R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},
|
||||
{15, "R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},
|
||||
{10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||
{14, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},
|
||||
{15, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -320,11 +320,11 @@ static struct bpf_align_test tests[] = {
|
|||
* alignment of 4.
|
||||
*/
|
||||
{8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
||||
{8, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Offset is added to packet pointer R5, resulting in
|
||||
* known fixed offset, and variable offset from R6.
|
||||
*/
|
||||
{11, "R5=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{11, "R5_w=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* At the time the word size load is performed from R5,
|
||||
* it's total offset is NET_IP_ALIGN + reg->off (0) +
|
||||
* reg->aux_off (14) which is 16. Then the variable
|
||||
|
|
@ -336,11 +336,11 @@ static struct bpf_align_test tests[] = {
|
|||
/* Variable offset is added to R5 packet pointer,
|
||||
* resulting in auxiliary alignment of 4.
|
||||
*/
|
||||
{18, "R5=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{18, "R5_w=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Constant offset is added to R5, resulting in
|
||||
* reg->off of 14.
|
||||
*/
|
||||
{19, "R5=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{19, "R5_w=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* At the time the word size load is performed from R5,
|
||||
* its total fixed offset is NET_IP_ALIGN + reg->off
|
||||
* (14) which is 16. Then the variable offset is 4-byte
|
||||
|
|
@ -352,18 +352,18 @@ static struct bpf_align_test tests[] = {
|
|||
/* Constant offset is added to R5 packet pointer,
|
||||
* resulting in reg->off value of 14.
|
||||
*/
|
||||
{26, "R5=pkt(id=0,off=14,r=8"},
|
||||
{26, "R5_w=pkt(id=0,off=14,r=8"},
|
||||
/* Variable offset is added to R5, resulting in a
|
||||
* variable offset of (4n).
|
||||
*/
|
||||
{27, "R5=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{27, "R5_w=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Constant is added to R5 again, setting reg->off to 18. */
|
||||
{28, "R5=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{28, "R5_w=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* And once more we add a variable; resulting var_off
|
||||
* is still (4n), fixed offset is not changed.
|
||||
* Also, we create a new reg->id.
|
||||
*/
|
||||
{29, "R5=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc))"},
|
||||
{29, "R5_w=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc))"},
|
||||
/* At the time the word size load is performed from R5,
|
||||
* its total fixed offset is NET_IP_ALIGN + reg->off (18)
|
||||
* which is 20. Then the variable offset is (4n), so
|
||||
|
|
@ -410,11 +410,11 @@ static struct bpf_align_test tests[] = {
|
|||
* alignment of 4.
|
||||
*/
|
||||
{8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
||||
{8, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Adding 14 makes R6 be (4n+2) */
|
||||
{9, "R6=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
{9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
/* Packet pointer has (4n+2) offset */
|
||||
{11, "R5=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
{11, "R5_w=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
{13, "R4=pkt(id=1,off=4,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
/* At the time the word size load is performed from R5,
|
||||
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
||||
|
|
@ -426,11 +426,11 @@ static struct bpf_align_test tests[] = {
|
|||
/* Newly read value in R6 was shifted left by 2, so has
|
||||
* known alignment of 4.
|
||||
*/
|
||||
{18, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{18, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Added (4n) to packet pointer's (4n+2) var_off, giving
|
||||
* another (4n+2).
|
||||
*/
|
||||
{19, "R5=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},
|
||||
{19, "R5_w=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},
|
||||
{21, "R4=pkt(id=2,off=4,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},
|
||||
/* At the time the word size load is performed from R5,
|
||||
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
||||
|
|
@ -446,11 +446,9 @@ static struct bpf_align_test tests[] = {
|
|||
.insns = {
|
||||
PREP_PKT_POINTERS,
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
/* ptr & const => unknown & const */
|
||||
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
||||
BPF_ALU64_IMM(BPF_AND, BPF_REG_5, 0x40),
|
||||
/* ptr << const => unknown << const */
|
||||
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
||||
/* (ptr - ptr) << 2 */
|
||||
BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
|
||||
BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),
|
||||
BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),
|
||||
/* We have a (4n) value. Let's make a packet offset
|
||||
* out of it. First add 14, to make it a (4n+2)
|
||||
|
|
@ -473,8 +471,26 @@ static struct bpf_align_test tests[] = {
|
|||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
.result = REJECT,
|
||||
.matches = {
|
||||
{4, "R5=pkt(id=0,off=0,r=0,imm=0)"},
|
||||
/* R5 bitwise operator &= on pointer prohibited */
|
||||
{4, "R5_w=pkt_end(id=0,off=0,imm=0)"},
|
||||
/* (ptr - ptr) << 2 == unknown, (4n) */
|
||||
{6, "R5_w=inv(id=0,smax_value=9223372036854775804,umax_value=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc))"},
|
||||
/* (4n) + 14 == (4n+2). We blow our bounds, because
|
||||
* the add could overflow.
|
||||
*/
|
||||
{7, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"},
|
||||
/* Checked s>=0 */
|
||||
{9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||
/* packet pointer + nonnegative (4n+2) */
|
||||
{11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||
{13, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||
/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
|
||||
* We checked the bounds, but it might have been able
|
||||
* to overflow if the packet pointer started in the
|
||||
* upper half of the address space.
|
||||
* So we did not get a 'range' on R6, and the access
|
||||
* attempt will fail.
|
||||
*/
|
||||
{15, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -510,11 +526,11 @@ static struct bpf_align_test tests[] = {
|
|||
* alignment of 4.
|
||||
*/
|
||||
{7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
||||
{9, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Adding 14 makes R6 be (4n+2) */
|
||||
{10, "R6=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
{10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||
/* New unknown value in R7 is (4n) */
|
||||
{11, "R7=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
{11, "R7_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||
/* Subtracting it from R6 blows our unsigned bounds */
|
||||
{12, "R6=inv(id=0,smin_value=-1006,smax_value=1034,var_off=(0x2; 0xfffffffffffffffc))"},
|
||||
/* Checked s>= 0 */
|
||||
|
|
@ -563,15 +579,15 @@ static struct bpf_align_test tests[] = {
|
|||
* alignment of 4.
|
||||
*/
|
||||
{7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
||||
{10, "R6=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},
|
||||
{10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},
|
||||
/* Adding 14 makes R6 be (4n+2) */
|
||||
{11, "R6=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},
|
||||
{11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},
|
||||
/* Subtracting from packet pointer overflows ubounds */
|
||||
{13, "R5=pkt(id=1,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c))"},
|
||||
{13, "R5_w=pkt(id=1,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c))"},
|
||||
/* New unknown value in R7 is (4n), >= 76 */
|
||||
{15, "R7=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"},
|
||||
{15, "R7_w=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"},
|
||||
/* Adding it to packet pointer gives nice bounds again */
|
||||
{16, "R5=pkt(id=2,off=0,r=0,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"},
|
||||
{16, "R5_w=pkt(id=2,off=0,r=0,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"},
|
||||
/* At the time the word size load is performed from R5,
|
||||
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
||||
* which is 2. Then the variable offset is (4n+2), so
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
|
|
@ -19,19 +21,23 @@
|
|||
|
||||
#define DEV_CGROUP_PROG "./dev_cgroup.o"
|
||||
|
||||
#define TEST_CGROUP "test-bpf-based-device-cgroup/"
|
||||
#define TEST_CGROUP "/test-bpf-based-device-cgroup/"
|
||||
|
||||
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");
|
||||
goto err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (setup_cgroup_environment()) {
|
||||
|
|
@ -89,5 +95,6 @@ int main(int argc, char **argv)
|
|||
err:
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
|
|
|||
473
tools/testing/selftests/bpf/test_l4lb_noinline.c
Normal file
473
tools/testing/selftests/bpf/test_l4lb_noinline.c
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017 Facebook
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include "bpf_helpers.h"
|
||||
#include "test_iptunnel_common.h"
|
||||
#include "bpf_endian.h"
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
static __u32 rol32(__u32 word, unsigned int shift)
|
||||
{
|
||||
return (word << shift) | (word >> ((-shift) & 31));
|
||||
}
|
||||
|
||||
/* copy paste of jhash from kernel sources to make sure llvm
|
||||
* can compile it into valid sequence of bpf instructions
|
||||
*/
|
||||
#define __jhash_mix(a, b, c) \
|
||||
{ \
|
||||
a -= c; a ^= rol32(c, 4); c += b; \
|
||||
b -= a; b ^= rol32(a, 6); a += c; \
|
||||
c -= b; c ^= rol32(b, 8); b += a; \
|
||||
a -= c; a ^= rol32(c, 16); c += b; \
|
||||
b -= a; b ^= rol32(a, 19); a += c; \
|
||||
c -= b; c ^= rol32(b, 4); b += a; \
|
||||
}
|
||||
|
||||
#define __jhash_final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; c -= rol32(b, 14); \
|
||||
a ^= c; a -= rol32(c, 11); \
|
||||
b ^= a; b -= rol32(a, 25); \
|
||||
c ^= b; c -= rol32(b, 16); \
|
||||
a ^= c; a -= rol32(c, 4); \
|
||||
b ^= a; b -= rol32(a, 14); \
|
||||
c ^= b; c -= rol32(b, 24); \
|
||||
}
|
||||
|
||||
#define JHASH_INITVAL 0xdeadbeef
|
||||
|
||||
typedef unsigned int u32;
|
||||
|
||||
static u32 jhash(const void *key, u32 length, u32 initval)
|
||||
{
|
||||
u32 a, b, c;
|
||||
const unsigned char *k = key;
|
||||
|
||||
a = b = c = JHASH_INITVAL + length + initval;
|
||||
|
||||
while (length > 12) {
|
||||
a += *(u32 *)(k);
|
||||
b += *(u32 *)(k + 4);
|
||||
c += *(u32 *)(k + 8);
|
||||
__jhash_mix(a, b, c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
switch (length) {
|
||||
case 12: c += (u32)k[11]<<24;
|
||||
case 11: c += (u32)k[10]<<16;
|
||||
case 10: c += (u32)k[9]<<8;
|
||||
case 9: c += k[8];
|
||||
case 8: b += (u32)k[7]<<24;
|
||||
case 7: b += (u32)k[6]<<16;
|
||||
case 6: b += (u32)k[5]<<8;
|
||||
case 5: b += k[4];
|
||||
case 4: a += (u32)k[3]<<24;
|
||||
case 3: a += (u32)k[2]<<16;
|
||||
case 2: a += (u32)k[1]<<8;
|
||||
case 1: a += k[0];
|
||||
__jhash_final(a, b, c);
|
||||
case 0: /* Nothing left to add */
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval)
|
||||
{
|
||||
a += initval;
|
||||
b += initval;
|
||||
c += initval;
|
||||
__jhash_final(a, b, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
static u32 jhash_2words(u32 a, u32 b, u32 initval)
|
||||
{
|
||||
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
|
||||
}
|
||||
|
||||
#define PCKT_FRAGMENTED 65343
|
||||
#define IPV4_HDR_LEN_NO_OPT 20
|
||||
#define IPV4_PLUS_ICMP_HDR 28
|
||||
#define IPV6_PLUS_ICMP_HDR 48
|
||||
#define RING_SIZE 2
|
||||
#define MAX_VIPS 12
|
||||
#define MAX_REALS 5
|
||||
#define CTL_MAP_SIZE 16
|
||||
#define CH_RINGS_SIZE (MAX_VIPS * RING_SIZE)
|
||||
#define F_IPV6 (1 << 0)
|
||||
#define F_HASH_NO_SRC_PORT (1 << 0)
|
||||
#define F_ICMP (1 << 0)
|
||||
#define F_SYN_SET (1 << 1)
|
||||
|
||||
struct packet_description {
|
||||
union {
|
||||
__be32 src;
|
||||
__be32 srcv6[4];
|
||||
};
|
||||
union {
|
||||
__be32 dst;
|
||||
__be32 dstv6[4];
|
||||
};
|
||||
union {
|
||||
__u32 ports;
|
||||
__u16 port16[2];
|
||||
};
|
||||
__u8 proto;
|
||||
__u8 flags;
|
||||
};
|
||||
|
||||
struct ctl_value {
|
||||
union {
|
||||
__u64 value;
|
||||
__u32 ifindex;
|
||||
__u8 mac[6];
|
||||
};
|
||||
};
|
||||
|
||||
struct vip_meta {
|
||||
__u32 flags;
|
||||
__u32 vip_num;
|
||||
};
|
||||
|
||||
struct real_definition {
|
||||
union {
|
||||
__be32 dst;
|
||||
__be32 dstv6[4];
|
||||
};
|
||||
__u8 flags;
|
||||
};
|
||||
|
||||
struct vip_stats {
|
||||
__u64 bytes;
|
||||
__u64 pkts;
|
||||
};
|
||||
|
||||
struct eth_hdr {
|
||||
unsigned char eth_dest[ETH_ALEN];
|
||||
unsigned char eth_source[ETH_ALEN];
|
||||
unsigned short eth_proto;
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") vip_map = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.key_size = sizeof(struct vip),
|
||||
.value_size = sizeof(struct vip_meta),
|
||||
.max_entries = MAX_VIPS,
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") ch_rings = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u32),
|
||||
.max_entries = CH_RINGS_SIZE,
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") reals = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct real_definition),
|
||||
.max_entries = MAX_REALS,
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") stats = {
|
||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct vip_stats),
|
||||
.max_entries = MAX_VIPS,
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") ctl_array = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct ctl_value),
|
||||
.max_entries = CTL_MAP_SIZE,
|
||||
};
|
||||
|
||||
static __u32 get_packet_hash(struct packet_description *pckt,
|
||||
bool ipv6)
|
||||
{
|
||||
if (ipv6)
|
||||
return jhash_2words(jhash(pckt->srcv6, 16, MAX_VIPS),
|
||||
pckt->ports, CH_RINGS_SIZE);
|
||||
else
|
||||
return jhash_2words(pckt->src, pckt->ports, CH_RINGS_SIZE);
|
||||
}
|
||||
|
||||
static bool get_packet_dst(struct real_definition **real,
|
||||
struct packet_description *pckt,
|
||||
struct vip_meta *vip_info,
|
||||
bool is_ipv6)
|
||||
{
|
||||
__u32 hash = get_packet_hash(pckt, is_ipv6);
|
||||
__u32 key = RING_SIZE * vip_info->vip_num + hash % RING_SIZE;
|
||||
__u32 *real_pos;
|
||||
|
||||
if (hash != 0x358459b7 /* jhash of ipv4 packet */ &&
|
||||
hash != 0x2f4bc6bb /* jhash of ipv6 packet */)
|
||||
return 0;
|
||||
|
||||
real_pos = bpf_map_lookup_elem(&ch_rings, &key);
|
||||
if (!real_pos)
|
||||
return false;
|
||||
key = *real_pos;
|
||||
*real = bpf_map_lookup_elem(&reals, &key);
|
||||
if (!(*real))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int parse_icmpv6(void *data, void *data_end, __u64 off,
|
||||
struct packet_description *pckt)
|
||||
{
|
||||
struct icmp6hdr *icmp_hdr;
|
||||
struct ipv6hdr *ip6h;
|
||||
|
||||
icmp_hdr = data + off;
|
||||
if (icmp_hdr + 1 > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
if (icmp_hdr->icmp6_type != ICMPV6_PKT_TOOBIG)
|
||||
return TC_ACT_OK;
|
||||
off += sizeof(struct icmp6hdr);
|
||||
ip6h = data + off;
|
||||
if (ip6h + 1 > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
pckt->proto = ip6h->nexthdr;
|
||||
pckt->flags |= F_ICMP;
|
||||
memcpy(pckt->srcv6, ip6h->daddr.s6_addr32, 16);
|
||||
memcpy(pckt->dstv6, ip6h->saddr.s6_addr32, 16);
|
||||
return TC_ACT_UNSPEC;
|
||||
}
|
||||
|
||||
static int parse_icmp(void *data, void *data_end, __u64 off,
|
||||
struct packet_description *pckt)
|
||||
{
|
||||
struct icmphdr *icmp_hdr;
|
||||
struct iphdr *iph;
|
||||
|
||||
icmp_hdr = data + off;
|
||||
if (icmp_hdr + 1 > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
if (icmp_hdr->type != ICMP_DEST_UNREACH ||
|
||||
icmp_hdr->code != ICMP_FRAG_NEEDED)
|
||||
return TC_ACT_OK;
|
||||
off += sizeof(struct icmphdr);
|
||||
iph = data + off;
|
||||
if (iph + 1 > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
if (iph->ihl != 5)
|
||||
return TC_ACT_SHOT;
|
||||
pckt->proto = iph->protocol;
|
||||
pckt->flags |= F_ICMP;
|
||||
pckt->src = iph->daddr;
|
||||
pckt->dst = iph->saddr;
|
||||
return TC_ACT_UNSPEC;
|
||||
}
|
||||
|
||||
static bool parse_udp(void *data, __u64 off, void *data_end,
|
||||
struct packet_description *pckt)
|
||||
{
|
||||
struct udphdr *udp;
|
||||
udp = data + off;
|
||||
|
||||
if (udp + 1 > data_end)
|
||||
return false;
|
||||
|
||||
if (!(pckt->flags & F_ICMP)) {
|
||||
pckt->port16[0] = udp->source;
|
||||
pckt->port16[1] = udp->dest;
|
||||
} else {
|
||||
pckt->port16[0] = udp->dest;
|
||||
pckt->port16[1] = udp->source;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_tcp(void *data, __u64 off, void *data_end,
|
||||
struct packet_description *pckt)
|
||||
{
|
||||
struct tcphdr *tcp;
|
||||
|
||||
tcp = data + off;
|
||||
if (tcp + 1 > data_end)
|
||||
return false;
|
||||
|
||||
if (tcp->syn)
|
||||
pckt->flags |= F_SYN_SET;
|
||||
|
||||
if (!(pckt->flags & F_ICMP)) {
|
||||
pckt->port16[0] = tcp->source;
|
||||
pckt->port16[1] = tcp->dest;
|
||||
} else {
|
||||
pckt->port16[0] = tcp->dest;
|
||||
pckt->port16[1] = tcp->source;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int process_packet(void *data, __u64 off, void *data_end,
|
||||
bool is_ipv6, struct __sk_buff *skb)
|
||||
{
|
||||
void *pkt_start = (void *)(long)skb->data;
|
||||
struct packet_description pckt = {};
|
||||
struct eth_hdr *eth = pkt_start;
|
||||
struct bpf_tunnel_key tkey = {};
|
||||
struct vip_stats *data_stats;
|
||||
struct real_definition *dst;
|
||||
struct vip_meta *vip_info;
|
||||
struct ctl_value *cval;
|
||||
__u32 v4_intf_pos = 1;
|
||||
__u32 v6_intf_pos = 2;
|
||||
struct ipv6hdr *ip6h;
|
||||
struct vip vip = {};
|
||||
struct iphdr *iph;
|
||||
int tun_flag = 0;
|
||||
__u16 pkt_bytes;
|
||||
__u64 iph_len;
|
||||
__u32 ifindex;
|
||||
__u8 protocol;
|
||||
__u32 vip_num;
|
||||
int action;
|
||||
|
||||
tkey.tunnel_ttl = 64;
|
||||
if (is_ipv6) {
|
||||
ip6h = data + off;
|
||||
if (ip6h + 1 > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
iph_len = sizeof(struct ipv6hdr);
|
||||
protocol = ip6h->nexthdr;
|
||||
pckt.proto = protocol;
|
||||
pkt_bytes = bpf_ntohs(ip6h->payload_len);
|
||||
off += iph_len;
|
||||
if (protocol == IPPROTO_FRAGMENT) {
|
||||
return TC_ACT_SHOT;
|
||||
} else if (protocol == IPPROTO_ICMPV6) {
|
||||
action = parse_icmpv6(data, data_end, off, &pckt);
|
||||
if (action >= 0)
|
||||
return action;
|
||||
off += IPV6_PLUS_ICMP_HDR;
|
||||
} else {
|
||||
memcpy(pckt.srcv6, ip6h->saddr.s6_addr32, 16);
|
||||
memcpy(pckt.dstv6, ip6h->daddr.s6_addr32, 16);
|
||||
}
|
||||
} else {
|
||||
iph = data + off;
|
||||
if (iph + 1 > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
if (iph->ihl != 5)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
protocol = iph->protocol;
|
||||
pckt.proto = protocol;
|
||||
pkt_bytes = bpf_ntohs(iph->tot_len);
|
||||
off += IPV4_HDR_LEN_NO_OPT;
|
||||
|
||||
if (iph->frag_off & PCKT_FRAGMENTED)
|
||||
return TC_ACT_SHOT;
|
||||
if (protocol == IPPROTO_ICMP) {
|
||||
action = parse_icmp(data, data_end, off, &pckt);
|
||||
if (action >= 0)
|
||||
return action;
|
||||
off += IPV4_PLUS_ICMP_HDR;
|
||||
} else {
|
||||
pckt.src = iph->saddr;
|
||||
pckt.dst = iph->daddr;
|
||||
}
|
||||
}
|
||||
protocol = pckt.proto;
|
||||
|
||||
if (protocol == IPPROTO_TCP) {
|
||||
if (!parse_tcp(data, off, data_end, &pckt))
|
||||
return TC_ACT_SHOT;
|
||||
} else if (protocol == IPPROTO_UDP) {
|
||||
if (!parse_udp(data, off, data_end, &pckt))
|
||||
return TC_ACT_SHOT;
|
||||
} else {
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (is_ipv6)
|
||||
memcpy(vip.daddr.v6, pckt.dstv6, 16);
|
||||
else
|
||||
vip.daddr.v4 = pckt.dst;
|
||||
|
||||
vip.dport = pckt.port16[1];
|
||||
vip.protocol = pckt.proto;
|
||||
vip_info = bpf_map_lookup_elem(&vip_map, &vip);
|
||||
if (!vip_info) {
|
||||
vip.dport = 0;
|
||||
vip_info = bpf_map_lookup_elem(&vip_map, &vip);
|
||||
if (!vip_info)
|
||||
return TC_ACT_SHOT;
|
||||
pckt.port16[1] = 0;
|
||||
}
|
||||
|
||||
if (vip_info->flags & F_HASH_NO_SRC_PORT)
|
||||
pckt.port16[0] = 0;
|
||||
|
||||
if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6))
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (dst->flags & F_IPV6) {
|
||||
cval = bpf_map_lookup_elem(&ctl_array, &v6_intf_pos);
|
||||
if (!cval)
|
||||
return TC_ACT_SHOT;
|
||||
ifindex = cval->ifindex;
|
||||
memcpy(tkey.remote_ipv6, dst->dstv6, 16);
|
||||
tun_flag = BPF_F_TUNINFO_IPV6;
|
||||
} else {
|
||||
cval = bpf_map_lookup_elem(&ctl_array, &v4_intf_pos);
|
||||
if (!cval)
|
||||
return TC_ACT_SHOT;
|
||||
ifindex = cval->ifindex;
|
||||
tkey.remote_ipv4 = dst->dst;
|
||||
}
|
||||
vip_num = vip_info->vip_num;
|
||||
data_stats = bpf_map_lookup_elem(&stats, &vip_num);
|
||||
if (!data_stats)
|
||||
return TC_ACT_SHOT;
|
||||
data_stats->pkts++;
|
||||
data_stats->bytes += pkt_bytes;
|
||||
bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), tun_flag);
|
||||
*(u32 *)eth->eth_dest = tkey.remote_ipv4;
|
||||
return bpf_redirect(ifindex, 0);
|
||||
}
|
||||
|
||||
SEC("l4lb-demo")
|
||||
int balancer_ingress(struct __sk_buff *ctx)
|
||||
{
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
struct eth_hdr *eth = data;
|
||||
__u32 eth_proto;
|
||||
__u32 nh_off;
|
||||
|
||||
nh_off = sizeof(struct eth_hdr);
|
||||
if (data + nh_off > data_end)
|
||||
return TC_ACT_SHOT;
|
||||
eth_proto = eth->eth_proto;
|
||||
if (eth_proto == bpf_htons(ETH_P_IP))
|
||||
return process_packet(data, nh_off, data_end, false, ctx);
|
||||
else if (eth_proto == bpf_htons(ETH_P_IPV6))
|
||||
return process_packet(data, nh_off, data_end, true, ctx);
|
||||
else
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -521,6 +522,218 @@ static void test_lpm_delete(void)
|
|||
close(map_fd);
|
||||
}
|
||||
|
||||
static void test_lpm_get_next_key(void)
|
||||
{
|
||||
struct bpf_lpm_trie_key *key_p, *next_key_p;
|
||||
size_t key_size;
|
||||
__u32 value = 0;
|
||||
int map_fd;
|
||||
|
||||
key_size = sizeof(*key_p) + sizeof(__u32);
|
||||
key_p = alloca(key_size);
|
||||
next_key_p = alloca(key_size);
|
||||
|
||||
map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, sizeof(value),
|
||||
100, BPF_F_NO_PREALLOC);
|
||||
assert(map_fd >= 0);
|
||||
|
||||
/* empty tree. get_next_key should return ENOENT */
|
||||
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* get and verify the first key, get the second one should fail. */
|
||||
key_p->prefixlen = 16;
|
||||
inet_pton(AF_INET, "192.168.0.0", key_p->data);
|
||||
assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
|
||||
|
||||
memset(key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
|
||||
assert(key_p->prefixlen == 16 && key_p->data[0] == 192 &&
|
||||
key_p->data[1] == 168);
|
||||
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* no exact matching key should get the first one in post order. */
|
||||
key_p->prefixlen = 8;
|
||||
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
|
||||
assert(key_p->prefixlen == 16 && key_p->data[0] == 192 &&
|
||||
key_p->data[1] == 168);
|
||||
|
||||
/* add one more element (total two) */
|
||||
key_p->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.0.0", key_p->data);
|
||||
assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
|
||||
|
||||
memset(key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
|
||||
assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
|
||||
key_p->data[1] == 168 && key_p->data[2] == 0);
|
||||
|
||||
memset(next_key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168);
|
||||
|
||||
memcpy(key_p, next_key_p, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* Add one more element (total three) */
|
||||
key_p->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.128.0", key_p->data);
|
||||
assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
|
||||
|
||||
memset(key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
|
||||
assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
|
||||
key_p->data[1] == 168 && key_p->data[2] == 0);
|
||||
|
||||
memset(next_key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
|
||||
|
||||
memcpy(key_p, next_key_p, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168);
|
||||
|
||||
memcpy(key_p, next_key_p, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* Add one more element (total four) */
|
||||
key_p->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", key_p->data);
|
||||
assert(bpf_map_update_elem(map_fd, key_p, &value, 0) == 0);
|
||||
|
||||
memset(key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, NULL, key_p) == 0);
|
||||
assert(key_p->prefixlen == 24 && key_p->data[0] == 192 &&
|
||||
key_p->data[1] == 168 && key_p->data[2] == 0);
|
||||
|
||||
memset(next_key_p, 0, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168 && next_key_p->data[2] == 1);
|
||||
|
||||
memcpy(key_p, next_key_p, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168 && next_key_p->data[2] == 128);
|
||||
|
||||
memcpy(key_p, next_key_p, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 16 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168);
|
||||
|
||||
memcpy(key_p, next_key_p, key_size);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* no exact matching key should return the first one in post order */
|
||||
key_p->prefixlen = 22;
|
||||
inet_pton(AF_INET, "192.168.1.0", key_p->data);
|
||||
assert(bpf_map_get_next_key(map_fd, key_p, next_key_p) == 0);
|
||||
assert(next_key_p->prefixlen == 24 && next_key_p->data[0] == 192 &&
|
||||
next_key_p->data[1] == 168 && next_key_p->data[2] == 0);
|
||||
|
||||
close(map_fd);
|
||||
}
|
||||
|
||||
#define MAX_TEST_KEYS 4
|
||||
struct lpm_mt_test_info {
|
||||
int cmd; /* 0: update, 1: delete, 2: lookup, 3: get_next_key */
|
||||
int iter;
|
||||
int map_fd;
|
||||
struct {
|
||||
__u32 prefixlen;
|
||||
__u32 data;
|
||||
} key[MAX_TEST_KEYS];
|
||||
};
|
||||
|
||||
static void *lpm_test_command(void *arg)
|
||||
{
|
||||
int i, j, ret, iter, key_size;
|
||||
struct lpm_mt_test_info *info = arg;
|
||||
struct bpf_lpm_trie_key *key_p;
|
||||
|
||||
key_size = sizeof(struct bpf_lpm_trie_key) + sizeof(__u32);
|
||||
key_p = alloca(key_size);
|
||||
for (iter = 0; iter < info->iter; iter++)
|
||||
for (i = 0; i < MAX_TEST_KEYS; i++) {
|
||||
/* first half of iterations in forward order,
|
||||
* and second half in backward order.
|
||||
*/
|
||||
j = (iter < (info->iter / 2)) ? i : MAX_TEST_KEYS - i - 1;
|
||||
key_p->prefixlen = info->key[j].prefixlen;
|
||||
memcpy(key_p->data, &info->key[j].data, sizeof(__u32));
|
||||
if (info->cmd == 0) {
|
||||
__u32 value = j;
|
||||
/* update must succeed */
|
||||
assert(bpf_map_update_elem(info->map_fd, key_p, &value, 0) == 0);
|
||||
} else if (info->cmd == 1) {
|
||||
ret = bpf_map_delete_elem(info->map_fd, key_p);
|
||||
assert(ret == 0 || errno == ENOENT);
|
||||
} else if (info->cmd == 2) {
|
||||
__u32 value;
|
||||
ret = bpf_map_lookup_elem(info->map_fd, key_p, &value);
|
||||
assert(ret == 0 || errno == ENOENT);
|
||||
} else {
|
||||
struct bpf_lpm_trie_key *next_key_p = alloca(key_size);
|
||||
ret = bpf_map_get_next_key(info->map_fd, key_p, next_key_p);
|
||||
assert(ret == 0 || errno == ENOENT || errno == ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass successful exit info back to the main thread
|
||||
pthread_exit((void *)info);
|
||||
}
|
||||
|
||||
static void setup_lpm_mt_test_info(struct lpm_mt_test_info *info, int map_fd)
|
||||
{
|
||||
info->iter = 2000;
|
||||
info->map_fd = map_fd;
|
||||
info->key[0].prefixlen = 16;
|
||||
inet_pton(AF_INET, "192.168.0.0", &info->key[0].data);
|
||||
info->key[1].prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.0.0", &info->key[1].data);
|
||||
info->key[2].prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.128.0", &info->key[2].data);
|
||||
info->key[3].prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", &info->key[3].data);
|
||||
}
|
||||
|
||||
static void test_lpm_multi_thread(void)
|
||||
{
|
||||
struct lpm_mt_test_info info[4];
|
||||
size_t key_size, value_size;
|
||||
pthread_t thread_id[4];
|
||||
int i, map_fd;
|
||||
void *ret;
|
||||
|
||||
/* create a trie */
|
||||
value_size = sizeof(__u32);
|
||||
key_size = sizeof(struct bpf_lpm_trie_key) + value_size;
|
||||
map_fd = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, key_size, value_size,
|
||||
100, BPF_F_NO_PREALLOC);
|
||||
|
||||
/* create 4 threads to test update, delete, lookup and get_next_key */
|
||||
setup_lpm_mt_test_info(&info[0], map_fd);
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i != 0)
|
||||
memcpy(&info[i], &info[0], sizeof(info[i]));
|
||||
info[i].cmd = i;
|
||||
assert(pthread_create(&thread_id[i], NULL, &lpm_test_command, &info[i]) == 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
assert(pthread_join(thread_id[i], &ret) == 0 && ret == (void *)&info[i]);
|
||||
|
||||
close(map_fd);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
|
|
@ -545,6 +758,10 @@ int main(void)
|
|||
|
||||
test_lpm_delete();
|
||||
|
||||
test_lpm_get_next_key();
|
||||
|
||||
test_lpm_multi_thread();
|
||||
|
||||
printf("test_lpm: OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ static void test_hashmap_percpu(int task, void *data)
|
|||
|
||||
static void test_hashmap_walk(int task, void *data)
|
||||
{
|
||||
int fd, i, max_entries = 100000;
|
||||
int fd, i, max_entries = 1000;
|
||||
long long key, value, next_key;
|
||||
bool next_key_valid = true;
|
||||
|
||||
|
|
@ -463,7 +463,7 @@ static void test_devmap(int task, void *data)
|
|||
#define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o"
|
||||
static void test_sockmap(int tasks, void *data)
|
||||
{
|
||||
int one = 1, map_fd_rx, map_fd_tx, map_fd_break, s, sc, rc;
|
||||
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;
|
||||
int ports[] = {50200, 50201, 50202, 50204};
|
||||
int err, i, fd, udp, sfd[6] = {0xdeadbeef};
|
||||
|
|
@ -868,9 +868,12 @@ static void test_sockmap(int tasks, void *data)
|
|||
goto out_sockmap;
|
||||
}
|
||||
|
||||
/* Test map close sockets */
|
||||
for (i = 0; i < 6; i++)
|
||||
/* Test map close sockets and empty maps */
|
||||
for (i = 0; i < 6; i++) {
|
||||
bpf_map_delete_elem(map_fd_tx, &i);
|
||||
bpf_map_delete_elem(map_fd_rx, &i);
|
||||
close(sfd[i]);
|
||||
}
|
||||
close(fd);
|
||||
close(map_fd_rx);
|
||||
bpf_object__close(obj);
|
||||
|
|
@ -881,8 +884,13 @@ out:
|
|||
printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno));
|
||||
exit(1);
|
||||
out_sockmap:
|
||||
for (i = 0; i < 6; i++)
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (map_fd_tx)
|
||||
bpf_map_delete_elem(map_fd_tx, &i);
|
||||
if (map_fd_rx)
|
||||
bpf_map_delete_elem(map_fd_rx, &i);
|
||||
close(sfd[i]);
|
||||
}
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -931,8 +939,12 @@ static void test_map_large(void)
|
|||
close(fd);
|
||||
}
|
||||
|
||||
static void run_parallel(int tasks, void (*fn)(int task, void *data),
|
||||
void *data)
|
||||
#define run_parallel(N, FN, DATA) \
|
||||
printf("Fork %d tasks to '" #FN "'\n", N); \
|
||||
__run_parallel(N, FN, DATA)
|
||||
|
||||
static void __run_parallel(int tasks, void (*fn)(int task, void *data),
|
||||
void *data)
|
||||
{
|
||||
pid_t pid[tasks];
|
||||
int i;
|
||||
|
|
@ -972,7 +984,7 @@ static void test_map_stress(void)
|
|||
#define DO_UPDATE 1
|
||||
#define DO_DELETE 0
|
||||
|
||||
static void do_work(int fn, void *data)
|
||||
static void test_update_delete(int fn, void *data)
|
||||
{
|
||||
int do_update = ((int *)data)[1];
|
||||
int fd = ((int *)data)[0];
|
||||
|
|
@ -1012,7 +1024,7 @@ static void test_map_parallel(void)
|
|||
*/
|
||||
data[0] = fd;
|
||||
data[1] = DO_UPDATE;
|
||||
run_parallel(TASKS, do_work, data);
|
||||
run_parallel(TASKS, test_update_delete, data);
|
||||
|
||||
/* Check that key=0 is already there. */
|
||||
assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 &&
|
||||
|
|
@ -1035,7 +1047,7 @@ static void test_map_parallel(void)
|
|||
|
||||
/* Now let's delete all elemenets in parallel. */
|
||||
data[1] = DO_DELETE;
|
||||
run_parallel(TASKS, do_work, data);
|
||||
run_parallel(TASKS, test_update_delete, data);
|
||||
|
||||
/* Nothing should be left. */
|
||||
key = -1;
|
||||
|
|
|
|||
1085
tools/testing/selftests/bpf/test_offload.py
Executable file
1085
tools/testing/selftests/bpf/test_offload.py
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -21,8 +21,10 @@ typedef __u16 __sum16;
|
|||
#include <linux/ipv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
|
|
@ -167,10 +169,9 @@ out:
|
|||
#define NUM_ITER 100000
|
||||
#define VIP_NUM 5
|
||||
|
||||
static void test_l4lb(void)
|
||||
static void test_l4lb(const char *file)
|
||||
{
|
||||
unsigned int nr_cpus = bpf_num_possible_cpus();
|
||||
const char *file = "./test_l4lb.o";
|
||||
struct vip key = {.protocol = 6};
|
||||
struct vip_meta {
|
||||
__u32 flags;
|
||||
|
|
@ -247,6 +248,95 @@ out:
|
|||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
static void test_l4lb_all(void)
|
||||
{
|
||||
const char *file1 = "./test_l4lb.o";
|
||||
const char *file2 = "./test_l4lb_noinline.o";
|
||||
|
||||
test_l4lb(file1);
|
||||
test_l4lb(file2);
|
||||
}
|
||||
|
||||
static void test_xdp_noinline(void)
|
||||
{
|
||||
const char *file = "./test_xdp_noinline.o";
|
||||
unsigned int nr_cpus = bpf_num_possible_cpus();
|
||||
struct vip key = {.protocol = 6};
|
||||
struct vip_meta {
|
||||
__u32 flags;
|
||||
__u32 vip_num;
|
||||
} value = {.vip_num = VIP_NUM};
|
||||
__u32 stats_key = VIP_NUM;
|
||||
struct vip_stats {
|
||||
__u64 bytes;
|
||||
__u64 pkts;
|
||||
} stats[nr_cpus];
|
||||
struct real_definition {
|
||||
union {
|
||||
__be32 dst;
|
||||
__be32 dstv6[4];
|
||||
};
|
||||
__u8 flags;
|
||||
} real_def = {.dst = MAGIC_VAL};
|
||||
__u32 ch_key = 11, real_num = 3;
|
||||
__u32 duration, retval, size;
|
||||
int err, i, prog_fd, map_fd;
|
||||
__u64 bytes = 0, pkts = 0;
|
||||
struct bpf_object *obj;
|
||||
char buf[128];
|
||||
u32 *magic = (u32 *)buf;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
|
||||
if (err) {
|
||||
error_cnt++;
|
||||
return;
|
||||
}
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "vip_map");
|
||||
if (map_fd < 0)
|
||||
goto out;
|
||||
bpf_map_update_elem(map_fd, &key, &value, 0);
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "ch_rings");
|
||||
if (map_fd < 0)
|
||||
goto out;
|
||||
bpf_map_update_elem(map_fd, &ch_key, &real_num, 0);
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "reals");
|
||||
if (map_fd < 0)
|
||||
goto out;
|
||||
bpf_map_update_elem(map_fd, &real_num, &real_def, 0);
|
||||
|
||||
err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4),
|
||||
buf, &size, &retval, &duration);
|
||||
CHECK(err || errno || retval != 1 || size != 54 ||
|
||||
*magic != MAGIC_VAL, "ipv4",
|
||||
"err %d errno %d retval %d size %d magic %x\n",
|
||||
err, errno, retval, size, *magic);
|
||||
|
||||
err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6),
|
||||
buf, &size, &retval, &duration);
|
||||
CHECK(err || errno || retval != 1 || size != 74 ||
|
||||
*magic != MAGIC_VAL, "ipv6",
|
||||
"err %d errno %d retval %d size %d magic %x\n",
|
||||
err, errno, retval, size, *magic);
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "stats");
|
||||
if (map_fd < 0)
|
||||
goto out;
|
||||
bpf_map_lookup_elem(map_fd, &stats_key, stats);
|
||||
for (i = 0; i < nr_cpus; i++) {
|
||||
bytes += stats[i].bytes;
|
||||
pkts += stats[i].pkts;
|
||||
}
|
||||
if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) {
|
||||
error_cnt++;
|
||||
printf("test_xdp_noinline:FAIL:stats %lld %lld\n", bytes, pkts);
|
||||
}
|
||||
out:
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
static void test_tcp_estats(void)
|
||||
{
|
||||
const char *file = "./test_tcp_estats.o";
|
||||
|
|
@ -617,6 +707,262 @@ static void test_obj_name(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void test_tp_attach_query(void)
|
||||
{
|
||||
const int num_progs = 3;
|
||||
int i, j, bytes, efd, err, prog_fd[num_progs], pmu_fd[num_progs];
|
||||
__u32 duration = 0, info_len, saved_prog_ids[num_progs];
|
||||
const char *file = "./test_tracepoint.o";
|
||||
struct perf_event_query_bpf *query;
|
||||
struct perf_event_attr attr = {};
|
||||
struct bpf_object *obj[num_progs];
|
||||
struct bpf_prog_info prog_info;
|
||||
char buf[256];
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"/sys/kernel/debug/tracing/events/sched/sched_switch/id");
|
||||
efd = open(buf, O_RDONLY, 0);
|
||||
if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
|
||||
return;
|
||||
bytes = read(efd, buf, sizeof(buf));
|
||||
close(efd);
|
||||
if (CHECK(bytes <= 0 || bytes >= sizeof(buf),
|
||||
"read", "bytes %d errno %d\n", bytes, errno))
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
query = malloc(sizeof(*query) + sizeof(__u32) * num_progs);
|
||||
for (i = 0; i < num_progs; i++) {
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj[i],
|
||||
&prog_fd[i]);
|
||||
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
||||
goto cleanup1;
|
||||
|
||||
bzero(&prog_info, sizeof(prog_info));
|
||||
prog_info.jited_prog_len = 0;
|
||||
prog_info.xlated_prog_len = 0;
|
||||
prog_info.nr_map_ids = 0;
|
||||
info_len = sizeof(prog_info);
|
||||
err = bpf_obj_get_info_by_fd(prog_fd[i], &prog_info, &info_len);
|
||||
if (CHECK(err, "bpf_obj_get_info_by_fd", "err %d errno %d\n",
|
||||
err, errno))
|
||||
goto cleanup1;
|
||||
saved_prog_ids[i] = prog_info.id;
|
||||
|
||||
pmu_fd[i] = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
|
||||
0 /* cpu 0 */, -1 /* group id */,
|
||||
0 /* flags */);
|
||||
if (CHECK(pmu_fd[i] < 0, "perf_event_open", "err %d errno %d\n",
|
||||
pmu_fd[i], errno))
|
||||
goto cleanup2;
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n",
|
||||
err, errno))
|
||||
goto cleanup3;
|
||||
|
||||
if (i == 0) {
|
||||
/* check NULL prog array query */
|
||||
query->ids_len = num_progs;
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query);
|
||||
if (CHECK(err || query->prog_cnt != 0,
|
||||
"perf_event_ioc_query_bpf",
|
||||
"err %d errno %d query->prog_cnt %u\n",
|
||||
err, errno, query->prog_cnt))
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[i]);
|
||||
if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n",
|
||||
err, errno))
|
||||
goto cleanup3;
|
||||
|
||||
if (i == 1) {
|
||||
/* try to get # of programs only */
|
||||
query->ids_len = 0;
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query);
|
||||
if (CHECK(err || query->prog_cnt != 2,
|
||||
"perf_event_ioc_query_bpf",
|
||||
"err %d errno %d query->prog_cnt %u\n",
|
||||
err, errno, query->prog_cnt))
|
||||
goto cleanup3;
|
||||
|
||||
/* try a few negative tests */
|
||||
/* invalid query pointer */
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF,
|
||||
(struct perf_event_query_bpf *)0x1);
|
||||
if (CHECK(!err || errno != EFAULT,
|
||||
"perf_event_ioc_query_bpf",
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto cleanup3;
|
||||
|
||||
/* no enough space */
|
||||
query->ids_len = 1;
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query);
|
||||
if (CHECK(!err || errno != ENOSPC || query->prog_cnt != 2,
|
||||
"perf_event_ioc_query_bpf",
|
||||
"err %d errno %d query->prog_cnt %u\n",
|
||||
err, errno, query->prog_cnt))
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
query->ids_len = num_progs;
|
||||
err = ioctl(pmu_fd[i], PERF_EVENT_IOC_QUERY_BPF, query);
|
||||
if (CHECK(err || query->prog_cnt != (i + 1),
|
||||
"perf_event_ioc_query_bpf",
|
||||
"err %d errno %d query->prog_cnt %u\n",
|
||||
err, errno, query->prog_cnt))
|
||||
goto cleanup3;
|
||||
for (j = 0; j < i + 1; j++)
|
||||
if (CHECK(saved_prog_ids[j] != query->ids[j],
|
||||
"perf_event_ioc_query_bpf",
|
||||
"#%d saved_prog_id %x query prog_id %x\n",
|
||||
j, saved_prog_ids[j], query->ids[j]))
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
i = num_progs - 1;
|
||||
for (; i >= 0; i--) {
|
||||
cleanup3:
|
||||
ioctl(pmu_fd[i], PERF_EVENT_IOC_DISABLE);
|
||||
cleanup2:
|
||||
close(pmu_fd[i]);
|
||||
cleanup1:
|
||||
bpf_object__close(obj[i]);
|
||||
}
|
||||
free(query);
|
||||
}
|
||||
|
||||
static int compare_map_keys(int map1_fd, int map2_fd)
|
||||
{
|
||||
__u32 key, next_key;
|
||||
char val_buf[PERF_MAX_STACK_DEPTH * sizeof(__u64)];
|
||||
int err;
|
||||
|
||||
err = bpf_map_get_next_key(map1_fd, NULL, &key);
|
||||
if (err)
|
||||
return err;
|
||||
err = bpf_map_lookup_elem(map2_fd, &key, val_buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
while (bpf_map_get_next_key(map1_fd, &key, &next_key) == 0) {
|
||||
err = bpf_map_lookup_elem(map2_fd, &next_key, val_buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
key = next_key;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_stacktrace_map()
|
||||
{
|
||||
int control_map_fd, stackid_hmap_fd, stackmap_fd;
|
||||
const char *file = "./test_stacktrace_map.o";
|
||||
int bytes, efd, err, pmu_fd, prog_fd;
|
||||
struct perf_event_attr attr = {};
|
||||
__u32 key, val, duration = 0;
|
||||
struct bpf_object *obj;
|
||||
char buf[256];
|
||||
|
||||
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/sched/sched_switch/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;
|
||||
|
||||
/* give some time for bpf program run */
|
||||
sleep(1);
|
||||
|
||||
/* 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))
|
||||
; /* fall through */
|
||||
|
||||
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)
|
||||
{
|
||||
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
|
|
@ -625,11 +971,14 @@ int main(void)
|
|||
|
||||
test_pkt_access();
|
||||
test_xdp();
|
||||
test_l4lb();
|
||||
test_l4lb_all();
|
||||
test_xdp_noinline();
|
||||
test_tcp_estats();
|
||||
test_bpf_obj_id();
|
||||
test_pkt_md_access();
|
||||
test_obj_name();
|
||||
test_tp_attach_query();
|
||||
test_stacktrace_map();
|
||||
|
||||
printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
|
||||
return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
|
|
|||
62
tools/testing/selftests/bpf/test_stacktrace_map.c
Normal file
62
tools/testing/selftests/bpf/test_stacktrace_map.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// 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(__u64) * PERF_MAX_STACK_DEPTH,
|
||||
.max_entries = 10000,
|
||||
};
|
||||
|
||||
/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */
|
||||
struct sched_switch_args {
|
||||
unsigned long long pad;
|
||||
char prev_comm[16];
|
||||
int prev_pid;
|
||||
int prev_prio;
|
||||
long long prev_state;
|
||||
char next_comm[16];
|
||||
int next_pid;
|
||||
int next_prio;
|
||||
};
|
||||
|
||||
SEC("tracepoint/sched/sched_switch")
|
||||
int oncpu(struct sched_switch_args *ctx)
|
||||
{
|
||||
__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(ctx, &stackmap, 0);
|
||||
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 */
|
||||
16
tools/testing/selftests/bpf/test_tcpbpf.h
Normal file
16
tools/testing/selftests/bpf/test_tcpbpf.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#ifndef _TEST_TCPBPF_H
|
||||
#define _TEST_TCPBPF_H
|
||||
|
||||
struct tcpbpf_globals {
|
||||
__u32 event_map;
|
||||
__u32 total_retrans;
|
||||
__u32 data_segs_in;
|
||||
__u32 data_segs_out;
|
||||
__u32 bad_cb_test_rv;
|
||||
__u32 good_cb_test_rv;
|
||||
__u64 bytes_received;
|
||||
__u64 bytes_acked;
|
||||
};
|
||||
#endif
|
||||
115
tools/testing/selftests/bpf/test_tcpbpf_kern.c
Normal file
115
tools/testing/selftests/bpf/test_tcpbpf_kern.c
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
#include "bpf_helpers.h"
|
||||
#include "bpf_endian.h"
|
||||
#include "test_tcpbpf.h"
|
||||
|
||||
struct bpf_map_def SEC("maps") global_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct tcpbpf_globals),
|
||||
.max_entries = 2,
|
||||
};
|
||||
|
||||
static inline void update_event_map(int event)
|
||||
{
|
||||
__u32 key = 0;
|
||||
struct tcpbpf_globals g, *gp;
|
||||
|
||||
gp = bpf_map_lookup_elem(&global_map, &key);
|
||||
if (gp == NULL) {
|
||||
struct tcpbpf_globals g = {0};
|
||||
|
||||
g.event_map |= (1 << event);
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
} else {
|
||||
g = *gp;
|
||||
g.event_map |= (1 << event);
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
SEC("sockops")
|
||||
int bpf_testcb(struct bpf_sock_ops *skops)
|
||||
{
|
||||
int rv = -1;
|
||||
int bad_call_rv = 0;
|
||||
int good_call_rv = 0;
|
||||
int op;
|
||||
int v = 0;
|
||||
|
||||
op = (int) skops->op;
|
||||
|
||||
update_event_map(op);
|
||||
|
||||
switch (op) {
|
||||
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
|
||||
/* Test failure to set largest cb flag (assumes not defined) */
|
||||
bad_call_rv = bpf_sock_ops_cb_flags_set(skops, 0x80);
|
||||
/* Set callback */
|
||||
good_call_rv = bpf_sock_ops_cb_flags_set(skops,
|
||||
BPF_SOCK_OPS_STATE_CB_FLAG);
|
||||
/* Update results */
|
||||
{
|
||||
__u32 key = 0;
|
||||
struct tcpbpf_globals g, *gp;
|
||||
|
||||
gp = bpf_map_lookup_elem(&global_map, &key);
|
||||
if (!gp)
|
||||
break;
|
||||
g = *gp;
|
||||
g.bad_cb_test_rv = bad_call_rv;
|
||||
g.good_cb_test_rv = good_call_rv;
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
}
|
||||
break;
|
||||
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
|
||||
skops->sk_txhash = 0x12345f;
|
||||
v = 0xff;
|
||||
rv = bpf_setsockopt(skops, SOL_IPV6, IPV6_TCLASS, &v,
|
||||
sizeof(v));
|
||||
break;
|
||||
case BPF_SOCK_OPS_RTO_CB:
|
||||
break;
|
||||
case BPF_SOCK_OPS_RETRANS_CB:
|
||||
break;
|
||||
case BPF_SOCK_OPS_STATE_CB:
|
||||
if (skops->args[1] == BPF_TCP_CLOSE) {
|
||||
__u32 key = 0;
|
||||
struct tcpbpf_globals g, *gp;
|
||||
|
||||
gp = bpf_map_lookup_elem(&global_map, &key);
|
||||
if (!gp)
|
||||
break;
|
||||
g = *gp;
|
||||
g.total_retrans = skops->total_retrans;
|
||||
g.data_segs_in = skops->data_segs_in;
|
||||
g.data_segs_out = skops->data_segs_out;
|
||||
g.bytes_received = skops->bytes_received;
|
||||
g.bytes_acked = skops->bytes_acked;
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rv = -1;
|
||||
}
|
||||
skops->reply = rv;
|
||||
return 1;
|
||||
}
|
||||
char _license[] SEC("license") = "GPL";
|
||||
126
tools/testing/selftests/bpf/test_tcpbpf_user.c
Normal file
126
tools/testing/selftests/bpf/test_tcpbpf_user.c
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <sys/ioctl.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 <linux/perf_event.h>
|
||||
#include "test_tcpbpf.h"
|
||||
|
||||
static int bpf_find_map(const char *test, struct bpf_object *obj,
|
||||
const char *name)
|
||||
{
|
||||
struct bpf_map *map;
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, name);
|
||||
if (!map) {
|
||||
printf("%s:FAIL:map '%s' not found\n", test, name);
|
||||
return -1;
|
||||
}
|
||||
return bpf_map__fd(map);
|
||||
}
|
||||
|
||||
#define SYSTEM(CMD) \
|
||||
do { \
|
||||
if (system(CMD)) { \
|
||||
printf("system(%s) FAILS!\n", CMD); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *file = "test_tcpbpf_kern.o";
|
||||
struct tcpbpf_globals g = {0};
|
||||
int cg_fd, prog_fd, map_fd;
|
||||
bool debug_flag = false;
|
||||
int error = EXIT_FAILURE;
|
||||
struct bpf_object *obj;
|
||||
char cmd[100], *dir;
|
||||
struct stat buffer;
|
||||
__u32 key = 0;
|
||||
int pid;
|
||||
int rv;
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "-d") == 0)
|
||||
debug_flag = true;
|
||||
|
||||
dir = "/tmp/cgroupv2/foo";
|
||||
|
||||
if (stat(dir, &buffer) != 0) {
|
||||
SYSTEM("mkdir -p /tmp/cgroupv2");
|
||||
SYSTEM("mount -t cgroup2 none /tmp/cgroupv2");
|
||||
SYSTEM("mkdir -p /tmp/cgroupv2/foo");
|
||||
}
|
||||
pid = (int) getpid();
|
||||
sprintf(cmd, "echo %d >> /tmp/cgroupv2/foo/cgroup.procs", pid);
|
||||
SYSTEM(cmd);
|
||||
|
||||
cg_fd = open(dir, O_DIRECTORY, O_RDONLY);
|
||||
if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
|
||||
printf("FAILED: load_bpf_file failed for: %s\n", file);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rv = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_SOCK_OPS, 0);
|
||||
if (rv) {
|
||||
printf("FAILED: bpf_prog_attach: %d (%s)\n",
|
||||
error, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
SYSTEM("./tcp_server.py");
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "global_map");
|
||||
if (map_fd < 0)
|
||||
goto err;
|
||||
|
||||
rv = bpf_map_lookup_elem(map_fd, &key, &g);
|
||||
if (rv != 0) {
|
||||
printf("FAILED: bpf_map_lookup_elem returns %d\n", rv);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (g.bytes_received != 501 || g.bytes_acked != 1002 ||
|
||||
g.data_segs_in != 1 || g.data_segs_out != 1 ||
|
||||
(g.event_map ^ 0x47e) != 0 || g.bad_cb_test_rv != 0x80 ||
|
||||
g.good_cb_test_rv != 0) {
|
||||
printf("FAILED: Wrong stats\n");
|
||||
if (debug_flag) {
|
||||
printf("\n");
|
||||
printf("bytes_received: %d (expecting 501)\n",
|
||||
(int)g.bytes_received);
|
||||
printf("bytes_acked: %d (expecting 1002)\n",
|
||||
(int)g.bytes_acked);
|
||||
printf("data_segs_in: %d (expecting 1)\n",
|
||||
g.data_segs_in);
|
||||
printf("data_segs_out: %d (expecting 1)\n",
|
||||
g.data_segs_out);
|
||||
printf("event_map: 0x%x (at least 0x47e)\n",
|
||||
g.event_map);
|
||||
printf("bad_cb_test_rv: 0x%x (expecting 0x80)\n",
|
||||
g.bad_cb_test_rv);
|
||||
printf("good_cb_test_rv:0x%x (expecting 0)\n",
|
||||
g.good_cb_test_rv);
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
printf("PASSED!\n");
|
||||
error = 0;
|
||||
err:
|
||||
bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
|
||||
return error;
|
||||
|
||||
}
|
||||
26
tools/testing/selftests/bpf/test_tracepoint.c
Normal file
26
tools/testing/selftests/bpf/test_tracepoint.c
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */
|
||||
struct sched_switch_args {
|
||||
unsigned long long pad;
|
||||
char prev_comm[16];
|
||||
int prev_pid;
|
||||
int prev_prio;
|
||||
long long prev_state;
|
||||
char next_comm[16];
|
||||
int next_pid;
|
||||
int next_prio;
|
||||
};
|
||||
|
||||
SEC("tracepoint/sched/sched_switch")
|
||||
int oncpu(struct sched_switch_args *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */
|
||||
File diff suppressed because it is too large
Load diff
833
tools/testing/selftests/bpf/test_xdp_noinline.c
Normal file
833
tools/testing/selftests/bpf/test_xdp_noinline.c
Normal file
|
|
@ -0,0 +1,833 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017 Facebook
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
static __u32 rol32(__u32 word, unsigned int shift)
|
||||
{
|
||||
return (word << shift) | (word >> ((-shift) & 31));
|
||||
}
|
||||
|
||||
/* copy paste of jhash from kernel sources to make sure llvm
|
||||
* can compile it into valid sequence of bpf instructions
|
||||
*/
|
||||
#define __jhash_mix(a, b, c) \
|
||||
{ \
|
||||
a -= c; a ^= rol32(c, 4); c += b; \
|
||||
b -= a; b ^= rol32(a, 6); a += c; \
|
||||
c -= b; c ^= rol32(b, 8); b += a; \
|
||||
a -= c; a ^= rol32(c, 16); c += b; \
|
||||
b -= a; b ^= rol32(a, 19); a += c; \
|
||||
c -= b; c ^= rol32(b, 4); b += a; \
|
||||
}
|
||||
|
||||
#define __jhash_final(a, b, c) \
|
||||
{ \
|
||||
c ^= b; c -= rol32(b, 14); \
|
||||
a ^= c; a -= rol32(c, 11); \
|
||||
b ^= a; b -= rol32(a, 25); \
|
||||
c ^= b; c -= rol32(b, 16); \
|
||||
a ^= c; a -= rol32(c, 4); \
|
||||
b ^= a; b -= rol32(a, 14); \
|
||||
c ^= b; c -= rol32(b, 24); \
|
||||
}
|
||||
|
||||
#define JHASH_INITVAL 0xdeadbeef
|
||||
|
||||
typedef unsigned int u32;
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
u32 jhash(const void *key, u32 length, u32 initval)
|
||||
{
|
||||
u32 a, b, c;
|
||||
const unsigned char *k = key;
|
||||
|
||||
a = b = c = JHASH_INITVAL + length + initval;
|
||||
|
||||
while (length > 12) {
|
||||
a += *(u32 *)(k);
|
||||
b += *(u32 *)(k + 4);
|
||||
c += *(u32 *)(k + 8);
|
||||
__jhash_mix(a, b, c);
|
||||
length -= 12;
|
||||
k += 12;
|
||||
}
|
||||
switch (length) {
|
||||
case 12: c += (u32)k[11]<<24;
|
||||
case 11: c += (u32)k[10]<<16;
|
||||
case 10: c += (u32)k[9]<<8;
|
||||
case 9: c += k[8];
|
||||
case 8: b += (u32)k[7]<<24;
|
||||
case 7: b += (u32)k[6]<<16;
|
||||
case 6: b += (u32)k[5]<<8;
|
||||
case 5: b += k[4];
|
||||
case 4: a += (u32)k[3]<<24;
|
||||
case 3: a += (u32)k[2]<<16;
|
||||
case 2: a += (u32)k[1]<<8;
|
||||
case 1: a += k[0];
|
||||
__jhash_final(a, b, c);
|
||||
case 0: /* Nothing left to add */
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval)
|
||||
{
|
||||
a += initval;
|
||||
b += initval;
|
||||
c += initval;
|
||||
__jhash_final(a, b, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
u32 jhash_2words(u32 a, u32 b, u32 initval)
|
||||
{
|
||||
return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
|
||||
}
|
||||
|
||||
struct flow_key {
|
||||
union {
|
||||
__be32 src;
|
||||
__be32 srcv6[4];
|
||||
};
|
||||
union {
|
||||
__be32 dst;
|
||||
__be32 dstv6[4];
|
||||
};
|
||||
union {
|
||||
__u32 ports;
|
||||
__u16 port16[2];
|
||||
};
|
||||
__u8 proto;
|
||||
};
|
||||
|
||||
struct packet_description {
|
||||
struct flow_key flow;
|
||||
__u8 flags;
|
||||
};
|
||||
|
||||
struct ctl_value {
|
||||
union {
|
||||
__u64 value;
|
||||
__u32 ifindex;
|
||||
__u8 mac[6];
|
||||
};
|
||||
};
|
||||
|
||||
struct vip_definition {
|
||||
union {
|
||||
__be32 vip;
|
||||
__be32 vipv6[4];
|
||||
};
|
||||
__u16 port;
|
||||
__u16 family;
|
||||
__u8 proto;
|
||||
};
|
||||
|
||||
struct vip_meta {
|
||||
__u32 flags;
|
||||
__u32 vip_num;
|
||||
};
|
||||
|
||||
struct real_pos_lru {
|
||||
__u32 pos;
|
||||
__u64 atime;
|
||||
};
|
||||
|
||||
struct real_definition {
|
||||
union {
|
||||
__be32 dst;
|
||||
__be32 dstv6[4];
|
||||
};
|
||||
__u8 flags;
|
||||
};
|
||||
|
||||
struct lb_stats {
|
||||
__u64 v2;
|
||||
__u64 v1;
|
||||
};
|
||||
|
||||
struct bpf_map_def __attribute__ ((section("maps"), used)) vip_map = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.key_size = sizeof(struct vip_definition),
|
||||
.value_size = sizeof(struct vip_meta),
|
||||
.max_entries = 512,
|
||||
.map_flags = 0,
|
||||
};
|
||||
|
||||
struct bpf_map_def __attribute__ ((section("maps"), used)) lru_cache = {
|
||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
||||
.key_size = sizeof(struct flow_key),
|
||||
.value_size = sizeof(struct real_pos_lru),
|
||||
.max_entries = 300,
|
||||
.map_flags = 1U << 1,
|
||||
};
|
||||
|
||||
struct bpf_map_def __attribute__ ((section("maps"), used)) ch_rings = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u32),
|
||||
.max_entries = 12 * 655,
|
||||
.map_flags = 0,
|
||||
};
|
||||
|
||||
struct bpf_map_def __attribute__ ((section("maps"), used)) reals = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct real_definition),
|
||||
.max_entries = 40,
|
||||
.map_flags = 0,
|
||||
};
|
||||
|
||||
struct bpf_map_def __attribute__ ((section("maps"), used)) stats = {
|
||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct lb_stats),
|
||||
.max_entries = 515,
|
||||
.map_flags = 0,
|
||||
};
|
||||
|
||||
struct bpf_map_def __attribute__ ((section("maps"), used)) ctl_array = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct ctl_value),
|
||||
.max_entries = 16,
|
||||
.map_flags = 0,
|
||||
};
|
||||
|
||||
struct eth_hdr {
|
||||
unsigned char eth_dest[6];
|
||||
unsigned char eth_source[6];
|
||||
unsigned short eth_proto;
|
||||
};
|
||||
|
||||
static inline __u64 calc_offset(bool is_ipv6, bool is_icmp)
|
||||
{
|
||||
__u64 off = sizeof(struct eth_hdr);
|
||||
if (is_ipv6) {
|
||||
off += sizeof(struct ipv6hdr);
|
||||
if (is_icmp)
|
||||
off += sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr);
|
||||
} else {
|
||||
off += sizeof(struct iphdr);
|
||||
if (is_icmp)
|
||||
off += sizeof(struct icmphdr) + sizeof(struct iphdr);
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
bool parse_udp(void *data, void *data_end,
|
||||
bool is_ipv6, struct packet_description *pckt)
|
||||
{
|
||||
|
||||
bool is_icmp = !((pckt->flags & (1 << 0)) == 0);
|
||||
__u64 off = calc_offset(is_ipv6, is_icmp);
|
||||
struct udphdr *udp;
|
||||
udp = data + off;
|
||||
|
||||
if (udp + 1 > data_end)
|
||||
return 0;
|
||||
if (!is_icmp) {
|
||||
pckt->flow.port16[0] = udp->source;
|
||||
pckt->flow.port16[1] = udp->dest;
|
||||
} else {
|
||||
pckt->flow.port16[0] = udp->dest;
|
||||
pckt->flow.port16[1] = udp->source;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
bool parse_tcp(void *data, void *data_end,
|
||||
bool is_ipv6, struct packet_description *pckt)
|
||||
{
|
||||
|
||||
bool is_icmp = !((pckt->flags & (1 << 0)) == 0);
|
||||
__u64 off = calc_offset(is_ipv6, is_icmp);
|
||||
struct tcphdr *tcp;
|
||||
|
||||
tcp = data + off;
|
||||
if (tcp + 1 > data_end)
|
||||
return 0;
|
||||
if (tcp->syn)
|
||||
pckt->flags |= (1 << 1);
|
||||
if (!is_icmp) {
|
||||
pckt->flow.port16[0] = tcp->source;
|
||||
pckt->flow.port16[1] = tcp->dest;
|
||||
} else {
|
||||
pckt->flow.port16[0] = tcp->dest;
|
||||
pckt->flow.port16[1] = tcp->source;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
bool encap_v6(struct xdp_md *xdp, struct ctl_value *cval,
|
||||
struct packet_description *pckt,
|
||||
struct real_definition *dst, __u32 pkt_bytes)
|
||||
{
|
||||
struct eth_hdr *new_eth;
|
||||
struct eth_hdr *old_eth;
|
||||
struct ipv6hdr *ip6h;
|
||||
__u32 ip_suffix;
|
||||
void *data_end;
|
||||
void *data;
|
||||
|
||||
if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr)))
|
||||
return 0;
|
||||
data = (void *)(long)xdp->data;
|
||||
data_end = (void *)(long)xdp->data_end;
|
||||
new_eth = data;
|
||||
ip6h = data + sizeof(struct eth_hdr);
|
||||
old_eth = data + sizeof(struct ipv6hdr);
|
||||
if (new_eth + 1 > data_end ||
|
||||
old_eth + 1 > data_end || ip6h + 1 > data_end)
|
||||
return 0;
|
||||
memcpy(new_eth->eth_dest, cval->mac, 6);
|
||||
memcpy(new_eth->eth_source, old_eth->eth_dest, 6);
|
||||
new_eth->eth_proto = 56710;
|
||||
ip6h->version = 6;
|
||||
ip6h->priority = 0;
|
||||
memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl));
|
||||
|
||||
ip6h->nexthdr = IPPROTO_IPV6;
|
||||
ip_suffix = pckt->flow.srcv6[3] ^ pckt->flow.port16[0];
|
||||
ip6h->payload_len =
|
||||
__builtin_bswap16(pkt_bytes + sizeof(struct ipv6hdr));
|
||||
ip6h->hop_limit = 4;
|
||||
|
||||
ip6h->saddr.in6_u.u6_addr32[0] = 1;
|
||||
ip6h->saddr.in6_u.u6_addr32[1] = 2;
|
||||
ip6h->saddr.in6_u.u6_addr32[2] = 3;
|
||||
ip6h->saddr.in6_u.u6_addr32[3] = ip_suffix;
|
||||
memcpy(ip6h->daddr.in6_u.u6_addr32, dst->dstv6, 16);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
bool encap_v4(struct xdp_md *xdp, struct ctl_value *cval,
|
||||
struct packet_description *pckt,
|
||||
struct real_definition *dst, __u32 pkt_bytes)
|
||||
{
|
||||
|
||||
__u32 ip_suffix = __builtin_bswap16(pckt->flow.port16[0]);
|
||||
struct eth_hdr *new_eth;
|
||||
struct eth_hdr *old_eth;
|
||||
__u16 *next_iph_u16;
|
||||
struct iphdr *iph;
|
||||
__u32 csum = 0;
|
||||
void *data_end;
|
||||
void *data;
|
||||
|
||||
ip_suffix <<= 15;
|
||||
ip_suffix ^= pckt->flow.src;
|
||||
if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr)))
|
||||
return 0;
|
||||
data = (void *)(long)xdp->data;
|
||||
data_end = (void *)(long)xdp->data_end;
|
||||
new_eth = data;
|
||||
iph = data + sizeof(struct eth_hdr);
|
||||
old_eth = data + sizeof(struct iphdr);
|
||||
if (new_eth + 1 > data_end ||
|
||||
old_eth + 1 > data_end || iph + 1 > data_end)
|
||||
return 0;
|
||||
memcpy(new_eth->eth_dest, cval->mac, 6);
|
||||
memcpy(new_eth->eth_source, old_eth->eth_dest, 6);
|
||||
new_eth->eth_proto = 8;
|
||||
iph->version = 4;
|
||||
iph->ihl = 5;
|
||||
iph->frag_off = 0;
|
||||
iph->protocol = IPPROTO_IPIP;
|
||||
iph->check = 0;
|
||||
iph->tos = 1;
|
||||
iph->tot_len = __builtin_bswap16(pkt_bytes + sizeof(struct iphdr));
|
||||
/* don't update iph->daddr, since it will overwrite old eth_proto
|
||||
* and multiple iterations of bpf_prog_run() will fail
|
||||
*/
|
||||
|
||||
iph->saddr = ((0xFFFF0000 & ip_suffix) | 4268) ^ dst->dst;
|
||||
iph->ttl = 4;
|
||||
|
||||
next_iph_u16 = (__u16 *) iph;
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < sizeof(struct iphdr) >> 1; i++)
|
||||
csum += *next_iph_u16++;
|
||||
iph->check = ~((csum & 0xffff) + (csum >> 16));
|
||||
if (bpf_xdp_adjust_head(xdp, (int)sizeof(struct iphdr)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
bool decap_v6(struct xdp_md *xdp, void **data, void **data_end, bool inner_v4)
|
||||
{
|
||||
struct eth_hdr *new_eth;
|
||||
struct eth_hdr *old_eth;
|
||||
|
||||
old_eth = *data;
|
||||
new_eth = *data + sizeof(struct ipv6hdr);
|
||||
memcpy(new_eth->eth_source, old_eth->eth_source, 6);
|
||||
memcpy(new_eth->eth_dest, old_eth->eth_dest, 6);
|
||||
if (inner_v4)
|
||||
new_eth->eth_proto = 8;
|
||||
else
|
||||
new_eth->eth_proto = 56710;
|
||||
if (bpf_xdp_adjust_head(xdp, (int)sizeof(struct ipv6hdr)))
|
||||
return 0;
|
||||
*data = (void *)(long)xdp->data;
|
||||
*data_end = (void *)(long)xdp->data_end;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
bool decap_v4(struct xdp_md *xdp, void **data, void **data_end)
|
||||
{
|
||||
struct eth_hdr *new_eth;
|
||||
struct eth_hdr *old_eth;
|
||||
|
||||
old_eth = *data;
|
||||
new_eth = *data + sizeof(struct iphdr);
|
||||
memcpy(new_eth->eth_source, old_eth->eth_source, 6);
|
||||
memcpy(new_eth->eth_dest, old_eth->eth_dest, 6);
|
||||
new_eth->eth_proto = 8;
|
||||
if (bpf_xdp_adjust_head(xdp, (int)sizeof(struct iphdr)))
|
||||
return 0;
|
||||
*data = (void *)(long)xdp->data;
|
||||
*data_end = (void *)(long)xdp->data_end;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
int swap_mac_and_send(void *data, void *data_end)
|
||||
{
|
||||
unsigned char tmp_mac[6];
|
||||
struct eth_hdr *eth;
|
||||
|
||||
eth = data;
|
||||
memcpy(tmp_mac, eth->eth_source, 6);
|
||||
memcpy(eth->eth_source, eth->eth_dest, 6);
|
||||
memcpy(eth->eth_dest, tmp_mac, 6);
|
||||
return XDP_TX;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
int send_icmp_reply(void *data, void *data_end)
|
||||
{
|
||||
struct icmphdr *icmp_hdr;
|
||||
__u16 *next_iph_u16;
|
||||
__u32 tmp_addr = 0;
|
||||
struct iphdr *iph;
|
||||
__u32 csum1 = 0;
|
||||
__u32 csum = 0;
|
||||
__u64 off = 0;
|
||||
|
||||
if (data + sizeof(struct eth_hdr)
|
||||
+ sizeof(struct iphdr) + sizeof(struct icmphdr) > data_end)
|
||||
return XDP_DROP;
|
||||
off += sizeof(struct eth_hdr);
|
||||
iph = data + off;
|
||||
off += sizeof(struct iphdr);
|
||||
icmp_hdr = data + off;
|
||||
icmp_hdr->type = 0;
|
||||
icmp_hdr->checksum += 0x0007;
|
||||
iph->ttl = 4;
|
||||
tmp_addr = iph->daddr;
|
||||
iph->daddr = iph->saddr;
|
||||
iph->saddr = tmp_addr;
|
||||
iph->check = 0;
|
||||
next_iph_u16 = (__u16 *) iph;
|
||||
#pragma clang loop unroll(full)
|
||||
for (int i = 0; i < sizeof(struct iphdr) >> 1; i++)
|
||||
csum += *next_iph_u16++;
|
||||
iph->check = ~((csum & 0xffff) + (csum >> 16));
|
||||
return swap_mac_and_send(data, data_end);
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
int send_icmp6_reply(void *data, void *data_end)
|
||||
{
|
||||
struct icmp6hdr *icmp_hdr;
|
||||
struct ipv6hdr *ip6h;
|
||||
__be32 tmp_addr[4];
|
||||
__u64 off = 0;
|
||||
|
||||
if (data + sizeof(struct eth_hdr)
|
||||
+ sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr) > data_end)
|
||||
return XDP_DROP;
|
||||
off += sizeof(struct eth_hdr);
|
||||
ip6h = data + off;
|
||||
off += sizeof(struct ipv6hdr);
|
||||
icmp_hdr = data + off;
|
||||
icmp_hdr->icmp6_type = 129;
|
||||
icmp_hdr->icmp6_cksum -= 0x0001;
|
||||
ip6h->hop_limit = 4;
|
||||
memcpy(tmp_addr, ip6h->saddr.in6_u.u6_addr32, 16);
|
||||
memcpy(ip6h->saddr.in6_u.u6_addr32, ip6h->daddr.in6_u.u6_addr32, 16);
|
||||
memcpy(ip6h->daddr.in6_u.u6_addr32, tmp_addr, 16);
|
||||
return swap_mac_and_send(data, data_end);
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
int parse_icmpv6(void *data, void *data_end, __u64 off,
|
||||
struct packet_description *pckt)
|
||||
{
|
||||
struct icmp6hdr *icmp_hdr;
|
||||
struct ipv6hdr *ip6h;
|
||||
|
||||
icmp_hdr = data + off;
|
||||
if (icmp_hdr + 1 > data_end)
|
||||
return XDP_DROP;
|
||||
if (icmp_hdr->icmp6_type == 128)
|
||||
return send_icmp6_reply(data, data_end);
|
||||
if (icmp_hdr->icmp6_type != 3)
|
||||
return XDP_PASS;
|
||||
off += sizeof(struct icmp6hdr);
|
||||
ip6h = data + off;
|
||||
if (ip6h + 1 > data_end)
|
||||
return XDP_DROP;
|
||||
pckt->flow.proto = ip6h->nexthdr;
|
||||
pckt->flags |= (1 << 0);
|
||||
memcpy(pckt->flow.srcv6, ip6h->daddr.in6_u.u6_addr32, 16);
|
||||
memcpy(pckt->flow.dstv6, ip6h->saddr.in6_u.u6_addr32, 16);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
int parse_icmp(void *data, void *data_end, __u64 off,
|
||||
struct packet_description *pckt)
|
||||
{
|
||||
struct icmphdr *icmp_hdr;
|
||||
struct iphdr *iph;
|
||||
|
||||
icmp_hdr = data + off;
|
||||
if (icmp_hdr + 1 > data_end)
|
||||
return XDP_DROP;
|
||||
if (icmp_hdr->type == 8)
|
||||
return send_icmp_reply(data, data_end);
|
||||
if ((icmp_hdr->type != 3) || (icmp_hdr->code != 4))
|
||||
return XDP_PASS;
|
||||
off += sizeof(struct icmphdr);
|
||||
iph = data + off;
|
||||
if (iph + 1 > data_end)
|
||||
return XDP_DROP;
|
||||
if (iph->ihl != 5)
|
||||
return XDP_DROP;
|
||||
pckt->flow.proto = iph->protocol;
|
||||
pckt->flags |= (1 << 0);
|
||||
pckt->flow.src = iph->daddr;
|
||||
pckt->flow.dst = iph->saddr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __attribute__ ((noinline))
|
||||
__u32 get_packet_hash(struct packet_description *pckt,
|
||||
bool hash_16bytes)
|
||||
{
|
||||
if (hash_16bytes)
|
||||
return jhash_2words(jhash(pckt->flow.srcv6, 16, 12),
|
||||
pckt->flow.ports, 24);
|
||||
else
|
||||
return jhash_2words(pckt->flow.src, pckt->flow.ports,
|
||||
24);
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static bool get_packet_dst(struct real_definition **real,
|
||||
struct packet_description *pckt,
|
||||
struct vip_meta *vip_info,
|
||||
bool is_ipv6, void *lru_map)
|
||||
{
|
||||
struct real_pos_lru new_dst_lru = { };
|
||||
bool hash_16bytes = is_ipv6;
|
||||
__u32 *real_pos, hash, key;
|
||||
__u64 cur_time;
|
||||
|
||||
if (vip_info->flags & (1 << 2))
|
||||
hash_16bytes = 1;
|
||||
if (vip_info->flags & (1 << 3)) {
|
||||
pckt->flow.port16[0] = pckt->flow.port16[1];
|
||||
memset(pckt->flow.srcv6, 0, 16);
|
||||
}
|
||||
hash = get_packet_hash(pckt, hash_16bytes);
|
||||
if (hash != 0x358459b7 /* jhash of ipv4 packet */ &&
|
||||
hash != 0x2f4bc6bb /* jhash of ipv6 packet */)
|
||||
return 0;
|
||||
key = 2 * vip_info->vip_num + hash % 2;
|
||||
real_pos = bpf_map_lookup_elem(&ch_rings, &key);
|
||||
if (!real_pos)
|
||||
return 0;
|
||||
key = *real_pos;
|
||||
*real = bpf_map_lookup_elem(&reals, &key);
|
||||
if (!(*real))
|
||||
return 0;
|
||||
if (!(vip_info->flags & (1 << 1))) {
|
||||
__u32 conn_rate_key = 512 + 2;
|
||||
struct lb_stats *conn_rate_stats =
|
||||
bpf_map_lookup_elem(&stats, &conn_rate_key);
|
||||
|
||||
if (!conn_rate_stats)
|
||||
return 1;
|
||||
cur_time = bpf_ktime_get_ns();
|
||||
if ((cur_time - conn_rate_stats->v2) >> 32 > 0xffFFFF) {
|
||||
conn_rate_stats->v1 = 1;
|
||||
conn_rate_stats->v2 = cur_time;
|
||||
} else {
|
||||
conn_rate_stats->v1 += 1;
|
||||
if (conn_rate_stats->v1 >= 1)
|
||||
return 1;
|
||||
}
|
||||
if (pckt->flow.proto == IPPROTO_UDP)
|
||||
new_dst_lru.atime = cur_time;
|
||||
new_dst_lru.pos = key;
|
||||
bpf_map_update_elem(lru_map, &pckt->flow, &new_dst_lru, 0);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static void connection_table_lookup(struct real_definition **real,
|
||||
struct packet_description *pckt,
|
||||
void *lru_map)
|
||||
{
|
||||
|
||||
struct real_pos_lru *dst_lru;
|
||||
__u64 cur_time;
|
||||
__u32 key;
|
||||
|
||||
dst_lru = bpf_map_lookup_elem(lru_map, &pckt->flow);
|
||||
if (!dst_lru)
|
||||
return;
|
||||
if (pckt->flow.proto == IPPROTO_UDP) {
|
||||
cur_time = bpf_ktime_get_ns();
|
||||
if (cur_time - dst_lru->atime > 300000)
|
||||
return;
|
||||
dst_lru->atime = cur_time;
|
||||
}
|
||||
key = dst_lru->pos;
|
||||
*real = bpf_map_lookup_elem(&reals, &key);
|
||||
}
|
||||
|
||||
/* don't believe your eyes!
|
||||
* below function has 6 arguments whereas bpf and llvm allow maximum of 5
|
||||
* but since it's _static_ llvm can optimize one argument away
|
||||
*/
|
||||
__attribute__ ((noinline))
|
||||
static int process_l3_headers_v6(struct packet_description *pckt,
|
||||
__u8 *protocol, __u64 off,
|
||||
__u16 *pkt_bytes, void *data,
|
||||
void *data_end)
|
||||
{
|
||||
struct ipv6hdr *ip6h;
|
||||
__u64 iph_len;
|
||||
int action;
|
||||
|
||||
ip6h = data + off;
|
||||
if (ip6h + 1 > data_end)
|
||||
return XDP_DROP;
|
||||
iph_len = sizeof(struct ipv6hdr);
|
||||
*protocol = ip6h->nexthdr;
|
||||
pckt->flow.proto = *protocol;
|
||||
*pkt_bytes = __builtin_bswap16(ip6h->payload_len);
|
||||
off += iph_len;
|
||||
if (*protocol == 45) {
|
||||
return XDP_DROP;
|
||||
} else if (*protocol == 59) {
|
||||
action = parse_icmpv6(data, data_end, off, pckt);
|
||||
if (action >= 0)
|
||||
return action;
|
||||
} else {
|
||||
memcpy(pckt->flow.srcv6, ip6h->saddr.in6_u.u6_addr32, 16);
|
||||
memcpy(pckt->flow.dstv6, ip6h->daddr.in6_u.u6_addr32, 16);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static int process_l3_headers_v4(struct packet_description *pckt,
|
||||
__u8 *protocol, __u64 off,
|
||||
__u16 *pkt_bytes, void *data,
|
||||
void *data_end)
|
||||
{
|
||||
struct iphdr *iph;
|
||||
__u64 iph_len;
|
||||
int action;
|
||||
|
||||
iph = data + off;
|
||||
if (iph + 1 > data_end)
|
||||
return XDP_DROP;
|
||||
if (iph->ihl != 5)
|
||||
return XDP_DROP;
|
||||
*protocol = iph->protocol;
|
||||
pckt->flow.proto = *protocol;
|
||||
*pkt_bytes = __builtin_bswap16(iph->tot_len);
|
||||
off += 20;
|
||||
if (iph->frag_off & 65343)
|
||||
return XDP_DROP;
|
||||
if (*protocol == IPPROTO_ICMP) {
|
||||
action = parse_icmp(data, data_end, off, pckt);
|
||||
if (action >= 0)
|
||||
return action;
|
||||
} else {
|
||||
pckt->flow.src = iph->saddr;
|
||||
pckt->flow.dst = iph->daddr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static int process_packet(void *data, __u64 off, void *data_end,
|
||||
bool is_ipv6, struct xdp_md *xdp)
|
||||
{
|
||||
|
||||
struct real_definition *dst = NULL;
|
||||
struct packet_description pckt = { };
|
||||
struct vip_definition vip = { };
|
||||
struct lb_stats *data_stats;
|
||||
struct eth_hdr *eth = data;
|
||||
void *lru_map = &lru_cache;
|
||||
struct vip_meta *vip_info;
|
||||
__u32 lru_stats_key = 513;
|
||||
__u32 mac_addr_pos = 0;
|
||||
__u32 stats_key = 512;
|
||||
struct ctl_value *cval;
|
||||
__u16 pkt_bytes;
|
||||
__u64 iph_len;
|
||||
__u8 protocol;
|
||||
__u32 vip_num;
|
||||
int action;
|
||||
|
||||
if (is_ipv6)
|
||||
action = process_l3_headers_v6(&pckt, &protocol, off,
|
||||
&pkt_bytes, data, data_end);
|
||||
else
|
||||
action = process_l3_headers_v4(&pckt, &protocol, off,
|
||||
&pkt_bytes, data, data_end);
|
||||
if (action >= 0)
|
||||
return action;
|
||||
protocol = pckt.flow.proto;
|
||||
if (protocol == IPPROTO_TCP) {
|
||||
if (!parse_tcp(data, data_end, is_ipv6, &pckt))
|
||||
return XDP_DROP;
|
||||
} else if (protocol == IPPROTO_UDP) {
|
||||
if (!parse_udp(data, data_end, is_ipv6, &pckt))
|
||||
return XDP_DROP;
|
||||
} else {
|
||||
return XDP_TX;
|
||||
}
|
||||
|
||||
if (is_ipv6)
|
||||
memcpy(vip.vipv6, pckt.flow.dstv6, 16);
|
||||
else
|
||||
vip.vip = pckt.flow.dst;
|
||||
vip.port = pckt.flow.port16[1];
|
||||
vip.proto = pckt.flow.proto;
|
||||
vip_info = bpf_map_lookup_elem(&vip_map, &vip);
|
||||
if (!vip_info) {
|
||||
vip.port = 0;
|
||||
vip_info = bpf_map_lookup_elem(&vip_map, &vip);
|
||||
if (!vip_info)
|
||||
return XDP_PASS;
|
||||
if (!(vip_info->flags & (1 << 4)))
|
||||
pckt.flow.port16[1] = 0;
|
||||
}
|
||||
if (data_end - data > 1400)
|
||||
return XDP_DROP;
|
||||
data_stats = bpf_map_lookup_elem(&stats, &stats_key);
|
||||
if (!data_stats)
|
||||
return XDP_DROP;
|
||||
data_stats->v1 += 1;
|
||||
if (!dst) {
|
||||
if (vip_info->flags & (1 << 0))
|
||||
pckt.flow.port16[0] = 0;
|
||||
if (!(pckt.flags & (1 << 1)) && !(vip_info->flags & (1 << 1)))
|
||||
connection_table_lookup(&dst, &pckt, lru_map);
|
||||
if (dst)
|
||||
goto out;
|
||||
if (pckt.flow.proto == IPPROTO_TCP) {
|
||||
struct lb_stats *lru_stats =
|
||||
bpf_map_lookup_elem(&stats, &lru_stats_key);
|
||||
|
||||
if (!lru_stats)
|
||||
return XDP_DROP;
|
||||
if (pckt.flags & (1 << 1))
|
||||
lru_stats->v1 += 1;
|
||||
else
|
||||
lru_stats->v2 += 1;
|
||||
}
|
||||
if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6, lru_map))
|
||||
return XDP_DROP;
|
||||
data_stats->v2 += 1;
|
||||
}
|
||||
out:
|
||||
cval = bpf_map_lookup_elem(&ctl_array, &mac_addr_pos);
|
||||
if (!cval)
|
||||
return XDP_DROP;
|
||||
if (dst->flags & (1 << 0)) {
|
||||
if (!encap_v6(xdp, cval, &pckt, dst, pkt_bytes))
|
||||
return XDP_DROP;
|
||||
} else {
|
||||
if (!encap_v4(xdp, cval, &pckt, dst, pkt_bytes))
|
||||
return XDP_DROP;
|
||||
}
|
||||
vip_num = vip_info->vip_num;
|
||||
data_stats = bpf_map_lookup_elem(&stats, &vip_num);
|
||||
if (!data_stats)
|
||||
return XDP_DROP;
|
||||
data_stats->v1 += 1;
|
||||
data_stats->v2 += pkt_bytes;
|
||||
|
||||
data = (void *)(long)xdp->data;
|
||||
data_end = (void *)(long)xdp->data_end;
|
||||
if (data + 4 > data_end)
|
||||
return XDP_DROP;
|
||||
*(u32 *)data = dst->dst;
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
__attribute__ ((section("xdp-test"), used))
|
||||
int balancer_ingress(struct xdp_md *ctx)
|
||||
{
|
||||
void *data = (void *)(long)ctx->data;
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
struct eth_hdr *eth = data;
|
||||
__u32 eth_proto;
|
||||
__u32 nh_off;
|
||||
|
||||
nh_off = sizeof(struct eth_hdr);
|
||||
if (data + nh_off > data_end)
|
||||
return XDP_DROP;
|
||||
eth_proto = eth->eth_proto;
|
||||
if (eth_proto == 8)
|
||||
return process_packet(data, nh_off, data_end, 0, ctx);
|
||||
else if (eth_proto == 56710)
|
||||
return process_packet(data, nh_off, data_end, 1, ctx);
|
||||
else
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
char _license[] __attribute__ ((section("license"), used)) = "GPL";
|
||||
int _version __attribute__ ((section("version"), used)) = 1;
|
||||
|
|
@ -175,96 +175,118 @@ trap "test_finish" EXIT
|
|||
echo "ABCD0123" >"$FW"
|
||||
NAME=$(basename "$FW")
|
||||
|
||||
DEVPATH="$DIR"/"nope-$NAME"/loading
|
||||
test_syfs_timeout()
|
||||
{
|
||||
DEVPATH="$DIR"/"nope-$NAME"/loading
|
||||
|
||||
# Test failure when doing nothing (timeout works).
|
||||
echo -n 2 >/sys/class/firmware/timeout
|
||||
echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
|
||||
# Test failure when doing nothing (timeout works).
|
||||
echo -n 2 >/sys/class/firmware/timeout
|
||||
echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
|
||||
|
||||
# Give the kernel some time to load the loading file, must be less
|
||||
# than the timeout above.
|
||||
sleep 1
|
||||
if [ ! -f $DEVPATH ]; then
|
||||
echo "$0: fallback mechanism immediately cancelled"
|
||||
echo ""
|
||||
echo "The file never appeared: $DEVPATH"
|
||||
echo ""
|
||||
echo "This might be a distribution udev rule setup by your distribution"
|
||||
echo "to immediately cancel all fallback requests, this must be"
|
||||
echo "removed before running these tests. To confirm look for"
|
||||
echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
|
||||
echo "and see if you have something like this:"
|
||||
echo ""
|
||||
echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
|
||||
echo ""
|
||||
echo "If you do remove this file or comment out this line before"
|
||||
echo "proceeding with these tests."
|
||||
exit 1
|
||||
fi
|
||||
# Give the kernel some time to load the loading file, must be less
|
||||
# than the timeout above.
|
||||
sleep 1
|
||||
if [ ! -f $DEVPATH ]; then
|
||||
echo "$0: fallback mechanism immediately cancelled"
|
||||
echo ""
|
||||
echo "The file never appeared: $DEVPATH"
|
||||
echo ""
|
||||
echo "This might be a distribution udev rule setup by your distribution"
|
||||
echo "to immediately cancel all fallback requests, this must be"
|
||||
echo "removed before running these tests. To confirm look for"
|
||||
echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
|
||||
echo "and see if you have something like this:"
|
||||
echo ""
|
||||
echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
|
||||
echo ""
|
||||
echo "If you do remove this file or comment out this line before"
|
||||
echo "proceeding with these tests."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: timeout works"
|
||||
fi
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: timeout works"
|
||||
fi
|
||||
}
|
||||
|
||||
# Put timeout high enough for us to do work but not so long that failures
|
||||
# slow down this test too much.
|
||||
echo 4 >/sys/class/firmware/timeout
|
||||
run_sysfs_main_tests()
|
||||
{
|
||||
test_syfs_timeout
|
||||
# Put timeout high enough for us to do work but not so long that failures
|
||||
# slow down this test too much.
|
||||
echo 4 >/sys/class/firmware/timeout
|
||||
|
||||
# Load this script instead of the desired firmware.
|
||||
load_fw "$NAME" "$0"
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: firmware comparison works"
|
||||
fi
|
||||
# Load this script instead of the desired firmware.
|
||||
load_fw "$NAME" "$0"
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not expected to match" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: firmware comparison works"
|
||||
fi
|
||||
|
||||
# Do a proper load, which should work correctly.
|
||||
load_fw "$NAME" "$FW"
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: fallback mechanism works"
|
||||
fi
|
||||
|
||||
load_fw_cancel "nope-$NAME" "$FW"
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was expected to be cancelled" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: cancelling fallback mechanism works"
|
||||
fi
|
||||
|
||||
if load_fw_custom "$NAME" "$FW" ; then
|
||||
# Do a proper load, which should work correctly.
|
||||
load_fw "$NAME" "$FW"
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: custom fallback loading mechanism works"
|
||||
echo "$0: fallback mechanism works"
|
||||
fi
|
||||
fi
|
||||
|
||||
if load_fw_custom_cancel "nope-$NAME" "$FW" ; then
|
||||
load_fw_cancel "nope-$NAME" "$FW"
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was expected to be cancelled" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: cancelling custom fallback mechanism works"
|
||||
echo "$0: cancelling fallback mechanism works"
|
||||
fi
|
||||
fi
|
||||
|
||||
set +e
|
||||
load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo "$0: SIGCHLD on sync ignored as expected" >&2
|
||||
else
|
||||
echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
|
||||
exit 1
|
||||
fi
|
||||
set -e
|
||||
set +e
|
||||
load_fw_fallback_with_child "nope-signal-$NAME" "$FW"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo "$0: SIGCHLD on sync ignored as expected" >&2
|
||||
else
|
||||
echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2
|
||||
exit 1
|
||||
fi
|
||||
set -e
|
||||
}
|
||||
|
||||
run_sysfs_custom_load_tests()
|
||||
{
|
||||
if load_fw_custom "$NAME" "$FW" ; then
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: custom fallback loading mechanism works"
|
||||
fi
|
||||
fi
|
||||
|
||||
if load_fw_custom "$NAME" "$FW" ; then
|
||||
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was not loaded" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: custom fallback loading mechanism works"
|
||||
fi
|
||||
fi
|
||||
|
||||
if load_fw_custom_cancel "nope-$NAME" "$FW" ; then
|
||||
if diff -q "$FW" /dev/test_firmware >/dev/null ; then
|
||||
echo "$0: firmware was expected to be cancelled" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "$0: cancelling custom fallback mechanism works"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
run_sysfs_main_tests
|
||||
run_sysfs_custom_load_tests
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -45,7 +45,10 @@ test_finish()
|
|||
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
|
||||
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
|
||||
fi
|
||||
echo -n "$OLD_PATH" >/sys/module/firmware_class/parameters/path
|
||||
if [ "$OLD_FWPATH" = "" ]; then
|
||||
OLD_FWPATH=" "
|
||||
fi
|
||||
echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path
|
||||
rm -f "$FW"
|
||||
rmdir "$FWPATH"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,24 @@ case `uname -m` in
|
|||
*) OFFS=0;;
|
||||
esac
|
||||
|
||||
echo "Setup up to 256 kprobes"
|
||||
grep t /proc/kallsyms | cut -f3 -d" " | grep -v .*\\..* | \
|
||||
head -n 256 | while read i; do echo p ${i}+${OFFS} ; done > kprobe_events ||:
|
||||
if [ -d events/kprobes ]; then
|
||||
echo 0 > events/kprobes/enable
|
||||
echo > kprobe_events
|
||||
fi
|
||||
|
||||
N=0
|
||||
echo "Setup up kprobes on first available 256 text symbols"
|
||||
grep -i " t " /proc/kallsyms | cut -f3 -d" " | grep -v .*\\..* | \
|
||||
while read i; do
|
||||
echo p ${i}+${OFFS} >> kprobe_events && N=$((N+1)) ||:
|
||||
test $N -eq 256 && break
|
||||
done
|
||||
|
||||
L=`wc -l kprobe_events`
|
||||
if [ $L -ne $N ]; then
|
||||
echo "The number of kprobes events ($L) is not $N"
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
echo 1 > events/kprobes/enable
|
||||
echo 0 > events/kprobes/enable
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# gen_kselftest_tar
|
||||
# Generate kselftest tarball
|
||||
# Author: Shuah Khan <[email protected]>
|
||||
# Copyright (C) 2015 Samsung Electronics Co., Ltd.
|
||||
|
||||
# This software may be freely redistributed under the terms of the GNU
|
||||
# General Public License (GPLv2).
|
||||
|
||||
# main
|
||||
main()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* kselftest.h: kselftest framework return codes to include from
|
||||
* selftests.
|
||||
|
|
@ -5,7 +6,6 @@
|
|||
* Copyright (c) 2014 Shuah Khan <shuahkh@osg.samsung.com>
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
#ifndef __KSELFTEST_H
|
||||
#define __KSELFTEST_H
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Kselftest Install
|
||||
# Install kselftest tests
|
||||
# Author: Shuah Khan <[email protected]>
|
||||
# Copyright (C) 2015 Samsung Electronics Co., Ltd.
|
||||
|
||||
# This software may be freely redistributed under the terms of the GNU
|
||||
# General Public License (GPLv2).
|
||||
|
||||
install_loc=`pwd`
|
||||
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ endif
|
|||
define EMIT_TESTS
|
||||
@for TEST in $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_PROGS); do \
|
||||
BASENAME_TEST=`basename $$TEST`; \
|
||||
echo "(./$$BASENAME_TEST > /tmp/$$BASENAME_TEST 2>&1 && echo \"selftests: $$BASENAME_TEST [PASS]\") || echo \"selftests: $$BASENAME_TEST [FAIL]\""; \
|
||||
echo "(./$$BASENAME_TEST >> \$$OUTPUT 2>&1 && echo \"selftests: $$BASENAME_TEST [PASS]\") || echo \"selftests: $$BASENAME_TEST [FAIL]\""; \
|
||||
done;
|
||||
endef
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
TEST_PROGS := media_device_test media_device_open video_device_test
|
||||
all: $(TEST_PROGS)
|
||||
TEST_GEN_PROGS := media_device_test media_device_open video_device_test
|
||||
all: $(TEST_GEN_PROGS)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
clean:
|
||||
rm -fr media_device_test media_device_open video_device_test
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* media_device_open.c - Media Controller Device Open Test
|
||||
*
|
||||
* Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
|
||||
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* media_device_test.c - Media Controller Device ioctl loop Test
|
||||
*
|
||||
* Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
|
||||
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* video_device_test - Video Device Test
|
||||
*
|
||||
* Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
|
||||
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -12,3 +12,8 @@ fuse_mnt.o: CFLAGS += $(shell pkg-config fuse --cflags)
|
|||
include ../lib.mk
|
||||
|
||||
$(OUTPUT)/fuse_mnt: LDLIBS += $(shell pkg-config fuse --libs)
|
||||
|
||||
$(OUTPUT)/memfd_test: memfd_test.c common.o
|
||||
$(OUTPUT)/fuse_test: fuse_test.c common.o
|
||||
|
||||
EXTRA_CLEAN = common.o
|
||||
|
|
|
|||
46
tools/testing/selftests/memfd/common.c
Normal file
46
tools/testing/selftests/memfd/common.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#define __EXPORTED_HEADERS__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int hugetlbfs_test = 0;
|
||||
|
||||
/*
|
||||
* Copied from mlock2-tests.c
|
||||
*/
|
||||
unsigned long default_huge_page_size(void)
|
||||
{
|
||||
unsigned long hps = 0;
|
||||
char *line = NULL;
|
||||
size_t linelen = 0;
|
||||
FILE *f = fopen("/proc/meminfo", "r");
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
while (getline(&line, &linelen, f) > 0) {
|
||||
if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
|
||||
hps <<= 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
return hps;
|
||||
}
|
||||
|
||||
int sys_memfd_create(const char *name, unsigned int flags)
|
||||
{
|
||||
if (hugetlbfs_test)
|
||||
flags |= MFD_HUGETLB;
|
||||
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
9
tools/testing/selftests/memfd/common.h
Normal file
9
tools/testing/selftests/memfd/common.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef COMMON_H_
|
||||
#define COMMON_H_
|
||||
|
||||
extern int hugetlbfs_test;
|
||||
|
||||
unsigned long default_huge_page_size(void);
|
||||
int sys_memfd_create(const char *name, unsigned int flags);
|
||||
|
||||
#endif
|
||||
|
|
@ -33,14 +33,12 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define MFD_DEF_SIZE 8192
|
||||
#define STACK_SIZE 65536
|
||||
|
||||
static int sys_memfd_create(const char *name,
|
||||
unsigned int flags)
|
||||
{
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
static size_t mfd_def_size = MFD_DEF_SIZE;
|
||||
|
||||
static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)
|
||||
{
|
||||
|
|
@ -127,7 +125,7 @@ static void *mfd_assert_mmap_shared(int fd)
|
|||
void *p;
|
||||
|
||||
p = mmap(NULL,
|
||||
MFD_DEF_SIZE,
|
||||
mfd_def_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
|
|
@ -145,7 +143,7 @@ static void *mfd_assert_mmap_private(int fd)
|
|||
void *p;
|
||||
|
||||
p = mmap(NULL,
|
||||
MFD_DEF_SIZE,
|
||||
mfd_def_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE,
|
||||
fd,
|
||||
|
|
@ -178,7 +176,7 @@ static int sealing_thread_fn(void *arg)
|
|||
usleep(200000);
|
||||
|
||||
/* unmount mapping before sealing to avoid i_mmap_writable failures */
|
||||
munmap(global_p, MFD_DEF_SIZE);
|
||||
munmap(global_p, mfd_def_size);
|
||||
|
||||
/* Try sealing the global file; expect EBUSY or success. Current
|
||||
* kernels will never succeed, but in the future, kernels might
|
||||
|
|
@ -228,7 +226,7 @@ static void join_sealing_thread(pid_t pid)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const char zero[MFD_DEF_SIZE];
|
||||
char *zero;
|
||||
int fd, mfd, r;
|
||||
void *p;
|
||||
int was_sealed;
|
||||
|
|
@ -239,6 +237,25 @@ int main(int argc, char **argv)
|
|||
abort();
|
||||
}
|
||||
|
||||
if (argc >= 3) {
|
||||
if (!strcmp(argv[2], "hugetlbfs")) {
|
||||
unsigned long hpage_size = default_huge_page_size();
|
||||
|
||||
if (!hpage_size) {
|
||||
printf("Unable to determine huge page size\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
hugetlbfs_test = 1;
|
||||
mfd_def_size = hpage_size * 2;
|
||||
} else {
|
||||
printf("Unknown option: %s\n", argv[2]);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
zero = calloc(sizeof(*zero), mfd_def_size);
|
||||
|
||||
/* open FUSE memfd file for GUP testing */
|
||||
printf("opening: %s\n", argv[1]);
|
||||
fd = open(argv[1], O_RDONLY | O_CLOEXEC);
|
||||
|
|
@ -249,7 +266,7 @@ int main(int argc, char **argv)
|
|||
|
||||
/* create new memfd-object */
|
||||
mfd = mfd_assert_new("kern_memfd_fuse",
|
||||
MFD_DEF_SIZE,
|
||||
mfd_def_size,
|
||||
MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
|
||||
/* mmap memfd-object for writing */
|
||||
|
|
@ -268,7 +285,7 @@ int main(int argc, char **argv)
|
|||
* This guarantees that the receive-buffer is pinned for 1s until the
|
||||
* data is written into it. The racing ADD_SEALS should thus fail as
|
||||
* the pages are still pinned. */
|
||||
r = read(fd, p, MFD_DEF_SIZE);
|
||||
r = read(fd, p, mfd_def_size);
|
||||
if (r < 0) {
|
||||
printf("read() failed: %m\n");
|
||||
abort();
|
||||
|
|
@ -295,10 +312,10 @@ int main(int argc, char **argv)
|
|||
* enough to avoid any in-flight writes. */
|
||||
|
||||
p = mfd_assert_mmap_private(mfd);
|
||||
if (was_sealed && memcmp(p, zero, MFD_DEF_SIZE)) {
|
||||
if (was_sealed && memcmp(p, zero, mfd_def_size)) {
|
||||
printf("memfd sealed during read() but data not discarded\n");
|
||||
abort();
|
||||
} else if (!was_sealed && !memcmp(p, zero, MFD_DEF_SIZE)) {
|
||||
} else if (!was_sealed && !memcmp(p, zero, mfd_def_size)) {
|
||||
printf("memfd sealed after read() but data discarded\n");
|
||||
abort();
|
||||
}
|
||||
|
|
@ -307,6 +324,7 @@ int main(int argc, char **argv)
|
|||
close(fd);
|
||||
|
||||
printf("fuse: DONE\n");
|
||||
free(zero);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define MEMFD_STR "memfd:"
|
||||
#define MEMFD_HUGE_STR "memfd-hugetlb:"
|
||||
#define SHARED_FT_STR "(shared file-table)"
|
||||
|
||||
#define MFD_DEF_SIZE 8192
|
||||
|
|
@ -28,41 +31,8 @@
|
|||
/*
|
||||
* Default is not to test hugetlbfs
|
||||
*/
|
||||
static int hugetlbfs_test;
|
||||
static size_t mfd_def_size = MFD_DEF_SIZE;
|
||||
|
||||
/*
|
||||
* Copied from mlock2-tests.c
|
||||
*/
|
||||
static unsigned long default_huge_page_size(void)
|
||||
{
|
||||
unsigned long hps = 0;
|
||||
char *line = NULL;
|
||||
size_t linelen = 0;
|
||||
FILE *f = fopen("/proc/meminfo", "r");
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
while (getline(&line, &linelen, f) > 0) {
|
||||
if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
|
||||
hps <<= 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
return hps;
|
||||
}
|
||||
|
||||
static int sys_memfd_create(const char *name,
|
||||
unsigned int flags)
|
||||
{
|
||||
if (hugetlbfs_test)
|
||||
flags |= MFD_HUGETLB;
|
||||
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
static const char *memfd_str = MEMFD_STR;
|
||||
|
||||
static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags)
|
||||
{
|
||||
|
|
@ -513,6 +483,10 @@ static void mfd_assert_grow_write(int fd)
|
|||
static char *buf;
|
||||
ssize_t l;
|
||||
|
||||
/* hugetlbfs does not support write */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
buf = malloc(mfd_def_size * 8);
|
||||
if (!buf) {
|
||||
printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);
|
||||
|
|
@ -533,6 +507,10 @@ static void mfd_fail_grow_write(int fd)
|
|||
static char *buf;
|
||||
ssize_t l;
|
||||
|
||||
/* hugetlbfs does not support write */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
buf = malloc(mfd_def_size * 8);
|
||||
if (!buf) {
|
||||
printf("malloc(%zu) failed: %m\n", mfd_def_size * 8);
|
||||
|
|
@ -598,7 +576,7 @@ static void test_create(void)
|
|||
char buf[2048];
|
||||
int fd;
|
||||
|
||||
printf("%s CREATE\n", MEMFD_STR);
|
||||
printf("%s CREATE\n", memfd_str);
|
||||
|
||||
/* test NULL name */
|
||||
mfd_fail_new(NULL, 0);
|
||||
|
|
@ -627,18 +605,13 @@ static void test_create(void)
|
|||
fd = mfd_assert_new("", 0, MFD_CLOEXEC);
|
||||
close(fd);
|
||||
|
||||
if (!hugetlbfs_test) {
|
||||
/* verify MFD_ALLOW_SEALING is allowed */
|
||||
fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING);
|
||||
close(fd);
|
||||
/* verify MFD_ALLOW_SEALING is allowed */
|
||||
fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING);
|
||||
close(fd);
|
||||
|
||||
/* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */
|
||||
fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC);
|
||||
close(fd);
|
||||
} else {
|
||||
/* sealing is not supported on hugetlbfs */
|
||||
mfd_fail_new("", MFD_ALLOW_SEALING);
|
||||
}
|
||||
/* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */
|
||||
fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -649,11 +622,7 @@ static void test_basic(void)
|
|||
{
|
||||
int fd;
|
||||
|
||||
/* hugetlbfs does not contain sealing support */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
printf("%s BASIC\n", MEMFD_STR);
|
||||
printf("%s BASIC\n", memfd_str);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_basic",
|
||||
mfd_def_size,
|
||||
|
|
@ -697,28 +666,6 @@ static void test_basic(void)
|
|||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* hugetlbfs doesn't support seals or write, so just verify grow and shrink
|
||||
* on a hugetlbfs file created via memfd_create.
|
||||
*/
|
||||
static void test_hugetlbfs_grow_shrink(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
printf("%s HUGETLBFS-GROW-SHRINK\n", MEMFD_STR);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_seal_write",
|
||||
mfd_def_size,
|
||||
MFD_CLOEXEC);
|
||||
|
||||
mfd_assert_read(fd);
|
||||
mfd_assert_write(fd);
|
||||
mfd_assert_shrink(fd);
|
||||
mfd_assert_grow(fd);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test SEAL_WRITE
|
||||
* Test whether SEAL_WRITE actually prevents modifications.
|
||||
|
|
@ -727,14 +674,7 @@ static void test_seal_write(void)
|
|||
{
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* hugetlbfs does not contain sealing or write support. Just test
|
||||
* basic grow and shrink via test_hugetlbfs_grow_shrink.
|
||||
*/
|
||||
if (hugetlbfs_test)
|
||||
return test_hugetlbfs_grow_shrink();
|
||||
|
||||
printf("%s SEAL-WRITE\n", MEMFD_STR);
|
||||
printf("%s SEAL-WRITE\n", memfd_str);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_seal_write",
|
||||
mfd_def_size,
|
||||
|
|
@ -760,11 +700,7 @@ static void test_seal_shrink(void)
|
|||
{
|
||||
int fd;
|
||||
|
||||
/* hugetlbfs does not contain sealing support */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
printf("%s SEAL-SHRINK\n", MEMFD_STR);
|
||||
printf("%s SEAL-SHRINK\n", memfd_str);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_seal_shrink",
|
||||
mfd_def_size,
|
||||
|
|
@ -790,11 +726,7 @@ static void test_seal_grow(void)
|
|||
{
|
||||
int fd;
|
||||
|
||||
/* hugetlbfs does not contain sealing support */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
printf("%s SEAL-GROW\n", MEMFD_STR);
|
||||
printf("%s SEAL-GROW\n", memfd_str);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_seal_grow",
|
||||
mfd_def_size,
|
||||
|
|
@ -820,11 +752,7 @@ static void test_seal_resize(void)
|
|||
{
|
||||
int fd;
|
||||
|
||||
/* hugetlbfs does not contain sealing support */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
printf("%s SEAL-RESIZE\n", MEMFD_STR);
|
||||
printf("%s SEAL-RESIZE\n", memfd_str);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_seal_resize",
|
||||
mfd_def_size,
|
||||
|
|
@ -842,32 +770,6 @@ static void test_seal_resize(void)
|
|||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* hugetlbfs does not support seals. Basic test to dup the memfd created
|
||||
* fd and perform some basic operations on it.
|
||||
*/
|
||||
static void hugetlbfs_dup(char *b_suffix)
|
||||
{
|
||||
int fd, fd2;
|
||||
|
||||
printf("%s HUGETLBFS-DUP %s\n", MEMFD_STR, b_suffix);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_share_dup",
|
||||
mfd_def_size,
|
||||
MFD_CLOEXEC);
|
||||
|
||||
fd2 = mfd_assert_dup(fd);
|
||||
|
||||
mfd_assert_read(fd);
|
||||
mfd_assert_write(fd);
|
||||
|
||||
mfd_assert_shrink(fd2);
|
||||
mfd_assert_grow(fd2);
|
||||
|
||||
close(fd2);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test sharing via dup()
|
||||
* Test that seals are shared between dupped FDs and they're all equal.
|
||||
|
|
@ -876,16 +778,7 @@ static void test_share_dup(char *banner, char *b_suffix)
|
|||
{
|
||||
int fd, fd2;
|
||||
|
||||
/*
|
||||
* hugetlbfs does not contain sealing support. Perform some
|
||||
* basic testing on dup'ed fd instead via hugetlbfs_dup.
|
||||
*/
|
||||
if (hugetlbfs_test) {
|
||||
hugetlbfs_dup(b_suffix);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s %s %s\n", MEMFD_STR, banner, b_suffix);
|
||||
printf("%s %s %s\n", memfd_str, banner, b_suffix);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_share_dup",
|
||||
mfd_def_size,
|
||||
|
|
@ -927,11 +820,7 @@ static void test_share_mmap(char *banner, char *b_suffix)
|
|||
int fd;
|
||||
void *p;
|
||||
|
||||
/* hugetlbfs does not contain sealing support */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
printf("%s %s %s\n", MEMFD_STR, banner, b_suffix);
|
||||
printf("%s %s %s\n", memfd_str, banner, b_suffix);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_share_mmap",
|
||||
mfd_def_size,
|
||||
|
|
@ -955,32 +844,6 @@ static void test_share_mmap(char *banner, char *b_suffix)
|
|||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic test to make sure we can open the hugetlbfs fd via /proc and
|
||||
* perform some simple operations on it.
|
||||
*/
|
||||
static void hugetlbfs_proc_open(char *b_suffix)
|
||||
{
|
||||
int fd, fd2;
|
||||
|
||||
printf("%s HUGETLBFS-PROC-OPEN %s\n", MEMFD_STR, b_suffix);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_share_open",
|
||||
mfd_def_size,
|
||||
MFD_CLOEXEC);
|
||||
|
||||
fd2 = mfd_assert_open(fd, O_RDWR, 0);
|
||||
|
||||
mfd_assert_read(fd);
|
||||
mfd_assert_write(fd);
|
||||
|
||||
mfd_assert_shrink(fd2);
|
||||
mfd_assert_grow(fd2);
|
||||
|
||||
close(fd2);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test sealing with open(/proc/self/fd/%d)
|
||||
* Via /proc we can get access to a separate file-context for the same memfd.
|
||||
|
|
@ -991,16 +854,7 @@ static void test_share_open(char *banner, char *b_suffix)
|
|||
{
|
||||
int fd, fd2;
|
||||
|
||||
/*
|
||||
* hugetlbfs does not contain sealing support. So test basic
|
||||
* functionality of using /proc fd via hugetlbfs_proc_open
|
||||
*/
|
||||
if (hugetlbfs_test) {
|
||||
hugetlbfs_proc_open(b_suffix);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s %s %s\n", MEMFD_STR, banner, b_suffix);
|
||||
printf("%s %s %s\n", memfd_str, banner, b_suffix);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_share_open",
|
||||
mfd_def_size,
|
||||
|
|
@ -1043,11 +897,7 @@ static void test_share_fork(char *banner, char *b_suffix)
|
|||
int fd;
|
||||
pid_t pid;
|
||||
|
||||
/* hugetlbfs does not contain sealing support */
|
||||
if (hugetlbfs_test)
|
||||
return;
|
||||
|
||||
printf("%s %s %s\n", MEMFD_STR, banner, b_suffix);
|
||||
printf("%s %s %s\n", memfd_str, banner, b_suffix);
|
||||
|
||||
fd = mfd_assert_new("kern_memfd_share_fork",
|
||||
mfd_def_size,
|
||||
|
|
@ -1083,7 +933,11 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
hugetlbfs_test = 1;
|
||||
memfd_str = MEMFD_HUGE_STR;
|
||||
mfd_def_size = hpage_size * 2;
|
||||
} else {
|
||||
printf("Unknown option: %s\n", argv[1]);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ set -e
|
|||
|
||||
mkdir mnt
|
||||
./fuse_mnt ./mnt
|
||||
./fuse_test ./mnt/memfd
|
||||
./fuse_test ./mnt/memfd $@
|
||||
fusermount -u ./mnt
|
||||
rmdir ./mnt
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ fi
|
|||
# Run the hugetlbfs test
|
||||
#
|
||||
./memfd_test hugetlbfs
|
||||
./run_fuse_test.sh hugetlbfs
|
||||
|
||||
#
|
||||
# Give back any huge pages allocated for the test
|
||||
|
|
|
|||
|
|
@ -5,6 +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
|
||||
TEST_GEN_FILES = socket
|
||||
TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy
|
||||
TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa
|
||||
|
|
|
|||
429
tools/testing/selftests/net/fib_tests.sh
Executable file
429
tools/testing/selftests/net/fib_tests.sh
Executable file
|
|
@ -0,0 +1,429 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# This test is for checking IPv4 and IPv6 FIB behavior in response to
|
||||
# different events.
|
||||
|
||||
ret=0
|
||||
|
||||
check_err()
|
||||
{
|
||||
if [ $ret -eq 0 ]; then
|
||||
ret=$1
|
||||
fi
|
||||
}
|
||||
|
||||
check_fail()
|
||||
{
|
||||
if [ $1 -eq 0 ]; then
|
||||
ret=1
|
||||
fi
|
||||
}
|
||||
|
||||
netns_create()
|
||||
{
|
||||
local testns=$1
|
||||
|
||||
ip netns add $testns
|
||||
ip netns exec $testns ip link set dev lo up
|
||||
}
|
||||
|
||||
fib_unreg_unicast_test()
|
||||
{
|
||||
ret=0
|
||||
|
||||
netns_create "testns"
|
||||
|
||||
ip netns exec testns ip link add dummy0 type dummy
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
|
||||
ip netns exec testns ip address add 198.51.100.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy0
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
|
||||
check_fail $?
|
||||
|
||||
ip netns del testns
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: unicast route test"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: unicast route test"
|
||||
}
|
||||
|
||||
fib_unreg_multipath_test()
|
||||
{
|
||||
ret=0
|
||||
|
||||
netns_create "testns"
|
||||
|
||||
ip netns exec testns ip link add dummy0 type dummy
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
|
||||
ip netns exec testns ip link add dummy1 type dummy
|
||||
ip netns exec testns ip link set dev dummy1 up
|
||||
|
||||
ip netns exec testns ip address add 198.51.100.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip address add 192.0.2.1/24 dev dummy1
|
||||
ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy1
|
||||
|
||||
ip netns exec testns 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 exec testns ip -6 route add 2001:db8:3::/64 \
|
||||
nexthop via 2001:db8:1::2 dev dummy0 \
|
||||
nexthop via 2001:db8:2::2 dev dummy1
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy0
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
|
||||
# In IPv6 we do not flush the entire multipath route.
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy1
|
||||
|
||||
ip netns del testns
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: multipath route test"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: multipath route test"
|
||||
}
|
||||
|
||||
fib_unreg_test()
|
||||
{
|
||||
echo "Running netdev unregister tests"
|
||||
|
||||
fib_unreg_unicast_test
|
||||
fib_unreg_multipath_test
|
||||
}
|
||||
|
||||
fib_down_unicast_test()
|
||||
{
|
||||
ret=0
|
||||
|
||||
netns_create "testns"
|
||||
|
||||
ip netns exec testns ip link add dummy0 type dummy
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
|
||||
ip netns exec testns ip address add 198.51.100.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 down
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy0
|
||||
|
||||
ip netns del testns
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: unicast route test"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: unicast route test"
|
||||
}
|
||||
|
||||
fib_down_multipath_test_do()
|
||||
{
|
||||
local down_dev=$1
|
||||
local up_dev=$2
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 \
|
||||
oif $down_dev &> /dev/null
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 \
|
||||
oif $down_dev &> /dev/null
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 \
|
||||
oif $up_dev &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 \
|
||||
oif $up_dev &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 | \
|
||||
grep $down_dev | grep -q "dead linkdown"
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 | \
|
||||
grep $down_dev | grep -q "dead linkdown"
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 | \
|
||||
grep $up_dev | grep -q "dead linkdown"
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 | \
|
||||
grep $up_dev | grep -q "dead linkdown"
|
||||
check_fail $?
|
||||
}
|
||||
|
||||
fib_down_multipath_test()
|
||||
{
|
||||
ret=0
|
||||
|
||||
netns_create "testns"
|
||||
|
||||
ip netns exec testns ip link add dummy0 type dummy
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
|
||||
ip netns exec testns ip link add dummy1 type dummy
|
||||
ip netns exec testns ip link set dev dummy1 up
|
||||
|
||||
ip netns exec testns ip address add 198.51.100.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip address add 192.0.2.1/24 dev dummy1
|
||||
ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy1
|
||||
|
||||
ip netns exec testns 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 exec testns ip -6 route add 2001:db8:3::/64 \
|
||||
nexthop via 2001:db8:1::2 dev dummy0 \
|
||||
nexthop via 2001:db8:2::2 dev dummy1
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 down
|
||||
check_err $?
|
||||
|
||||
fib_down_multipath_test_do "dummy0" "dummy1"
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
check_err $?
|
||||
ip netns exec testns ip link set dev dummy1 down
|
||||
check_err $?
|
||||
|
||||
fib_down_multipath_test_do "dummy1" "dummy0"
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 down
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 203.0.113.1 &> /dev/null
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:3::1 &> /dev/null
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy1
|
||||
ip netns exec testns ip link del dev dummy0
|
||||
|
||||
ip netns del testns
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: multipath route test"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: multipath route test"
|
||||
}
|
||||
|
||||
fib_down_test()
|
||||
{
|
||||
echo "Running netdev down tests"
|
||||
|
||||
fib_down_unicast_test
|
||||
fib_down_multipath_test
|
||||
}
|
||||
|
||||
fib_carrier_local_test()
|
||||
{
|
||||
ret=0
|
||||
|
||||
# Local routes should not be affected when carrier changes.
|
||||
netns_create "testns"
|
||||
|
||||
ip netns exec testns ip link add dummy0 type dummy
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 carrier on
|
||||
|
||||
ip netns exec testns ip address add 198.51.100.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.1 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.1 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 carrier off
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.1 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.1 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::1 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip address add 192.0.2.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip route get fibmatch 192.0.2.1 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:2::1 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 192.0.2.1 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:2::1 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy0
|
||||
|
||||
ip netns del testns
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: local route carrier test"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: local route carrier test"
|
||||
}
|
||||
|
||||
fib_carrier_unicast_test()
|
||||
{
|
||||
ret=0
|
||||
|
||||
netns_create "testns"
|
||||
|
||||
ip netns exec testns ip link add dummy0 type dummy
|
||||
ip netns exec testns ip link set dev dummy0 up
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 carrier on
|
||||
|
||||
ip netns exec testns ip address add 198.51.100.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:1::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 | \
|
||||
grep -q "linkdown"
|
||||
check_fail $?
|
||||
|
||||
ip netns exec testns ip link set dev dummy0 carrier off
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 198.51.100.2 | \
|
||||
grep -q "linkdown"
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:1::2 | \
|
||||
grep -q "linkdown"
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip address add 192.0.2.1/24 dev dummy0
|
||||
ip netns exec testns ip -6 address add 2001:db8:2::1/64 dev dummy0
|
||||
|
||||
ip netns exec testns ip route get fibmatch 192.0.2.2 &> /dev/null
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:2::2 &> /dev/null
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip route get fibmatch 192.0.2.2 | \
|
||||
grep -q "linkdown"
|
||||
check_err $?
|
||||
ip netns exec testns ip -6 route get fibmatch 2001:db8:2::2 | \
|
||||
grep -q "linkdown"
|
||||
check_err $?
|
||||
|
||||
ip netns exec testns ip link del dev dummy0
|
||||
|
||||
ip netns del testns
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: unicast route carrier test"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: unicast route carrier test"
|
||||
}
|
||||
|
||||
fib_carrier_test()
|
||||
{
|
||||
echo "Running netdev carrier change tests"
|
||||
|
||||
fib_carrier_local_test
|
||||
fib_carrier_unicast_test
|
||||
}
|
||||
|
||||
fib_test()
|
||||
{
|
||||
fib_unreg_test
|
||||
fib_down_test
|
||||
fib_carrier_test
|
||||
}
|
||||
|
||||
if [ "$(id -u)" -ne 0 ];then
|
||||
echo "SKIP: Need root privileges"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -x "$(command -v ip)" ]; then
|
||||
echo "SKIP: Could not run test without ip tool"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ip route help 2>&1 | grep -q fibmatch
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "SKIP: iproute2 too old, missing fibmatch"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
fib_test
|
||||
|
||||
exit $ret
|
||||
|
|
@ -259,22 +259,28 @@ static int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len)
|
|||
return sizeof(*ip6h);
|
||||
}
|
||||
|
||||
static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
|
||||
|
||||
static void setup_sockaddr(int domain, const char *str_addr,
|
||||
struct sockaddr_storage *sockaddr)
|
||||
{
|
||||
struct sockaddr_in6 *addr6 = (void *) sockaddr;
|
||||
struct sockaddr_in *addr4 = (void *) sockaddr;
|
||||
|
||||
switch (domain) {
|
||||
case PF_INET:
|
||||
memset(addr4, 0, sizeof(*addr4));
|
||||
addr4->sin_family = AF_INET;
|
||||
addr4->sin_port = htons(cfg_port);
|
||||
if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
|
||||
if (str_addr &&
|
||||
inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
|
||||
error(1, 0, "ipv4 parse error: %s", str_addr);
|
||||
break;
|
||||
case PF_INET6:
|
||||
memset(addr6, 0, sizeof(*addr6));
|
||||
addr6->sin6_family = AF_INET6;
|
||||
addr6->sin6_port = htons(cfg_port);
|
||||
if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
|
||||
if (str_addr &&
|
||||
inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
|
||||
error(1, 0, "ipv6 parse error: %s", str_addr);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -603,6 +609,7 @@ static void parse_opts(int argc, char **argv)
|
|||
sizeof(struct tcphdr) -
|
||||
40 /* max tcp options */;
|
||||
int c;
|
||||
char *daddr = NULL, *saddr = NULL;
|
||||
|
||||
cfg_payload_len = max_payload_len;
|
||||
|
||||
|
|
@ -627,7 +634,7 @@ static void parse_opts(int argc, char **argv)
|
|||
cfg_cpu = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'D':
|
||||
setup_sockaddr(cfg_family, optarg, &cfg_dst_addr);
|
||||
daddr = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
cfg_ifindex = if_nametoindex(optarg);
|
||||
|
|
@ -638,7 +645,7 @@ static void parse_opts(int argc, char **argv)
|
|||
cfg_cork_mixed = true;
|
||||
break;
|
||||
case 'p':
|
||||
cfg_port = htons(strtoul(optarg, NULL, 0));
|
||||
cfg_port = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'r':
|
||||
cfg_rx = true;
|
||||
|
|
@ -647,7 +654,7 @@ static void parse_opts(int argc, char **argv)
|
|||
cfg_payload_len = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'S':
|
||||
setup_sockaddr(cfg_family, optarg, &cfg_src_addr);
|
||||
saddr = optarg;
|
||||
break;
|
||||
case 't':
|
||||
cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000;
|
||||
|
|
@ -660,6 +667,8 @@ static void parse_opts(int argc, char **argv)
|
|||
break;
|
||||
}
|
||||
}
|
||||
setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
|
||||
setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
|
||||
|
||||
if (cfg_payload_len > max_payload_len)
|
||||
error(1, 0, "-s: payload exceeds max (%d)", max_payload_len);
|
||||
|
|
|
|||
|
|
@ -502,6 +502,231 @@ kci_test_macsec()
|
|||
echo "PASS: macsec"
|
||||
}
|
||||
|
||||
kci_test_gretap()
|
||||
{
|
||||
testns="testns"
|
||||
DEV_NS=gretap00
|
||||
ret=0
|
||||
|
||||
ip netns add "$testns"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "SKIP gretap tests: cannot add net namespace $testns"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ip link help gretap 2>&1 | grep -q "^Usage:"
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: gretap: iproute2 too old"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# test native tunnel
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type gretap seq \
|
||||
key 102 local 172.16.1.100 remote 172.16.1.200
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip addr add dev "$DEV_NS" 10.1.1.100/24
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link set dev $DEV_NS up
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
# test external mode
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type gretap external
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: gretap"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: gretap"
|
||||
|
||||
ip netns del "$testns"
|
||||
}
|
||||
|
||||
kci_test_ip6gretap()
|
||||
{
|
||||
testns="testns"
|
||||
DEV_NS=ip6gretap00
|
||||
ret=0
|
||||
|
||||
ip netns add "$testns"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "SKIP ip6gretap tests: cannot add net namespace $testns"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ip link help ip6gretap 2>&1 | grep -q "^Usage:"
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: ip6gretap: iproute2 too old"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# test native tunnel
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type ip6gretap seq \
|
||||
key 102 local fc00:100::1 remote fc00:100::2
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip addr add dev "$DEV_NS" fc00:200::1/96
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link set dev $DEV_NS up
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
# test external mode
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type ip6gretap external
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: ip6gretap"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: ip6gretap"
|
||||
|
||||
ip netns del "$testns"
|
||||
}
|
||||
|
||||
kci_test_erspan()
|
||||
{
|
||||
testns="testns"
|
||||
DEV_NS=erspan00
|
||||
ret=0
|
||||
|
||||
ip link help erspan 2>&1 | grep -q "^Usage:"
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: erspan: iproute2 too old"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ip netns add "$testns"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "SKIP erspan tests: cannot add net namespace $testns"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# test native tunnel erspan v1
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type erspan seq \
|
||||
key 102 local 172.16.1.100 remote 172.16.1.200 \
|
||||
erspan_ver 1 erspan 488
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip addr add dev "$DEV_NS" 10.1.1.100/24
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link set dev $DEV_NS up
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
# test native tunnel erspan v2
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type erspan seq \
|
||||
key 102 local 172.16.1.100 remote 172.16.1.200 \
|
||||
erspan_ver 2 erspan_dir ingress erspan_hwid 7
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip addr add dev "$DEV_NS" 10.1.1.100/24
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link set dev $DEV_NS up
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
# test external mode
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type erspan external
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: erspan"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: erspan"
|
||||
|
||||
ip netns del "$testns"
|
||||
}
|
||||
|
||||
kci_test_ip6erspan()
|
||||
{
|
||||
testns="testns"
|
||||
DEV_NS=ip6erspan00
|
||||
ret=0
|
||||
|
||||
ip link help ip6erspan 2>&1 | grep -q "^Usage:"
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: ip6erspan: iproute2 too old"
|
||||
return 1
|
||||
fi
|
||||
|
||||
ip netns add "$testns"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "SKIP ip6erspan tests: cannot add net namespace $testns"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# test native tunnel ip6erspan v1
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type ip6erspan seq \
|
||||
key 102 local fc00:100::1 remote fc00:100::2 \
|
||||
erspan_ver 1 erspan 488
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip addr add dev "$DEV_NS" 10.1.1.100/24
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link set dev $DEV_NS up
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
# test native tunnel ip6erspan v2
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" type ip6erspan seq \
|
||||
key 102 local fc00:100::1 remote fc00:100::2 \
|
||||
erspan_ver 2 erspan_dir ingress erspan_hwid 7
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip addr add dev "$DEV_NS" 10.1.1.100/24
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link set dev $DEV_NS up
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
# test external mode
|
||||
ip netns exec "$testns" ip link add dev "$DEV_NS" \
|
||||
type ip6erspan external
|
||||
check_err $?
|
||||
|
||||
ip netns exec "$testns" ip link del "$DEV_NS"
|
||||
check_err $?
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "FAIL: ip6erspan"
|
||||
return 1
|
||||
fi
|
||||
echo "PASS: ip6erspan"
|
||||
|
||||
ip netns del "$testns"
|
||||
}
|
||||
|
||||
kci_test_rtnl()
|
||||
{
|
||||
kci_add_dummy
|
||||
|
|
@ -514,6 +739,10 @@ kci_test_rtnl()
|
|||
kci_test_route_get
|
||||
kci_test_tc
|
||||
kci_test_gre
|
||||
kci_test_gretap
|
||||
kci_test_ip6gretap
|
||||
kci_test_erspan
|
||||
kci_test_ip6erspan
|
||||
kci_test_bridge
|
||||
kci_test_addrlabel
|
||||
kci_test_ifalias
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ int main(int argc, char *argv[])
|
|||
return pr_err("NS_GET_PARENT returned a wrong namespace");
|
||||
|
||||
if (ioctl(pns, NS_GET_PARENT) >= 0 || errno != EPERM)
|
||||
return pr_err("Don't get EPERM");;
|
||||
return pr_err("Don't get EPERM");
|
||||
}
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ LIST_DEVS=FALSE
|
|||
|
||||
DEBUGFS=${DEBUGFS-/sys/kernel/debug}
|
||||
|
||||
DB_BITMASK=0x7FFF
|
||||
PERF_RUN_ORDER=32
|
||||
MAX_MW_SIZE=0
|
||||
RUN_DMA_TESTS=
|
||||
|
|
@ -39,15 +38,17 @@ function show_help()
|
|||
echo "be highly recommended."
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -b BITMASK doorbell clear bitmask for ntb_tool"
|
||||
echo " -C don't cleanup ntb modules on exit"
|
||||
echo " -d run dma tests"
|
||||
echo " -h show this help message"
|
||||
echo " -l list available local and remote PCI ids"
|
||||
echo " -r REMOTE_HOST specify the remote's hostname to connect"
|
||||
echo " to for the test (using ssh)"
|
||||
echo " -p NUM ntb_perf run order (default: $PERF_RUN_ORDER)"
|
||||
echo " -w max_mw_size maxmium memory window size"
|
||||
echo " to for the test (using ssh)"
|
||||
echo " -m MW_SIZE memory window size for ntb_tool"
|
||||
echo " (default: $MW_SIZE)"
|
||||
echo " -d run dma tests for ntb_perf"
|
||||
echo " -p ORDER total data order for ntb_perf"
|
||||
echo " (default: $PERF_RUN_ORDER)"
|
||||
echo " -w MAX_MW_SIZE maxmium memory window size for ntb_perf"
|
||||
echo
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +57,6 @@ function parse_args()
|
|||
OPTIND=0
|
||||
while getopts "b:Cdhlm:r:p:w:" opt; do
|
||||
case "$opt" in
|
||||
b) DB_BITMASK=${OPTARG} ;;
|
||||
C) DONT_CLEANUP=1 ;;
|
||||
d) RUN_DMA_TESTS=1 ;;
|
||||
h) show_help; exit 0 ;;
|
||||
|
|
@ -87,7 +87,7 @@ set -e
|
|||
|
||||
function _modprobe()
|
||||
{
|
||||
modprobe "$@"
|
||||
modprobe "$@"
|
||||
|
||||
if [[ "$REMOTE_HOST" != "" ]]; then
|
||||
ssh "$REMOTE_HOST" modprobe "$@"
|
||||
|
|
@ -127,15 +127,70 @@ function write_file()
|
|||
fi
|
||||
}
|
||||
|
||||
function check_file()
|
||||
{
|
||||
split_remote $1
|
||||
|
||||
if [[ "$REMOTE" != "" ]]; then
|
||||
ssh "$REMOTE" "[[ -e ${VPATH} ]]"
|
||||
else
|
||||
[[ -e ${VPATH} ]]
|
||||
fi
|
||||
}
|
||||
|
||||
function subdirname()
|
||||
{
|
||||
echo $(basename $(dirname $1)) 2> /dev/null
|
||||
}
|
||||
|
||||
function find_pidx()
|
||||
{
|
||||
PORT=$1
|
||||
PPATH=$2
|
||||
|
||||
for ((i = 0; i < 64; i++)); do
|
||||
PEER_DIR="$PPATH/peer$i"
|
||||
|
||||
check_file ${PEER_DIR} || break
|
||||
|
||||
PEER_PORT=$(read_file "${PEER_DIR}/port")
|
||||
if [[ ${PORT} -eq $PEER_PORT ]]; then
|
||||
echo $i
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function port_test()
|
||||
{
|
||||
LOC=$1
|
||||
REM=$2
|
||||
|
||||
echo "Running port tests on: $(basename $LOC) / $(basename $REM)"
|
||||
|
||||
LOCAL_PORT=$(read_file "$LOC/port")
|
||||
REMOTE_PORT=$(read_file "$REM/port")
|
||||
|
||||
LOCAL_PIDX=$(find_pidx ${REMOTE_PORT} "$LOC")
|
||||
REMOTE_PIDX=$(find_pidx ${LOCAL_PORT} "$REM")
|
||||
|
||||
echo "Local port ${LOCAL_PORT} with index ${REMOTE_PIDX} on remote host"
|
||||
echo "Peer port ${REMOTE_PORT} with index ${LOCAL_PIDX} on local host"
|
||||
|
||||
echo " Passed"
|
||||
}
|
||||
|
||||
function link_test()
|
||||
{
|
||||
LOC=$1
|
||||
REM=$2
|
||||
EXP=0
|
||||
|
||||
echo "Running link tests on: $(basename $LOC) / $(basename $REM)"
|
||||
echo "Running link tests on: $(subdirname $LOC) / $(subdirname $REM)"
|
||||
|
||||
if ! write_file "N" "$LOC/link" 2> /dev/null; then
|
||||
if ! write_file "N" "$LOC/../link" 2> /dev/null; then
|
||||
echo " Unsupported"
|
||||
return
|
||||
fi
|
||||
|
|
@ -143,12 +198,11 @@ function link_test()
|
|||
write_file "N" "$LOC/link_event"
|
||||
|
||||
if [[ $(read_file "$REM/link") != "N" ]]; then
|
||||
echo "Expected remote link to be down in $REM/link" >&2
|
||||
echo "Expected link to be down in $REM/link" >&2
|
||||
exit -1
|
||||
fi
|
||||
|
||||
write_file "Y" "$LOC/link"
|
||||
write_file "Y" "$LOC/link_event"
|
||||
write_file "Y" "$LOC/../link"
|
||||
|
||||
echo " Passed"
|
||||
}
|
||||
|
|
@ -161,58 +215,136 @@ function doorbell_test()
|
|||
|
||||
echo "Running db tests on: $(basename $LOC) / $(basename $REM)"
|
||||
|
||||
write_file "c $DB_BITMASK" "$REM/db"
|
||||
DB_VALID_MASK=$(read_file "$LOC/db_valid_mask")
|
||||
|
||||
for ((i=1; i <= 8; i++)); do
|
||||
let DB=$(read_file "$REM/db") || true
|
||||
if [[ "$DB" != "$EXP" ]]; then
|
||||
write_file "c $DB_VALID_MASK" "$REM/db"
|
||||
|
||||
for ((i = 0; i < 64; i++)); do
|
||||
DB=$(read_file "$REM/db")
|
||||
if [[ "$DB" -ne "$EXP" ]]; then
|
||||
echo "Doorbell doesn't match expected value $EXP " \
|
||||
"in $REM/db" >&2
|
||||
exit -1
|
||||
fi
|
||||
|
||||
let "MASK=1 << ($i-1)" || true
|
||||
let "EXP=$EXP | $MASK" || true
|
||||
let "MASK = (1 << $i) & $DB_VALID_MASK" || true
|
||||
let "EXP = $EXP | $MASK" || true
|
||||
|
||||
write_file "s $MASK" "$LOC/peer_db"
|
||||
done
|
||||
|
||||
write_file "c $DB_VALID_MASK" "$REM/db_mask"
|
||||
write_file $DB_VALID_MASK "$REM/db_event"
|
||||
write_file "s $DB_VALID_MASK" "$REM/db_mask"
|
||||
|
||||
write_file "c $DB_VALID_MASK" "$REM/db"
|
||||
|
||||
echo " Passed"
|
||||
}
|
||||
|
||||
function read_spad()
|
||||
function get_files_count()
|
||||
{
|
||||
VPATH=$1
|
||||
IDX=$2
|
||||
NAME=$1
|
||||
LOC=$2
|
||||
|
||||
ROW=($(read_file "$VPATH" | grep -e "^$IDX"))
|
||||
let VAL=${ROW[1]} || true
|
||||
echo $VAL
|
||||
split_remote $LOC
|
||||
|
||||
if [[ "$REMOTE" == "" ]]; then
|
||||
echo $(ls -1 "$LOC"/${NAME}* 2>/dev/null | wc -l)
|
||||
else
|
||||
echo $(ssh "$REMOTE" "ls -1 \"$VPATH\"/${NAME}* | \
|
||||
wc -l" 2> /dev/null)
|
||||
fi
|
||||
}
|
||||
|
||||
function scratchpad_test()
|
||||
{
|
||||
LOC=$1
|
||||
REM=$2
|
||||
CNT=$(read_file "$LOC/spad" | wc -l)
|
||||
|
||||
echo "Running spad tests on: $(basename $LOC) / $(basename $REM)"
|
||||
echo "Running spad tests on: $(subdirname $LOC) / $(subdirname $REM)"
|
||||
|
||||
CNT=$(get_files_count "spad" "$LOC")
|
||||
|
||||
if [[ $CNT -eq 0 ]]; then
|
||||
echo " Unsupported"
|
||||
return
|
||||
fi
|
||||
|
||||
for ((i = 0; i < $CNT; i++)); do
|
||||
VAL=$RANDOM
|
||||
write_file "$i $VAL" "$LOC/peer_spad"
|
||||
RVAL=$(read_spad "$REM/spad" $i)
|
||||
write_file "$VAL" "$LOC/spad$i"
|
||||
RVAL=$(read_file "$REM/../spad$i")
|
||||
|
||||
if [[ "$VAL" != "$RVAL" ]]; then
|
||||
echo "Scratchpad doesn't match expected value $VAL " \
|
||||
"in $REM/spad, got $RVAL" >&2
|
||||
if [[ "$VAL" -ne "$RVAL" ]]; then
|
||||
echo "Scratchpad $i value $RVAL doesn't match $VAL" >&2
|
||||
exit -1
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
echo " Passed"
|
||||
}
|
||||
|
||||
function message_test()
|
||||
{
|
||||
LOC=$1
|
||||
REM=$2
|
||||
|
||||
echo "Running msg tests on: $(subdirname $LOC) / $(subdirname $REM)"
|
||||
|
||||
CNT=$(get_files_count "msg" "$LOC")
|
||||
|
||||
if [[ $CNT -eq 0 ]]; then
|
||||
echo " Unsupported"
|
||||
return
|
||||
fi
|
||||
|
||||
MSG_OUTBITS_MASK=$(read_file "$LOC/../msg_inbits")
|
||||
MSG_INBITS_MASK=$(read_file "$REM/../msg_inbits")
|
||||
|
||||
write_file "c $MSG_OUTBITS_MASK" "$LOC/../msg_sts"
|
||||
write_file "c $MSG_INBITS_MASK" "$REM/../msg_sts"
|
||||
|
||||
for ((i = 0; i < $CNT; i++)); do
|
||||
VAL=$RANDOM
|
||||
write_file "$VAL" "$LOC/msg$i"
|
||||
RVAL=$(read_file "$REM/../msg$i")
|
||||
|
||||
if [[ "$VAL" -ne "${RVAL%%<-*}" ]]; then
|
||||
echo "Message $i value $RVAL doesn't match $VAL" >&2
|
||||
exit -1
|
||||
fi
|
||||
done
|
||||
|
||||
echo " Passed"
|
||||
}
|
||||
|
||||
function get_number()
|
||||
{
|
||||
KEY=$1
|
||||
|
||||
sed -n "s/^\(${KEY}\)[ \t]*\(0x[0-9a-fA-F]*\)\(\[p\]\)\?$/\2/p"
|
||||
}
|
||||
|
||||
function mw_alloc()
|
||||
{
|
||||
IDX=$1
|
||||
LOC=$2
|
||||
REM=$3
|
||||
|
||||
write_file $MW_SIZE "$LOC/mw_trans$IDX"
|
||||
|
||||
INB_MW=$(read_file "$LOC/mw_trans$IDX")
|
||||
MW_ALIGNED_SIZE=$(echo "$INB_MW" | get_number "Window Size")
|
||||
MW_DMA_ADDR=$(echo "$INB_MW" | get_number "DMA Address")
|
||||
|
||||
write_file "$MW_DMA_ADDR:$(($MW_ALIGNED_SIZE))" "$REM/peer_mw_trans$IDX"
|
||||
|
||||
if [[ $MW_SIZE -ne $MW_ALIGNED_SIZE ]]; then
|
||||
echo "MW $IDX size aligned to $MW_ALIGNED_SIZE"
|
||||
fi
|
||||
}
|
||||
|
||||
function write_mw()
|
||||
{
|
||||
split_remote $2
|
||||
|
|
@ -225,17 +357,15 @@ function write_mw()
|
|||
fi
|
||||
}
|
||||
|
||||
function mw_test()
|
||||
function mw_check()
|
||||
{
|
||||
IDX=$1
|
||||
LOC=$2
|
||||
REM=$3
|
||||
|
||||
echo "Running $IDX tests on: $(basename $LOC) / $(basename $REM)"
|
||||
write_mw "$LOC/mw$IDX"
|
||||
|
||||
write_mw "$LOC/$IDX"
|
||||
|
||||
split_remote "$LOC/$IDX"
|
||||
split_remote "$LOC/mw$IDX"
|
||||
if [[ "$REMOTE" == "" ]]; then
|
||||
A=$VPATH
|
||||
else
|
||||
|
|
@ -243,7 +373,7 @@ function mw_test()
|
|||
ssh "$REMOTE" cat "$VPATH" > "$A"
|
||||
fi
|
||||
|
||||
split_remote "$REM/peer_$IDX"
|
||||
split_remote "$REM/peer_mw$IDX"
|
||||
if [[ "$REMOTE" == "" ]]; then
|
||||
B=$VPATH
|
||||
else
|
||||
|
|
@ -251,7 +381,7 @@ function mw_test()
|
|||
ssh "$REMOTE" cat "$VPATH" > "$B"
|
||||
fi
|
||||
|
||||
cmp -n $MW_SIZE "$A" "$B"
|
||||
cmp -n $MW_ALIGNED_SIZE "$A" "$B"
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Memory window $MW did not match!" >&2
|
||||
fi
|
||||
|
|
@ -263,8 +393,39 @@ function mw_test()
|
|||
if [[ "$B" == "/tmp/*" ]]; then
|
||||
rm "$B"
|
||||
fi
|
||||
}
|
||||
|
||||
function mw_free()
|
||||
{
|
||||
IDX=$1
|
||||
LOC=$2
|
||||
REM=$3
|
||||
|
||||
write_file "$MW_DMA_ADDR:0" "$REM/peer_mw_trans$IDX"
|
||||
|
||||
write_file 0 "$LOC/mw_trans$IDX"
|
||||
}
|
||||
|
||||
function mw_test()
|
||||
{
|
||||
LOC=$1
|
||||
REM=$2
|
||||
|
||||
CNT=$(get_files_count "mw_trans" "$LOC")
|
||||
|
||||
for ((i = 0; i < $CNT; i++)); do
|
||||
echo "Running mw$i tests on: $(subdirname $LOC) / " \
|
||||
"$(subdirname $REM)"
|
||||
|
||||
mw_alloc $i $LOC $REM
|
||||
|
||||
mw_check $i $LOC $REM
|
||||
|
||||
mw_free $i $LOC $REM
|
||||
|
||||
echo " Passed"
|
||||
done
|
||||
|
||||
echo " Passed"
|
||||
}
|
||||
|
||||
function pingpong_test()
|
||||
|
|
@ -274,13 +435,13 @@ function pingpong_test()
|
|||
|
||||
echo "Running ping pong tests on: $(basename $LOC) / $(basename $REM)"
|
||||
|
||||
LOC_START=$(read_file $LOC/count)
|
||||
REM_START=$(read_file $REM/count)
|
||||
LOC_START=$(read_file "$LOC/count")
|
||||
REM_START=$(read_file "$REM/count")
|
||||
|
||||
sleep 7
|
||||
|
||||
LOC_END=$(read_file $LOC/count)
|
||||
REM_END=$(read_file $REM/count)
|
||||
LOC_END=$(read_file "$LOC/count")
|
||||
REM_END=$(read_file "$REM/count")
|
||||
|
||||
if [[ $LOC_START == $LOC_END ]] || [[ $REM_START == $REM_END ]]; then
|
||||
echo "Ping pong counter not incrementing!" >&2
|
||||
|
|
@ -300,19 +461,19 @@ function perf_test()
|
|||
WITH="without"
|
||||
fi
|
||||
|
||||
_modprobe ntb_perf run_order=$PERF_RUN_ORDER \
|
||||
_modprobe ntb_perf total_order=$PERF_RUN_ORDER \
|
||||
max_mw_size=$MAX_MW_SIZE use_dma=$USE_DMA
|
||||
|
||||
echo "Running local perf test $WITH DMA"
|
||||
write_file "" $LOCAL_PERF/run
|
||||
write_file "$LOCAL_PIDX" "$LOCAL_PERF/run"
|
||||
echo -n " "
|
||||
read_file $LOCAL_PERF/run
|
||||
read_file "$LOCAL_PERF/run"
|
||||
echo " Passed"
|
||||
|
||||
echo "Running remote perf test $WITH DMA"
|
||||
write_file "" $REMOTE_PERF/run
|
||||
write_file "$REMOTE_PIDX" "$REMOTE_PERF/run"
|
||||
echo -n " "
|
||||
read_file $REMOTE_PERF/run
|
||||
read_file "$REMOTE_PERF/run"
|
||||
echo " Passed"
|
||||
|
||||
_modprobe -r ntb_perf
|
||||
|
|
@ -320,48 +481,44 @@ function perf_test()
|
|||
|
||||
function ntb_tool_tests()
|
||||
{
|
||||
LOCAL_TOOL=$DEBUGFS/ntb_tool/$LOCAL_DEV
|
||||
REMOTE_TOOL=$REMOTE_HOST:$DEBUGFS/ntb_tool/$REMOTE_DEV
|
||||
LOCAL_TOOL="$DEBUGFS/ntb_tool/$LOCAL_DEV"
|
||||
REMOTE_TOOL="$REMOTE_HOST:$DEBUGFS/ntb_tool/$REMOTE_DEV"
|
||||
|
||||
echo "Starting ntb_tool tests..."
|
||||
|
||||
_modprobe ntb_tool
|
||||
|
||||
write_file Y $LOCAL_TOOL/link_event
|
||||
write_file Y $REMOTE_TOOL/link_event
|
||||
port_test "$LOCAL_TOOL" "$REMOTE_TOOL"
|
||||
|
||||
link_test $LOCAL_TOOL $REMOTE_TOOL
|
||||
link_test $REMOTE_TOOL $LOCAL_TOOL
|
||||
LOCAL_PEER_TOOL="$LOCAL_TOOL/peer$LOCAL_PIDX"
|
||||
REMOTE_PEER_TOOL="$REMOTE_TOOL/peer$REMOTE_PIDX"
|
||||
|
||||
link_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL"
|
||||
link_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL"
|
||||
|
||||
#Ensure the link is up on both sides before continuing
|
||||
write_file Y $LOCAL_TOOL/link_event
|
||||
write_file Y $REMOTE_TOOL/link_event
|
||||
write_file "Y" "$LOCAL_PEER_TOOL/link_event"
|
||||
write_file "Y" "$REMOTE_PEER_TOOL/link_event"
|
||||
|
||||
for PEER_TRANS in $(ls $LOCAL_TOOL/peer_trans*); do
|
||||
PT=$(basename $PEER_TRANS)
|
||||
write_file $MW_SIZE $LOCAL_TOOL/$PT
|
||||
write_file $MW_SIZE $REMOTE_TOOL/$PT
|
||||
done
|
||||
doorbell_test "$LOCAL_TOOL" "$REMOTE_TOOL"
|
||||
doorbell_test "$REMOTE_TOOL" "$LOCAL_TOOL"
|
||||
|
||||
doorbell_test $LOCAL_TOOL $REMOTE_TOOL
|
||||
doorbell_test $REMOTE_TOOL $LOCAL_TOOL
|
||||
scratchpad_test $LOCAL_TOOL $REMOTE_TOOL
|
||||
scratchpad_test $REMOTE_TOOL $LOCAL_TOOL
|
||||
scratchpad_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL"
|
||||
scratchpad_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL"
|
||||
|
||||
for MW in $(ls $LOCAL_TOOL/mw*); do
|
||||
MW=$(basename $MW)
|
||||
message_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL"
|
||||
message_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL"
|
||||
|
||||
mw_test $MW $LOCAL_TOOL $REMOTE_TOOL
|
||||
mw_test $MW $REMOTE_TOOL $LOCAL_TOOL
|
||||
done
|
||||
mw_test "$LOCAL_PEER_TOOL" "$REMOTE_PEER_TOOL"
|
||||
mw_test "$REMOTE_PEER_TOOL" "$LOCAL_PEER_TOOL"
|
||||
|
||||
_modprobe -r ntb_tool
|
||||
}
|
||||
|
||||
function ntb_pingpong_tests()
|
||||
{
|
||||
LOCAL_PP=$DEBUGFS/ntb_pingpong/$LOCAL_DEV
|
||||
REMOTE_PP=$REMOTE_HOST:$DEBUGFS/ntb_pingpong/$REMOTE_DEV
|
||||
LOCAL_PP="$DEBUGFS/ntb_pingpong/$LOCAL_DEV"
|
||||
REMOTE_PP="$REMOTE_HOST:$DEBUGFS/ntb_pingpong/$REMOTE_DEV"
|
||||
|
||||
echo "Starting ntb_pingpong tests..."
|
||||
|
||||
|
|
@ -374,8 +531,8 @@ function ntb_pingpong_tests()
|
|||
|
||||
function ntb_perf_tests()
|
||||
{
|
||||
LOCAL_PERF=$DEBUGFS/ntb_perf/$LOCAL_DEV
|
||||
REMOTE_PERF=$REMOTE_HOST:$DEBUGFS/ntb_perf/$REMOTE_DEV
|
||||
LOCAL_PERF="$DEBUGFS/ntb_perf/$LOCAL_DEV"
|
||||
REMOTE_PERF="$REMOTE_HOST:$DEBUGFS/ntb_perf/$REMOTE_DEV"
|
||||
|
||||
echo "Starting ntb_perf tests..."
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
TEST_GEN_PROGS := copy_unaligned copy_first_unaligned paste_unaligned paste_last_unaligned
|
||||
TEST_GEN_PROGS := copy_unaligned copy_first_unaligned paste_unaligned \
|
||||
paste_last_unaligned alignment_handler
|
||||
|
||||
include ../../lib.mk
|
||||
|
||||
|
|
|
|||
491
tools/testing/selftests/powerpc/alignment/alignment_handler.c
Normal file
491
tools/testing/selftests/powerpc/alignment/alignment_handler.c
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* Test the powerpc alignment handler on POWER8/POWER9
|
||||
*
|
||||
* Copyright (C) 2017 IBM Corporation (Michael Neuling, Andrew Donnellan)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This selftest exercises the powerpc alignment fault handler.
|
||||
*
|
||||
* We create two sets of source and destination buffers, one in regular memory,
|
||||
* the other cache-inhibited (we use /dev/fb0 for this).
|
||||
*
|
||||
* We initialise the source buffers, then use whichever set of load/store
|
||||
* instructions is under test to copy bytes from the source buffers to the
|
||||
* destination buffers. For the regular buffers, these instructions will
|
||||
* execute normally. For the cache-inhibited buffers, these instructions
|
||||
* will trap and cause an alignment fault, and the alignment fault handler
|
||||
* will emulate the particular instruction under test. We then compare the
|
||||
* destination buffers to ensure that the native and emulated cases give the
|
||||
* same result.
|
||||
*
|
||||
* TODO:
|
||||
* - Any FIXMEs below
|
||||
* - Test VSX regs < 32 and > 32
|
||||
* - Test all loads and stores
|
||||
* - Check update forms do update register
|
||||
* - Test alignment faults over page boundary
|
||||
*
|
||||
* Some old binutils may not support all the instructions.
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
int bufsize;
|
||||
int debug;
|
||||
int testing;
|
||||
volatile int gotsig;
|
||||
|
||||
void sighandler(int sig, siginfo_t *info, void *ctx)
|
||||
{
|
||||
struct ucontext *ucp = ctx;
|
||||
|
||||
if (!testing) {
|
||||
signal(sig, SIG_DFL);
|
||||
kill(0, sig);
|
||||
}
|
||||
gotsig = sig;
|
||||
#ifdef __powerpc64__
|
||||
ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
|
||||
#else
|
||||
ucp->uc_mcontext.uc_regs->gregs[PT_NIP] += 4;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define XFORM(reg, n) " " #reg " ,%"#n",%2 ;"
|
||||
#define DFORM(reg, n) " " #reg " ,0(%"#n") ;"
|
||||
|
||||
#define TEST(name, ld_op, st_op, form, ld_reg, st_reg) \
|
||||
void test_##name(char *s, char *d) \
|
||||
{ \
|
||||
asm volatile( \
|
||||
#ld_op form(ld_reg, 0) \
|
||||
#st_op form(st_reg, 1) \
|
||||
:: "r"(s), "r"(d), "r"(0) \
|
||||
: "memory", "vs0", "vs32", "r31"); \
|
||||
} \
|
||||
rc |= do_test(#name, test_##name)
|
||||
|
||||
#define LOAD_VSX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 32, 32)
|
||||
#define STORE_VSX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 32)
|
||||
#define LOAD_VSX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 32, 32)
|
||||
#define STORE_VSX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 32)
|
||||
#define LOAD_VMX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 0, 32)
|
||||
#define STORE_VMX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 0)
|
||||
#define LOAD_VMX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 0, 32)
|
||||
#define STORE_VMX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 0)
|
||||
|
||||
#define LOAD_XFORM_TEST(op) TEST(op, op, stdx, XFORM, 31, 31)
|
||||
#define STORE_XFORM_TEST(op) TEST(op, ldx, op, XFORM, 31, 31)
|
||||
#define LOAD_DFORM_TEST(op) TEST(op, op, std, DFORM, 31, 31)
|
||||
#define STORE_DFORM_TEST(op) TEST(op, ld, op, DFORM, 31, 31)
|
||||
|
||||
#define LOAD_FLOAT_DFORM_TEST(op) TEST(op, op, stfd, DFORM, 0, 0)
|
||||
#define STORE_FLOAT_DFORM_TEST(op) TEST(op, lfd, op, DFORM, 0, 0)
|
||||
#define LOAD_FLOAT_XFORM_TEST(op) TEST(op, op, stfdx, XFORM, 0, 0)
|
||||
#define STORE_FLOAT_XFORM_TEST(op) TEST(op, lfdx, op, XFORM, 0, 0)
|
||||
|
||||
|
||||
/* FIXME: Unimplemented tests: */
|
||||
// STORE_DFORM_TEST(stq) /* FIXME: need two registers for quad */
|
||||
// STORE_DFORM_TEST(stswi) /* FIXME: string instruction */
|
||||
|
||||
// STORE_XFORM_TEST(stwat) /* AMO can't emulate or run on CI */
|
||||
// STORE_XFORM_TEST(stdat) /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
|
||||
|
||||
/* preload byte by byte */
|
||||
void preload_data(void *dst, int offset, int width)
|
||||
{
|
||||
char *c = dst;
|
||||
int i;
|
||||
|
||||
c += offset;
|
||||
|
||||
for (i = 0 ; i < width ; i++)
|
||||
c[i] = i;
|
||||
}
|
||||
|
||||
int test_memcpy(void *dst, void *src, int size, int offset,
|
||||
void (*test_func)(char *, char *))
|
||||
{
|
||||
char *s, *d;
|
||||
|
||||
s = src;
|
||||
s += offset;
|
||||
d = dst;
|
||||
d += offset;
|
||||
|
||||
assert(size == 16);
|
||||
gotsig = 0;
|
||||
testing = 1;
|
||||
|
||||
test_func(s, d); /* run the actual test */
|
||||
|
||||
testing = 0;
|
||||
if (gotsig) {
|
||||
if (debug)
|
||||
printf(" Got signal %i\n", gotsig);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dumpdata(char *s1, char *s2, int n, char *test_name)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(" %s: unexpected result:\n", test_name);
|
||||
printf(" mem:");
|
||||
for (i = 0; i < n; i++)
|
||||
printf(" %02x", s1[i]);
|
||||
printf("\n");
|
||||
printf(" ci: ");
|
||||
for (i = 0; i < n; i++)
|
||||
printf(" %02x", s2[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int test_memcmp(void *s1, void *s2, int n, int offset, char *test_name)
|
||||
{
|
||||
char *s1c, *s2c;
|
||||
|
||||
s1c = s1;
|
||||
s1c += offset;
|
||||
s2c = s2;
|
||||
s2c += offset;
|
||||
|
||||
if (memcmp(s1c, s2c, n)) {
|
||||
if (debug) {
|
||||
printf("\n Compare failed. Offset:%i length:%i\n",
|
||||
offset, n);
|
||||
dumpdata(s1c, s2c, n, test_name);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do two memcpy tests using the same instructions. One cachable
|
||||
* memory and the other doesn't.
|
||||
*/
|
||||
int do_test(char *test_name, void (*test_func)(char *, char *))
|
||||
{
|
||||
int offset, width, fd, rc = 0, r;
|
||||
void *mem0, *mem1, *ci0, *ci1;
|
||||
|
||||
printf("\tDoing %s:\t", test_name);
|
||||
|
||||
fd = open("/dev/fb0", O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("\n");
|
||||
perror("Can't open /dev/fb0");
|
||||
SKIP_IF(1);
|
||||
}
|
||||
|
||||
ci0 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED,
|
||||
fd, 0x0);
|
||||
ci1 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED,
|
||||
fd, bufsize);
|
||||
if ((ci0 == MAP_FAILED) || (ci1 == MAP_FAILED)) {
|
||||
printf("\n");
|
||||
perror("mmap failed");
|
||||
SKIP_IF(1);
|
||||
}
|
||||
|
||||
rc = posix_memalign(&mem0, bufsize, bufsize);
|
||||
if (rc) {
|
||||
printf("\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = posix_memalign(&mem1, bufsize, bufsize);
|
||||
if (rc) {
|
||||
printf("\n");
|
||||
free(mem0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* offset = 0 no alignment fault, so skip */
|
||||
for (offset = 1; offset < 16; offset++) {
|
||||
width = 16; /* vsx == 16 bytes */
|
||||
r = 0;
|
||||
|
||||
/* load pattern into memory byte by byte */
|
||||
preload_data(ci0, offset, width);
|
||||
preload_data(mem0, offset, width); // FIXME: remove??
|
||||
memcpy(ci0, mem0, bufsize);
|
||||
memcpy(ci1, mem1, bufsize); /* initialise output to the same */
|
||||
|
||||
/* sanity check */
|
||||
test_memcmp(mem0, ci0, width, offset, test_name);
|
||||
|
||||
r |= test_memcpy(ci1, ci0, width, offset, test_func);
|
||||
r |= test_memcpy(mem1, mem0, width, offset, test_func);
|
||||
if (r && !debug) {
|
||||
printf("FAILED: Got signal");
|
||||
break;
|
||||
}
|
||||
|
||||
r |= test_memcmp(mem1, ci1, width, offset, test_name);
|
||||
rc |= r;
|
||||
if (r && !debug) {
|
||||
printf("FAILED: Wrong Data");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!r)
|
||||
printf("PASSED");
|
||||
printf("\n");
|
||||
|
||||
munmap(ci0, bufsize);
|
||||
munmap(ci1, bufsize);
|
||||
free(mem0);
|
||||
free(mem1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_vsx_206(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
printf("VSX: 2.06B\n");
|
||||
LOAD_VSX_XFORM_TEST(lxvd2x);
|
||||
LOAD_VSX_XFORM_TEST(lxvw4x);
|
||||
LOAD_VSX_XFORM_TEST(lxsdx);
|
||||
LOAD_VSX_XFORM_TEST(lxvdsx);
|
||||
STORE_VSX_XFORM_TEST(stxvd2x);
|
||||
STORE_VSX_XFORM_TEST(stxvw4x);
|
||||
STORE_VSX_XFORM_TEST(stxsdx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_vsx_207(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
printf("VSX: 2.07B\n");
|
||||
LOAD_VSX_XFORM_TEST(lxsspx);
|
||||
LOAD_VSX_XFORM_TEST(lxsiwax);
|
||||
LOAD_VSX_XFORM_TEST(lxsiwzx);
|
||||
STORE_VSX_XFORM_TEST(stxsspx);
|
||||
STORE_VSX_XFORM_TEST(stxsiwx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_vsx_300(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));
|
||||
printf("VSX: 3.00B\n");
|
||||
LOAD_VMX_DFORM_TEST(lxsd);
|
||||
LOAD_VSX_XFORM_TEST(lxsibzx);
|
||||
LOAD_VSX_XFORM_TEST(lxsihzx);
|
||||
LOAD_VMX_DFORM_TEST(lxssp);
|
||||
LOAD_VSX_DFORM_TEST(lxv);
|
||||
LOAD_VSX_XFORM_TEST(lxvb16x);
|
||||
LOAD_VSX_XFORM_TEST(lxvh8x);
|
||||
LOAD_VSX_XFORM_TEST(lxvx);
|
||||
LOAD_VSX_XFORM_TEST(lxvwsx);
|
||||
LOAD_VSX_XFORM_TEST(lxvl);
|
||||
LOAD_VSX_XFORM_TEST(lxvll);
|
||||
STORE_VMX_DFORM_TEST(stxsd);
|
||||
STORE_VSX_XFORM_TEST(stxsibx);
|
||||
STORE_VSX_XFORM_TEST(stxsihx);
|
||||
STORE_VMX_DFORM_TEST(stxssp);
|
||||
STORE_VSX_DFORM_TEST(stxv);
|
||||
STORE_VSX_XFORM_TEST(stxvb16x);
|
||||
STORE_VSX_XFORM_TEST(stxvh8x);
|
||||
STORE_VSX_XFORM_TEST(stxvx);
|
||||
STORE_VSX_XFORM_TEST(stxvl);
|
||||
STORE_VSX_XFORM_TEST(stxvll);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_integer(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
printf("Integer\n");
|
||||
LOAD_DFORM_TEST(lbz);
|
||||
LOAD_DFORM_TEST(lbzu);
|
||||
LOAD_XFORM_TEST(lbzx);
|
||||
LOAD_XFORM_TEST(lbzux);
|
||||
LOAD_DFORM_TEST(lhz);
|
||||
LOAD_DFORM_TEST(lhzu);
|
||||
LOAD_XFORM_TEST(lhzx);
|
||||
LOAD_XFORM_TEST(lhzux);
|
||||
LOAD_DFORM_TEST(lha);
|
||||
LOAD_DFORM_TEST(lhau);
|
||||
LOAD_XFORM_TEST(lhax);
|
||||
LOAD_XFORM_TEST(lhaux);
|
||||
LOAD_XFORM_TEST(lhbrx);
|
||||
LOAD_DFORM_TEST(lwz);
|
||||
LOAD_DFORM_TEST(lwzu);
|
||||
LOAD_XFORM_TEST(lwzx);
|
||||
LOAD_XFORM_TEST(lwzux);
|
||||
LOAD_DFORM_TEST(lwa);
|
||||
LOAD_XFORM_TEST(lwax);
|
||||
LOAD_XFORM_TEST(lwaux);
|
||||
LOAD_XFORM_TEST(lwbrx);
|
||||
LOAD_DFORM_TEST(ld);
|
||||
LOAD_DFORM_TEST(ldu);
|
||||
LOAD_XFORM_TEST(ldx);
|
||||
LOAD_XFORM_TEST(ldux);
|
||||
LOAD_XFORM_TEST(ldbrx);
|
||||
LOAD_DFORM_TEST(lmw);
|
||||
STORE_DFORM_TEST(stb);
|
||||
STORE_XFORM_TEST(stbx);
|
||||
STORE_DFORM_TEST(stbu);
|
||||
STORE_XFORM_TEST(stbux);
|
||||
STORE_DFORM_TEST(sth);
|
||||
STORE_XFORM_TEST(sthx);
|
||||
STORE_DFORM_TEST(sthu);
|
||||
STORE_XFORM_TEST(sthux);
|
||||
STORE_XFORM_TEST(sthbrx);
|
||||
STORE_DFORM_TEST(stw);
|
||||
STORE_XFORM_TEST(stwx);
|
||||
STORE_DFORM_TEST(stwu);
|
||||
STORE_XFORM_TEST(stwux);
|
||||
STORE_XFORM_TEST(stwbrx);
|
||||
STORE_DFORM_TEST(std);
|
||||
STORE_XFORM_TEST(stdx);
|
||||
STORE_DFORM_TEST(stdu);
|
||||
STORE_XFORM_TEST(stdux);
|
||||
STORE_XFORM_TEST(stdbrx);
|
||||
STORE_DFORM_TEST(stmw);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_vmx(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
printf("VMX\n");
|
||||
LOAD_VMX_XFORM_TEST(lvx);
|
||||
|
||||
/*
|
||||
* FIXME: These loads only load part of the register, so our
|
||||
* testing method doesn't work. Also they don't take alignment
|
||||
* faults, so it's kinda pointless anyway
|
||||
*
|
||||
LOAD_VMX_XFORM_TEST(lvebx)
|
||||
LOAD_VMX_XFORM_TEST(lvehx)
|
||||
LOAD_VMX_XFORM_TEST(lvewx)
|
||||
LOAD_VMX_XFORM_TEST(lvxl)
|
||||
*/
|
||||
STORE_VMX_XFORM_TEST(stvx);
|
||||
STORE_VMX_XFORM_TEST(stvebx);
|
||||
STORE_VMX_XFORM_TEST(stvehx);
|
||||
STORE_VMX_XFORM_TEST(stvewx);
|
||||
STORE_VMX_XFORM_TEST(stvxl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_alignment_handler_fp(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
printf("Floating point\n");
|
||||
LOAD_FLOAT_DFORM_TEST(lfd);
|
||||
LOAD_FLOAT_XFORM_TEST(lfdx);
|
||||
LOAD_FLOAT_DFORM_TEST(lfdp);
|
||||
LOAD_FLOAT_XFORM_TEST(lfdpx);
|
||||
LOAD_FLOAT_DFORM_TEST(lfdu);
|
||||
LOAD_FLOAT_XFORM_TEST(lfdux);
|
||||
LOAD_FLOAT_DFORM_TEST(lfs);
|
||||
LOAD_FLOAT_XFORM_TEST(lfsx);
|
||||
LOAD_FLOAT_DFORM_TEST(lfsu);
|
||||
LOAD_FLOAT_XFORM_TEST(lfsux);
|
||||
LOAD_FLOAT_XFORM_TEST(lfiwzx);
|
||||
LOAD_FLOAT_XFORM_TEST(lfiwax);
|
||||
STORE_FLOAT_DFORM_TEST(stfd);
|
||||
STORE_FLOAT_XFORM_TEST(stfdx);
|
||||
STORE_FLOAT_DFORM_TEST(stfdp);
|
||||
STORE_FLOAT_XFORM_TEST(stfdpx);
|
||||
STORE_FLOAT_DFORM_TEST(stfdu);
|
||||
STORE_FLOAT_XFORM_TEST(stfdux);
|
||||
STORE_FLOAT_DFORM_TEST(stfs);
|
||||
STORE_FLOAT_XFORM_TEST(stfsx);
|
||||
STORE_FLOAT_DFORM_TEST(stfsu);
|
||||
STORE_FLOAT_XFORM_TEST(stfsux);
|
||||
STORE_FLOAT_XFORM_TEST(stfiwx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void usage(char *prog)
|
||||
{
|
||||
printf("Usage: %s [options]\n", prog);
|
||||
printf(" -d Enable debug error output\n");
|
||||
printf("\n");
|
||||
printf("This test requires a POWER8 or POWER9 CPU and a usable ");
|
||||
printf("framebuffer at /dev/fb0.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
struct sigaction sa;
|
||||
int rc = 0;
|
||||
int option = 0;
|
||||
|
||||
while ((option = getopt(argc, argv, "d")) != -1) {
|
||||
switch (option) {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
bufsize = getpagesize();
|
||||
|
||||
sa.sa_sigaction = sighandler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
if (sigaction(SIGSEGV, &sa, NULL) == -1
|
||||
|| sigaction(SIGBUS, &sa, NULL) == -1
|
||||
|| sigaction(SIGILL, &sa, NULL) == -1) {
|
||||
perror("sigaction");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc |= test_harness(test_alignment_handler_vsx_206,
|
||||
"test_alignment_handler_vsx_206");
|
||||
rc |= test_harness(test_alignment_handler_vsx_207,
|
||||
"test_alignment_handler_vsx_207");
|
||||
rc |= test_harness(test_alignment_handler_vsx_300,
|
||||
"test_alignment_handler_vsx_300");
|
||||
rc |= test_harness(test_alignment_handler_integer,
|
||||
"test_alignment_handler_integer");
|
||||
rc |= test_harness(test_alignment_handler_vmx,
|
||||
"test_alignment_handler_vmx");
|
||||
rc |= test_harness(test_alignment_handler_fp,
|
||||
"test_alignment_handler_fp");
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -7,17 +7,34 @@
|
|||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define ITERATIONS 5000000
|
||||
|
||||
#define MEMSIZE (128 * 1024 * 1024)
|
||||
#define MEMSIZE (1UL << 27)
|
||||
#define PAGE_SIZE (1UL << 16)
|
||||
#define CHUNK_COUNT (MEMSIZE/PAGE_SIZE)
|
||||
|
||||
static int pg_fault;
|
||||
static int iterations = ITERATIONS;
|
||||
|
||||
static struct option options[] = {
|
||||
{ "pgfault", no_argument, &pg_fault, 1 },
|
||||
{ "iterations", required_argument, 0, 'i' },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("mmap_bench <--pgfault> <--iterations count>\n");
|
||||
}
|
||||
|
||||
int test_mmap(void)
|
||||
{
|
||||
struct timespec ts_start, ts_end;
|
||||
unsigned long i = ITERATIONS;
|
||||
unsigned long i = iterations;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts_start);
|
||||
|
||||
|
|
@ -25,6 +42,11 @@ int test_mmap(void)
|
|||
char *c = mmap(NULL, MEMSIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
FAIL_IF(c == MAP_FAILED);
|
||||
if (pg_fault) {
|
||||
int count;
|
||||
for (count = 0; count < CHUNK_COUNT; count++)
|
||||
c[count << 16] = 'c';
|
||||
}
|
||||
munmap(c, MEMSIZE);
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +57,32 @@ int test_mmap(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
signed char c;
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
|
||||
c = getopt_long(argc, argv, "", options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
if (options[option_index].flag != 0)
|
||||
break;
|
||||
|
||||
usage();
|
||||
exit(1);
|
||||
break;
|
||||
case 'i':
|
||||
iterations = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return test_harness(test_mmap, "mmap_bench");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
hugetlb_vs_thp_test
|
||||
subpage_prot
|
||||
tempfile
|
||||
prot_sao
|
||||
prot_sao
|
||||
segv_errors
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
noarg:
|
||||
$(MAKE) -C ../
|
||||
|
||||
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao
|
||||
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors
|
||||
TEST_GEN_FILES := tempfile
|
||||
|
||||
include ../../lib.mk
|
||||
|
|
|
|||
78
tools/testing/selftests/powerpc/mm/segv_errors.c
Normal file
78
tools/testing/selftests/powerpc/mm/segv_errors.c
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2017 John Sperbeck
|
||||
*
|
||||
* Test that an access to a mapped but inaccessible area causes a SEGV and
|
||||
* reports si_code == SEGV_ACCERR.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <assert.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static bool faulted;
|
||||
static int si_code;
|
||||
|
||||
static void segv_handler(int n, siginfo_t *info, void *ctxt_v)
|
||||
{
|
||||
ucontext_t *ctxt = (ucontext_t *)ctxt_v;
|
||||
struct pt_regs *regs = ctxt->uc_mcontext.regs;
|
||||
|
||||
faulted = true;
|
||||
si_code = info->si_code;
|
||||
regs->nip += 4;
|
||||
}
|
||||
|
||||
int test_segv_errors(void)
|
||||
{
|
||||
struct sigaction act = {
|
||||
.sa_sigaction = segv_handler,
|
||||
.sa_flags = SA_SIGINFO,
|
||||
};
|
||||
char c, *p = NULL;
|
||||
|
||||
p = mmap(NULL, getpagesize(), 0, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
FAIL_IF(p == MAP_FAILED);
|
||||
|
||||
FAIL_IF(sigaction(SIGSEGV, &act, NULL) != 0);
|
||||
|
||||
faulted = false;
|
||||
si_code = 0;
|
||||
|
||||
/*
|
||||
* We just need a compiler barrier, but mb() works and has the nice
|
||||
* property of being easy to spot in the disassembly.
|
||||
*/
|
||||
mb();
|
||||
c = *p;
|
||||
mb();
|
||||
|
||||
FAIL_IF(!faulted);
|
||||
FAIL_IF(si_code != SEGV_ACCERR);
|
||||
|
||||
faulted = false;
|
||||
si_code = 0;
|
||||
|
||||
mb();
|
||||
*p = c;
|
||||
mb();
|
||||
|
||||
FAIL_IF(!faulted);
|
||||
FAIL_IF(si_code != SEGV_ACCERR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return test_harness(test_segv_errors, "segv_errors");
|
||||
}
|
||||
|
|
@ -79,8 +79,8 @@ trans:
|
|||
: [res] "=r" (result), [texasr] "=r" (texasr)
|
||||
: [fp_load] "r" (fp_load), [fp_load_ckpt] "r" (fp_load_ckpt),
|
||||
[sprn_texasr] "i" (SPRN_TEXASR)
|
||||
: "memory", "r0", "r1", "r2", "r3", "r4",
|
||||
"r8", "r9", "r10", "r11"
|
||||
: "memory", "r0", "r1", "r3", "r4",
|
||||
"r7", "r8", "r9", "r10", "r11"
|
||||
);
|
||||
|
||||
if (result) {
|
||||
|
|
|
|||
|
|
@ -76,8 +76,7 @@ trans:
|
|||
: [tfhar] "=r" (tfhar), [res] "=r" (result),
|
||||
[texasr] "=r" (texasr), [cptr1] "=r" (cptr1)
|
||||
: [sprn_texasr] "i" (SPRN_TEXASR)
|
||||
: "memory", "r0", "r1", "r2", "r3", "r4",
|
||||
"r8", "r9", "r10", "r11", "r31"
|
||||
: "memory", "r0", "r8", "r31"
|
||||
);
|
||||
|
||||
/* There are 2 32bit instructions before tbegin. */
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ trans:
|
|||
: [res] "=r" (result), [texasr] "=r" (texasr)
|
||||
: [fp_load] "r" (fp_load), [fp_load_ckpt] "r" (fp_load_ckpt),
|
||||
[sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "r" (&cptr[1])
|
||||
: "memory", "r0", "r1", "r2", "r3", "r4",
|
||||
: "memory", "r0", "r1", "r3", "r4",
|
||||
"r7", "r8", "r9", "r10", "r11"
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@ tm-signal-context-chk-vmx
|
|||
tm-signal-context-chk-vsx
|
||||
tm-vmx-unavail
|
||||
tm-unavailable
|
||||
tm-trap
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu
|
|||
tm-signal-context-chk-vmx tm-signal-context-chk-vsx
|
||||
|
||||
TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \
|
||||
tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail tm-unavailable \
|
||||
tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail tm-unavailable tm-trap \
|
||||
$(SIGNAL_CONTEXT_CHK_TESTS)
|
||||
|
||||
include ../../lib.mk
|
||||
|
|
@ -18,6 +18,7 @@ $(OUTPUT)/tm-tmspr: CFLAGS += -pthread
|
|||
$(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64
|
||||
$(OUTPUT)/tm-resched-dscr: ../pmu/lib.o
|
||||
$(OUTPUT)/tm-unavailable: CFLAGS += -O0 -pthread -m64 -Wno-error=uninitialized -mvsx
|
||||
$(OUTPUT)/tm-trap: CFLAGS += -O0 -pthread -m64
|
||||
|
||||
SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS))
|
||||
$(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S
|
||||
|
|
|
|||
329
tools/testing/selftests/powerpc/tm/tm-trap.c
Normal file
329
tools/testing/selftests/powerpc/tm/tm-trap.c
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* Copyright 2017, Gustavo Romero, IBM Corp.
|
||||
* Licensed under GPLv2.
|
||||
*
|
||||
* Check if thread endianness is flipped inadvertently to BE on trap
|
||||
* caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after
|
||||
* load_fp and load_vec overflowed).
|
||||
*
|
||||
* The issue can be checked on LE machines simply by zeroing load_fp
|
||||
* and load_vec and then causing a trap in TM. Since the endianness
|
||||
* changes to BE on return from the signal handler, 'nop' is
|
||||
* thread as an illegal instruction in following sequence:
|
||||
* tbegin.
|
||||
* beq 1f
|
||||
* trap
|
||||
* tend.
|
||||
* 1: nop
|
||||
*
|
||||
* However, although the issue is also present on BE machines, it's a
|
||||
* bit trickier to check it on BE machines because MSR.LE bit is set
|
||||
* to zero which determines a BE endianness that is the native
|
||||
* endianness on BE machines, so nothing notably critical happens,
|
||||
* i.e. no illegal instruction is observed immediately after returning
|
||||
* from the signal handler (as it happens on LE machines). Thus to test
|
||||
* it on BE machines LE endianness is forced after a first trap and then
|
||||
* the endianness is verified on subsequent traps to determine if the
|
||||
* endianness "flipped back" to the native endianness (BE).
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <error.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <htmintrin.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "tm.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define pr_error(error_code, format, ...) \
|
||||
error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
#define MSR_LE 1UL
|
||||
#define LE 1UL
|
||||
|
||||
pthread_t t0_ping;
|
||||
pthread_t t1_pong;
|
||||
|
||||
int exit_from_pong;
|
||||
|
||||
int trap_event;
|
||||
int le;
|
||||
|
||||
bool success;
|
||||
|
||||
void trap_signal_handler(int signo, siginfo_t *si, void *uc)
|
||||
{
|
||||
ucontext_t *ucp = uc;
|
||||
uint64_t thread_endianness;
|
||||
|
||||
/* Get thread endianness: extract bit LE from MSR */
|
||||
thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR];
|
||||
|
||||
/***
|
||||
* Little-Endian Machine
|
||||
*/
|
||||
|
||||
if (le) {
|
||||
/* First trap event */
|
||||
if (trap_event == 0) {
|
||||
/* Do nothing. Since it is returning from this trap
|
||||
* event that endianness is flipped by the bug, so just
|
||||
* let the process return from the signal handler and
|
||||
* check on the second trap event if endianness is
|
||||
* flipped or not.
|
||||
*/
|
||||
}
|
||||
/* Second trap event */
|
||||
else if (trap_event == 1) {
|
||||
/*
|
||||
* Since trap was caught in TM on first trap event, if
|
||||
* endianness was still LE (not flipped inadvertently)
|
||||
* after returning from the signal handler instruction
|
||||
* (1) is executed (basically a 'nop'), as it's located
|
||||
* at address of tbegin. +4 (rollback addr). As (1) on
|
||||
* LE endianness does in effect nothing, instruction (2)
|
||||
* is then executed again as 'trap', generating a second
|
||||
* trap event (note that in that case 'trap' is caught
|
||||
* not in transacional mode). On te other hand, if after
|
||||
* the return from the signal handler the endianness in-
|
||||
* advertently flipped, instruction (1) is tread as a
|
||||
* branch instruction, i.e. b .+8, hence instruction (3)
|
||||
* and (4) are executed (tbegin.; trap;) and we get sim-
|
||||
* ilaly on the trap signal handler, but now in TM mode.
|
||||
* Either way, it's now possible to check the MSR LE bit
|
||||
* once in the trap handler to verify if endianness was
|
||||
* flipped or not after the return from the second trap
|
||||
* event. If endianness is flipped, the bug is present.
|
||||
* Finally, getting a trap in TM mode or not is just
|
||||
* worth noting because it affects the math to determine
|
||||
* the offset added to the NIP on return: the NIP for a
|
||||
* trap caught in TM is the rollback address, i.e. the
|
||||
* next instruction after 'tbegin.', whilst the NIP for
|
||||
* a trap caught in non-transactional mode is the very
|
||||
* same address of the 'trap' instruction that generated
|
||||
* the trap event.
|
||||
*/
|
||||
|
||||
if (thread_endianness == LE) {
|
||||
/* Go to 'success', i.e. instruction (6) */
|
||||
ucp->uc_mcontext.gp_regs[PT_NIP] += 16;
|
||||
} else {
|
||||
/*
|
||||
* Thread endianness is BE, so it flipped
|
||||
* inadvertently. Thus we flip back to LE and
|
||||
* set NIP to go to 'failure', instruction (5).
|
||||
*/
|
||||
ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
|
||||
ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Big-Endian Machine
|
||||
*/
|
||||
|
||||
else {
|
||||
/* First trap event */
|
||||
if (trap_event == 0) {
|
||||
/*
|
||||
* Force thread endianness to be LE. Instructions (1),
|
||||
* (3), and (4) will be executed, generating a second
|
||||
* trap in TM mode.
|
||||
*/
|
||||
ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
|
||||
}
|
||||
/* Second trap event */
|
||||
else if (trap_event == 1) {
|
||||
/*
|
||||
* Do nothing. If bug is present on return from this
|
||||
* second trap event endianness will flip back "automat-
|
||||
* ically" to BE, otherwise thread endianness will
|
||||
* continue to be LE, just as it was set above.
|
||||
*/
|
||||
}
|
||||
/* A third trap event */
|
||||
else {
|
||||
/*
|
||||
* Once here it means that after returning from the sec-
|
||||
* ond trap event instruction (4) (trap) was executed
|
||||
* as LE, generating a third trap event. In that case
|
||||
* endianness is still LE as set on return from the
|
||||
* first trap event, hence no bug. Otherwise, bug
|
||||
* flipped back to BE on return from the second trap
|
||||
* event and instruction (4) was executed as 'tdi' (so
|
||||
* basically a 'nop') and branch to 'failure' in
|
||||
* instruction (5) was taken to indicate failure and we
|
||||
* never get here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Flip back to BE and go to instruction (6), i.e. go to
|
||||
* 'success'.
|
||||
*/
|
||||
ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL;
|
||||
ucp->uc_mcontext.gp_regs[PT_NIP] += 8;
|
||||
}
|
||||
}
|
||||
|
||||
trap_event++;
|
||||
}
|
||||
|
||||
void usr1_signal_handler(int signo, siginfo_t *si, void *not_used)
|
||||
{
|
||||
/* Got a USR1 signal from ping(), so just tell pong() to exit */
|
||||
exit_from_pong = 1;
|
||||
}
|
||||
|
||||
void *ping(void *not_used)
|
||||
{
|
||||
uint64_t i;
|
||||
|
||||
trap_event = 0;
|
||||
|
||||
/*
|
||||
* Wait an amount of context switches so load_fp and load_vec overflows
|
||||
* and MSR_[FP|VEC|V] is 0.
|
||||
*/
|
||||
for (i = 0; i < 1024*1024*512; i++)
|
||||
;
|
||||
|
||||
asm goto(
|
||||
/*
|
||||
* [NA] means "Native Endianness", i.e. it tells how a
|
||||
* instruction is executed on machine's native endianness (in
|
||||
* other words, native endianness matches kernel endianness).
|
||||
* [OP] means "Opposite Endianness", i.e. on a BE machine, it
|
||||
* tells how a instruction is executed as a LE instruction; con-
|
||||
* versely, on a LE machine, it tells how a instruction is
|
||||
* executed as a BE instruction. When [NA] is omitted, it means
|
||||
* that the native interpretation of a given instruction is not
|
||||
* relevant for the test. Likewise when [OP] is omitted.
|
||||
*/
|
||||
|
||||
" tbegin. ;" /* (0) tbegin. [NA] */
|
||||
" tdi 0, 0, 0x48;" /* (1) nop [NA]; b (3) [OP] */
|
||||
" trap ;" /* (2) trap [NA] */
|
||||
".long 0x1D05007C;" /* (3) tbegin. [OP] */
|
||||
".long 0x0800E07F;" /* (4) trap [OP]; nop [NA] */
|
||||
" b %l[failure] ;" /* (5) b [NA]; MSR.LE flipped (bug) */
|
||||
" b %l[success] ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/
|
||||
|
||||
: : : : failure, success);
|
||||
|
||||
failure:
|
||||
success = false;
|
||||
goto exit_from_ping;
|
||||
|
||||
success:
|
||||
success = true;
|
||||
|
||||
exit_from_ping:
|
||||
/* Tell pong() to exit before leaving */
|
||||
pthread_kill(t1_pong, SIGUSR1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *pong(void *not_used)
|
||||
{
|
||||
while (!exit_from_pong)
|
||||
/*
|
||||
* Induce context switches on ping() thread
|
||||
* until ping() finishes its job and signs
|
||||
* to exit from this loop.
|
||||
*/
|
||||
sched_yield();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tm_trap_test(void)
|
||||
{
|
||||
uint16_t k = 1;
|
||||
|
||||
int rc;
|
||||
|
||||
pthread_attr_t attr;
|
||||
cpu_set_t cpuset;
|
||||
|
||||
struct sigaction trap_sa;
|
||||
|
||||
trap_sa.sa_flags = SA_SIGINFO;
|
||||
trap_sa.sa_sigaction = trap_signal_handler;
|
||||
sigaction(SIGTRAP, &trap_sa, NULL);
|
||||
|
||||
struct sigaction usr1_sa;
|
||||
|
||||
usr1_sa.sa_flags = SA_SIGINFO;
|
||||
usr1_sa.sa_sigaction = usr1_signal_handler;
|
||||
sigaction(SIGUSR1, &usr1_sa, NULL);
|
||||
|
||||
/* Set only CPU 0 in the mask. Both threads will be bound to cpu 0. */
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(0, &cpuset);
|
||||
|
||||
/* Init pthread attribute */
|
||||
rc = pthread_attr_init(&attr);
|
||||
if (rc)
|
||||
pr_error(rc, "pthread_attr_init()");
|
||||
|
||||
/*
|
||||
* Bind thread ping() and pong() both to CPU 0 so they ping-pong and
|
||||
* speed up context switches on ping() thread, speeding up the load_fp
|
||||
* and load_vec overflow.
|
||||
*/
|
||||
rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
||||
if (rc)
|
||||
pr_error(rc, "pthread_attr_setaffinity()");
|
||||
|
||||
/* Figure out the machine endianness */
|
||||
le = (int) *(uint8_t *)&k;
|
||||
|
||||
printf("%s machine detected. Checking if endianness flips %s",
|
||||
le ? "Little-Endian" : "Big-Endian",
|
||||
"inadvertently on trap in TM... ");
|
||||
|
||||
rc = fflush(0);
|
||||
if (rc)
|
||||
pr_error(rc, "fflush()");
|
||||
|
||||
/* Launch ping() */
|
||||
rc = pthread_create(&t0_ping, &attr, ping, NULL);
|
||||
if (rc)
|
||||
pr_error(rc, "pthread_create()");
|
||||
|
||||
exit_from_pong = 0;
|
||||
|
||||
/* Launch pong() */
|
||||
rc = pthread_create(&t1_pong, &attr, pong, NULL);
|
||||
if (rc)
|
||||
pr_error(rc, "pthread_create()");
|
||||
|
||||
rc = pthread_join(t0_ping, NULL);
|
||||
if (rc)
|
||||
pr_error(rc, "pthread_join()");
|
||||
|
||||
rc = pthread_join(t1_pong, NULL);
|
||||
if (rc)
|
||||
pr_error(rc, "pthread_join()");
|
||||
|
||||
if (success) {
|
||||
printf("no.\n"); /* no, endianness did not flip inadvertently */
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
printf("yes!\n"); /* yes, endianness did flip inadvertently */
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return test_harness(tm_trap_test, "tm_trap_test");
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <error.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -33,6 +34,11 @@
|
|||
#define VSX_UNA_EXCEPTION 2
|
||||
|
||||
#define NUM_EXCEPTIONS 3
|
||||
#define err_at_line(status, errnum, format, ...) \
|
||||
error_at_line(status, errnum, __FILE__, __LINE__, format ##__VA_ARGS__)
|
||||
|
||||
#define pr_warn(code, format, ...) err_at_line(0, code, format, ##__VA_ARGS__)
|
||||
#define pr_err(code, format, ...) err_at_line(1, code, format, ##__VA_ARGS__)
|
||||
|
||||
struct Flags {
|
||||
int touch_fp;
|
||||
|
|
@ -303,10 +309,19 @@ void test_fp_vec(int fp, int vec, pthread_attr_t *attr)
|
|||
* checking if the failure cause is the one we expect.
|
||||
*/
|
||||
do {
|
||||
int rc;
|
||||
|
||||
/* Bind 'ping' to CPU 0, as specified in 'attr'. */
|
||||
pthread_create(&t0, attr, ping, (void *) &flags);
|
||||
pthread_setname_np(t0, "ping");
|
||||
pthread_join(t0, &ret_value);
|
||||
rc = pthread_create(&t0, attr, ping, (void *) &flags);
|
||||
if (rc)
|
||||
pr_err(rc, "pthread_create()");
|
||||
rc = pthread_setname_np(t0, "ping");
|
||||
if (rc)
|
||||
pr_warn(rc, "pthread_setname_np");
|
||||
rc = pthread_join(t0, &ret_value);
|
||||
if (rc)
|
||||
pr_err(rc, "pthread_join");
|
||||
|
||||
retries--;
|
||||
} while (ret_value != NULL && retries);
|
||||
|
||||
|
|
@ -320,7 +335,7 @@ void test_fp_vec(int fp, int vec, pthread_attr_t *attr)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int exception; /* FP = 0, VEC = 1, VSX = 2 */
|
||||
int rc, exception; /* FP = 0, VEC = 1, VSX = 2 */
|
||||
pthread_t t1;
|
||||
pthread_attr_t attr;
|
||||
cpu_set_t cpuset;
|
||||
|
|
@ -330,13 +345,23 @@ int main(int argc, char **argv)
|
|||
CPU_SET(0, &cpuset);
|
||||
|
||||
/* Init pthread attribute. */
|
||||
pthread_attr_init(&attr);
|
||||
rc = pthread_attr_init(&attr);
|
||||
if (rc)
|
||||
pr_err(rc, "pthread_attr_init()");
|
||||
|
||||
/* Set CPU 0 mask into the pthread attribute. */
|
||||
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
||||
rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
||||
if (rc)
|
||||
pr_err(rc, "pthread_attr_setaffinity_np()");
|
||||
|
||||
pthread_create(&t1, &attr /* Bind 'pong' to CPU 0 */, pong, NULL);
|
||||
pthread_setname_np(t1, "pong"); /* Name it for systemtap convenience */
|
||||
rc = pthread_create(&t1, &attr /* Bind 'pong' to CPU 0 */, pong, NULL);
|
||||
if (rc)
|
||||
pr_err(rc, "pthread_create()");
|
||||
|
||||
/* Name it for systemtap convenience */
|
||||
rc = pthread_setname_np(t1, "pong");
|
||||
if (rc)
|
||||
pr_warn(rc, "pthread_create()");
|
||||
|
||||
flags.result = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1717,7 +1717,7 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
|
|||
|
||||
if (nr == __NR_getpid)
|
||||
change_syscall(_metadata, tracee, __NR_getppid);
|
||||
if (nr == __NR_open)
|
||||
if (nr == __NR_openat)
|
||||
change_syscall(_metadata, tracee, -1);
|
||||
}
|
||||
|
||||
|
|
@ -1792,7 +1792,7 @@ TEST_F(TRACE_syscall, ptrace_syscall_dropped)
|
|||
true);
|
||||
|
||||
/* Tracer should skip the open syscall, resulting in EPERM. */
|
||||
EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_open));
|
||||
EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_openat));
|
||||
}
|
||||
|
||||
TEST_F(TRACE_syscall, syscall_allowed)
|
||||
|
|
|
|||
1
tools/testing/selftests/vm/.gitignore
vendored
1
tools/testing/selftests/vm/.gitignore
vendored
|
|
@ -10,3 +10,4 @@ userfaultfd
|
|||
mlock-intersect-test
|
||||
mlock-random-test
|
||||
virtual_address_range
|
||||
gup_benchmark
|
||||
|
|
|
|||
|
|
@ -8,17 +8,18 @@ endif
|
|||
CFLAGS = -Wall -I ../../../../usr/include $(EXTRA_CFLAGS)
|
||||
LDLIBS = -lrt
|
||||
TEST_GEN_FILES = compaction_test
|
||||
TEST_GEN_FILES += gup_benchmark
|
||||
TEST_GEN_FILES += hugepage-mmap
|
||||
TEST_GEN_FILES += hugepage-shm
|
||||
TEST_GEN_FILES += map_hugetlb
|
||||
TEST_GEN_FILES += mlock-random-test
|
||||
TEST_GEN_FILES += mlock2-tests
|
||||
TEST_GEN_FILES += on-fault-limit
|
||||
TEST_GEN_FILES += thuge-gen
|
||||
TEST_GEN_FILES += transhuge-stress
|
||||
TEST_GEN_FILES += userfaultfd
|
||||
TEST_GEN_FILES += mlock-random-test
|
||||
TEST_GEN_FILES += va_128TBswitch
|
||||
TEST_GEN_FILES += virtual_address_range
|
||||
TEST_GEN_FILES += gup_benchmark
|
||||
|
||||
TEST_PROGS := run_vmtests
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue