mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge branch 'linus' into perf/urgent, to synchronize UAPI headers
Signed-off-by: Ingo Molnar <[email protected]>
This commit is contained in:
commit
d6eabce257
11139 changed files with 536190 additions and 258122 deletions
|
|
@ -20,7 +20,7 @@ help:
|
|||
@echo ' kvm_stat - top-like utility for displaying kvm statistics'
|
||||
@echo ' leds - LEDs tools'
|
||||
@echo ' liblockdep - user-space wrapper for kernel locking-validator'
|
||||
@echo ' net - misc networking tools'
|
||||
@echo ' bpf - misc BPF tools'
|
||||
@echo ' perf - Linux performance measurement and analysis tool'
|
||||
@echo ' selftests - various kernel selftests'
|
||||
@echo ' spi - spi tools'
|
||||
|
|
@ -30,6 +30,7 @@ help:
|
|||
@echo ' usb - USB testing tools'
|
||||
@echo ' virtio - vhost test module'
|
||||
@echo ' vm - misc vm tools'
|
||||
@echo ' wmi - WMI interface examples'
|
||||
@echo ' x86_energy_perf_policy - Intel energy policy tool'
|
||||
@echo ''
|
||||
@echo 'You can do:'
|
||||
|
|
@ -58,7 +59,7 @@ acpi: FORCE
|
|||
cpupower: FORCE
|
||||
$(call descend,power/$@)
|
||||
|
||||
cgroup firewire hv guest spi usb virtio vm net iio gpio objtool leds: FORCE
|
||||
cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE
|
||||
$(call descend,$@)
|
||||
|
||||
liblockdep: FORCE
|
||||
|
|
@ -92,8 +93,8 @@ kvm_stat: FORCE
|
|||
|
||||
all: acpi cgroup cpupower gpio hv firewire liblockdep \
|
||||
perf selftests spi turbostat usb \
|
||||
virtio vm net x86_energy_perf_policy \
|
||||
tmon freefall iio objtool kvm_stat
|
||||
virtio vm bpf x86_energy_perf_policy \
|
||||
tmon freefall iio objtool kvm_stat wmi
|
||||
|
||||
acpi_install:
|
||||
$(call descend,power/$(@:_install=),install)
|
||||
|
|
@ -101,7 +102,7 @@ acpi_install:
|
|||
cpupower_install:
|
||||
$(call descend,power/$(@:_install=),install)
|
||||
|
||||
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install net_install objtool_install:
|
||||
cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install:
|
||||
$(call descend,$(@:_install=),install)
|
||||
|
||||
liblockdep_install:
|
||||
|
|
@ -125,8 +126,9 @@ kvm_stat_install:
|
|||
install: acpi_install cgroup_install cpupower_install gpio_install \
|
||||
hv_install firewire_install iio_install liblockdep_install \
|
||||
perf_install selftests_install turbostat_install usb_install \
|
||||
virtio_install vm_install net_install x86_energy_perf_policy_install \
|
||||
tmon_install freefall_install objtool_install kvm_stat_install
|
||||
virtio_install vm_install bpf_install x86_energy_perf_policy_install \
|
||||
tmon_install freefall_install objtool_install kvm_stat_install \
|
||||
wmi_install
|
||||
|
||||
acpi_clean:
|
||||
$(call descend,power/acpi,clean)
|
||||
|
|
@ -134,7 +136,7 @@ acpi_clean:
|
|||
cpupower_clean:
|
||||
$(call descend,power/cpupower,clean)
|
||||
|
||||
cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean objtool_clean leds_clean:
|
||||
cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean:
|
||||
$(call descend,$(@:_clean=),clean)
|
||||
|
||||
liblockdep_clean:
|
||||
|
|
@ -170,8 +172,8 @@ build_clean:
|
|||
|
||||
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
|
||||
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
|
||||
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
|
||||
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
|
||||
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
|
||||
gpio_clean objtool_clean leds_clean
|
||||
gpio_clean objtool_clean leds_clean wmi_clean
|
||||
|
||||
.PHONY: FORCE
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ prefix = /usr
|
|||
CC = gcc
|
||||
LEX = flex
|
||||
YACC = bison
|
||||
MAKE = make
|
||||
|
||||
CFLAGS += -Wall -O2
|
||||
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
||||
|
|
@ -14,7 +15,7 @@ CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
|
|||
%.lex.c: %.l
|
||||
$(LEX) -o $@ $<
|
||||
|
||||
all : bpf_jit_disasm bpf_dbg bpf_asm
|
||||
all: bpf_jit_disasm bpf_dbg bpf_asm bpftool
|
||||
|
||||
bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'
|
||||
bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
|
||||
|
|
@ -27,10 +28,21 @@ bpf_asm : LDLIBS =
|
|||
bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
|
||||
bpf_exp.lex.o : bpf_exp.yacc.c
|
||||
|
||||
clean :
|
||||
clean: bpftool_clean
|
||||
rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.*
|
||||
|
||||
install :
|
||||
install: bpftool_install
|
||||
install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
|
||||
install bpf_dbg $(prefix)/bin/bpf_dbg
|
||||
install bpf_asm $(prefix)/bin/bpf_asm
|
||||
|
||||
bpftool:
|
||||
$(MAKE) -C bpftool
|
||||
|
||||
bpftool_install:
|
||||
$(MAKE) -C bpftool install
|
||||
|
||||
bpftool_clean:
|
||||
$(MAKE) -C bpftool clean
|
||||
|
||||
.PHONY: bpftool FORCE
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include <sys/klog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define CMD_ACTION_SIZE_BUFFER 10
|
||||
#define CMD_ACTION_READ_ALL 3
|
||||
|
|
@ -51,7 +52,7 @@ static void get_exec_path(char *tpath, size_t size)
|
|||
static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
|
||||
{
|
||||
int count, i, pc = 0;
|
||||
char tpath[256];
|
||||
char tpath[PATH_MAX];
|
||||
struct disassemble_info info;
|
||||
disassembler_ftype disassemble;
|
||||
bfd *bfdf;
|
||||
34
tools/bpf/bpftool/Documentation/Makefile
Normal file
34
tools/bpf/bpftool/Documentation/Makefile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
include ../../../scripts/Makefile.include
|
||||
include ../../../scripts/utilities.mak
|
||||
|
||||
INSTALL ?= install
|
||||
RM ?= rm -f
|
||||
|
||||
# Make the path relative to DESTDIR, not prefix
|
||||
ifndef DESTDIR
|
||||
prefix ?= /usr/local
|
||||
endif
|
||||
mandir ?= $(prefix)/share/man
|
||||
man8dir = $(mandir)/man8
|
||||
|
||||
MAN8_RST = $(wildcard *.rst)
|
||||
|
||||
_DOC_MAN8 = $(patsubst %.rst,%.8,$(MAN8_RST))
|
||||
DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8))
|
||||
|
||||
man: man8
|
||||
man8: $(DOC_MAN8)
|
||||
|
||||
$(OUTPUT)%.8: %.rst
|
||||
rst2man $< > $@
|
||||
|
||||
clean:
|
||||
$(call QUIET_CLEAN, Documentation) $(RM) $(DOC_MAN8)
|
||||
|
||||
install: man
|
||||
$(call QUIET_INSTALL, Documentation-man) \
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir); \
|
||||
$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir);
|
||||
|
||||
.PHONY: man man8 clean install
|
||||
.DEFAULT_GOAL := man
|
||||
131
tools/bpf/bpftool/Documentation/bpftool-map.rst
Normal file
131
tools/bpf/bpftool/Documentation/bpftool-map.rst
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
================
|
||||
bpftool-map
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF maps
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **map** *COMMAND*
|
||||
|
||||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
| **pin** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **map show** [*MAP*]
|
||||
| **bpftool** **map dump** *MAP*
|
||||
| **bpftool** **map update** *MAP* **key** *BYTES* **value** *VALUE* [*UPDATE_FLAGS*]
|
||||
| **bpftool** **map lookup** *MAP* **key** *BYTES*
|
||||
| **bpftool** **map getnext** *MAP* [**key** *BYTES*]
|
||||
| **bpftool** **map delete** *MAP* **key** *BYTES*
|
||||
| **bpftool** **map pin** *MAP* *FILE*
|
||||
| **bpftool** **map help**
|
||||
|
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||
| *VALUE* := { *BYTES* | *MAP* | *PROGRAM* }
|
||||
| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool map show** [*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.
|
||||
|
||||
Output will start with map ID followed by map type and
|
||||
zero or more named attributes (depending on kernel version).
|
||||
|
||||
**bpftool map dump** *MAP*
|
||||
Dump all entries in a given *MAP*.
|
||||
|
||||
**bpftool map update** *MAP* **key** *BYTES* **value** *VALUE* [*UPDATE_FLAGS*]
|
||||
Update map entry for a given *KEY*.
|
||||
|
||||
*UPDATE_FLAGS* can be one of: **any** update existing entry
|
||||
or add if doesn't exit; **exist** update only if entry already
|
||||
exists; **noexist** update only if entry doesn't exist.
|
||||
|
||||
**bpftool map lookup** *MAP* **key** *BYTES*
|
||||
Lookup **key** in the map.
|
||||
|
||||
**bpftool map getnext** *MAP* [**key** *BYTES*]
|
||||
Get next key. If *key* is not specified, get first key.
|
||||
|
||||
**bpftool map delete** *MAP* **key** *BYTES*
|
||||
Remove entry from the map.
|
||||
|
||||
**bpftool map pin** *MAP* *FILE*
|
||||
Pin map *MAP* as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount.
|
||||
|
||||
**bpftool map 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 maps.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
**# bpftool map show**
|
||||
::
|
||||
|
||||
10: hash name some_map flags 0x0
|
||||
key 4B value 8B max_entries 2048 memlock 167936B
|
||||
|
||||
**# bpftool map update id 10 key 13 00 07 00 value 02 00 00 00 01 02 03 04**
|
||||
|
||||
**# bpftool map lookup id 10 key 0 1 2 3**
|
||||
|
||||
::
|
||||
|
||||
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
|
||||
|
||||
|
||||
**# bpftool map dump id 10**
|
||||
::
|
||||
|
||||
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
|
||||
key: 0d 00 07 00 value: 02 00 00 00 01 02 03 04
|
||||
Found 2 elements
|
||||
|
||||
**# bpftool map getnext id 10 key 0 1 2 3**
|
||||
::
|
||||
|
||||
key:
|
||||
00 01 02 03
|
||||
next key:
|
||||
0d 00 07 00
|
||||
|
||||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# bpftool map pin id 10 /sys/fs/bpf/map**
|
||||
| **# bpftool map del pinned /sys/fs/bpf/map key 13 00 07 00**
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool**\ (8), **bpftool-prog**\ (8)
|
||||
150
tools/bpf/bpftool/Documentation/bpftool-prog.rst
Normal file
150
tools/bpf/bpftool/Documentation/bpftool-prog.rst
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
================
|
||||
bpftool-prog
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF progs
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] **prog** *COMMAND*
|
||||
|
||||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ **show** | **dump xlated** | **dump jited** | **pin** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** **prog show** [*PROG*]
|
||||
| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}]
|
||||
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
|
||||
| **bpftool** **prog pin** *PROG* *FILE*
|
||||
| **bpftool** **prog help**
|
||||
|
|
||||
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
**bpftool prog show** [*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.
|
||||
|
||||
Output will start with program ID followed by program type and
|
||||
zero or more named attributes (depending on kernel version).
|
||||
|
||||
**bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** }]
|
||||
Dump eBPF instructions of the program from the kernel.
|
||||
If *FILE* is specified image will be written to a file,
|
||||
otherwise it will be disassembled and printed to stdout.
|
||||
|
||||
**opcodes** controls if raw opcodes will be printed.
|
||||
|
||||
**bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** }]
|
||||
Dump jited image (host machine code) of the program.
|
||||
If *FILE* is specified image will be written to a file,
|
||||
otherwise it will be disassembled and printed to stdout.
|
||||
|
||||
**opcodes** controls if raw opcodes will be printed.
|
||||
|
||||
**bpftool prog pin** *PROG* *FILE*
|
||||
Pin program *PROG* as *FILE*.
|
||||
|
||||
Note: *FILE* must be located in *bpffs* mount.
|
||||
|
||||
**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
|
||||
========
|
||||
**# bpftool prog show**
|
||||
::
|
||||
|
||||
10: xdp name some_prog tag 005a3d2123620c8b
|
||||
loaded_at Sep 29/20:11 uid 0
|
||||
xlated 528B jited 370B memlock 4096B map_ids 10
|
||||
|
||||
**# bpftool --json --pretty prog show**
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"programs": [{
|
||||
"id": 10,
|
||||
"type": "xdp",
|
||||
"tag": "005a3d2123620c8b",
|
||||
"loaded_at": "Sep 29/20:11",
|
||||
"uid": 0,
|
||||
"bytes_xlated": 528,
|
||||
"jited": true,
|
||||
"bytes_jited": 370,
|
||||
"bytes_memlock": 4096,
|
||||
"map_ids": [10
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
||||
| **# bpftool prog dump xlated id 10 file /tmp/t**
|
||||
| **# ls -l /tmp/t**
|
||||
| -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
|
||||
|
||||
**# bpftool prog dum jited tag 005a3d2123620c8b**
|
||||
|
||||
::
|
||||
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
sub $0x228,%rsp
|
||||
sub $0x28,%rbp
|
||||
mov %rbx,0x0(%rbp)
|
||||
|
||||
|
|
||||
| **# mount -t bpf none /sys/fs/bpf/**
|
||||
| **# bpftool prog pin id 10 /sys/fs/bpf/prog**
|
||||
| **# ls -l /sys/fs/bpf/**
|
||||
| -rw------- 1 root root 0 Jul 22 01:43 prog
|
||||
|
||||
**# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes**
|
||||
|
||||
::
|
||||
|
||||
push %rbp
|
||||
55
|
||||
mov %rsp,%rbp
|
||||
48 89 e5
|
||||
sub $0x228,%rsp
|
||||
48 81 ec 28 02 00 00
|
||||
sub $0x28,%rbp
|
||||
48 83 ed 28
|
||||
mov %rbx,0x0(%rbp)
|
||||
48 89 5d 00
|
||||
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool**\ (8), **bpftool-map**\ (8)
|
||||
56
tools/bpf/bpftool/Documentation/bpftool.rst
Normal file
56
tools/bpf/bpftool/Documentation/bpftool.rst
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
================
|
||||
BPFTOOL
|
||||
================
|
||||
-------------------------------------------------------------------------------
|
||||
tool for inspection and simple manipulation of eBPF programs and maps
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
:Manual section: 8
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** [*OPTIONS*] *OBJECT* { *COMMAND* | **help** }
|
||||
|
||||
**bpftool** **batch file** *FILE*
|
||||
|
||||
**bpftool** **version**
|
||||
|
||||
*OBJECT* := { **map** | **program** }
|
||||
|
||||
*OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }
|
||||
| { **-j** | **--json** } [{ **-p** | **--pretty** }] }
|
||||
|
||||
*MAP-COMMANDS* :=
|
||||
{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
| **pin** | **help** }
|
||||
|
||||
*PROG-COMMANDS* := { **show** | **dump jited** | **dump xlated** | **pin**
|
||||
| **help** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
*bpftool* allows for inspection and simple modification of BPF objects
|
||||
on the system.
|
||||
|
||||
Note that format of the output of all tools is not guaranteed to be
|
||||
stable and should not be depended upon.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
-h, --help
|
||||
Print short 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**.
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
**bpftool-map**\ (8), **bpftool-prog**\ (8)
|
||||
93
tools/bpf/bpftool/Makefile
Normal file
93
tools/bpf/bpftool/Makefile
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
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)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
BPF_DIR = $(srctree)/tools/lib/bpf/
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
BPF_PATH=$(OUTPUT)
|
||||
else
|
||||
BPF_PATH=$(BPF_DIR)
|
||||
endif
|
||||
|
||||
LIBBPF = $(BPF_PATH)libbpf.a
|
||||
|
||||
$(LIBBPF): FORCE
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT)
|
||||
|
||||
$(LIBBPF)-clean:
|
||||
$(call QUIET_CLEAN, libbpf)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null
|
||||
|
||||
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/
|
||||
LIBS = -lelf -lbfd -lopcodes $(LIBBPF)
|
||||
|
||||
include $(wildcard *.d)
|
||||
|
||||
all: $(OUTPUT)bpftool
|
||||
|
||||
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 $@ $<
|
||||
|
||||
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
$(OUTPUT)%.o: %.c
|
||||
$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<
|
||||
|
||||
clean: $(LIBBPF)-clean
|
||||
$(call QUIET_CLEAN, bpftool)
|
||||
$(Q)rm -rf $(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)
|
||||
|
||||
doc:
|
||||
$(Q)$(MAKE) -C Documentation/
|
||||
|
||||
doc-install:
|
||||
$(Q)$(MAKE) -C Documentation/ install
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: all clean FORCE install doc doc-install
|
||||
.DEFAULT_GOAL := all
|
||||
354
tools/bpf/bpftool/bash-completion/bpftool
Normal file
354
tools/bpf/bpftool/bash-completion/bpftool
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
# bpftool(8) bash completion -*- shell-script -*-
|
||||
#
|
||||
# Copyright (C) 2017 Netronome Systems, Inc.
|
||||
#
|
||||
# This software is dual licensed under the GNU General License
|
||||
# Version 2, June 1991 as shown in the file COPYING in the top-level
|
||||
# directory of this source tree or the BSD 2-Clause License provided
|
||||
# below. You have the option to license this software under the
|
||||
# complete terms of either license.
|
||||
#
|
||||
# The BSD 2-Clause License:
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or
|
||||
# without modification, are permitted provided that the following
|
||||
# conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials
|
||||
# provided with the distribution.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Author: Quentin Monnet <[email protected]>
|
||||
|
||||
# Takes a list of words in argument; each one of them is added to COMPREPLY if
|
||||
# it is not already present on the command line. Returns no value.
|
||||
_bpftool_once_attr()
|
||||
{
|
||||
local w idx found
|
||||
for w in $*; do
|
||||
found=0
|
||||
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
|
||||
if [[ $w == ${words[idx]} ]]; then
|
||||
found=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
[[ $found -eq 0 ]] && \
|
||||
COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
|
||||
done
|
||||
}
|
||||
|
||||
# Takes a list of words in argument; adds them all to COMPREPLY if none of them
|
||||
# is already present on the command line. Returns no value.
|
||||
_bpftool_one_of_list()
|
||||
{
|
||||
local w idx
|
||||
for w in $*; do
|
||||
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
|
||||
[[ $w == ${words[idx]} ]] && return 1
|
||||
done
|
||||
done
|
||||
COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_bpftool_get_map_ids()
|
||||
{
|
||||
COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \
|
||||
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_bpftool_get_prog_ids()
|
||||
{
|
||||
COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
|
||||
command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
|
||||
}
|
||||
|
||||
_bpftool_get_prog_tags()
|
||||
{
|
||||
COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \
|
||||
command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
|
||||
}
|
||||
|
||||
# For bpftool map update: retrieve type of the map to update.
|
||||
_bpftool_map_update_map_type()
|
||||
{
|
||||
local keyword ref
|
||||
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
|
||||
if [[ ${words[$((idx-2))]} == "update" ]]; then
|
||||
keyword=${words[$((idx-1))]}
|
||||
ref=${words[$((idx))]}
|
||||
fi
|
||||
done
|
||||
[[ -z $ref ]] && return 0
|
||||
|
||||
local type
|
||||
type=$(bpftool -jp map show $keyword $ref | \
|
||||
command sed -n 's/.*"type": "\(.*\)",$/\1/p')
|
||||
printf $type
|
||||
}
|
||||
|
||||
_bpftool_map_update_get_id()
|
||||
{
|
||||
# Is it the map to update, or a map to insert into the map to update?
|
||||
# Search for "value" keyword.
|
||||
local idx value
|
||||
for (( idx=7; idx < ${#words[@]}-1; idx++ )); do
|
||||
if [[ ${words[idx]} == "value" ]]; then
|
||||
value=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
[[ $value -eq 0 ]] && _bpftool_get_map_ids && return 0
|
||||
|
||||
# Id to complete is for a value. It can be either prog id or map id. This
|
||||
# depends on the type of the map to update.
|
||||
local type=$(_bpftool_map_update_map_type)
|
||||
case $type in
|
||||
array_of_maps|hash_of_maps)
|
||||
_bpftool_get_map_ids
|
||||
return 0
|
||||
;;
|
||||
prog_array)
|
||||
_bpftool_get_prog_ids
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_bpftool()
|
||||
{
|
||||
local cur prev words objword
|
||||
_init_completion || return
|
||||
|
||||
# Deal with simplest keywords
|
||||
case $prev in
|
||||
help|key|opcodes)
|
||||
return 0
|
||||
;;
|
||||
tag)
|
||||
_bpftool_get_prog_tags
|
||||
return 0
|
||||
;;
|
||||
file|pinned)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
batch)
|
||||
COMPREPLY=( $( compgen -W 'file' -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Search for object and command
|
||||
local object command cmdword
|
||||
for (( cmdword=1; cmdword < ${#words[@]}-1; cmdword++ )); do
|
||||
[[ -n $object ]] && command=${words[cmdword]} && break
|
||||
[[ ${words[cmdword]} != -* ]] && object=${words[cmdword]}
|
||||
done
|
||||
|
||||
if [[ -z $object ]]; then
|
||||
case $cur in
|
||||
-*)
|
||||
local c='--version --json --pretty'
|
||||
COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $( compgen -W "$( bpftool help 2>&1 | \
|
||||
command sed \
|
||||
-e '/OBJECT := /!d' \
|
||||
-e 's/.*{//' \
|
||||
-e 's/}.*//' \
|
||||
-e 's/|//g' )" -- "$cur" ) )
|
||||
COMPREPLY+=( $( compgen -W 'batch help' -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ $command == help ]] && return 0
|
||||
|
||||
# Completion depends on object and command in use
|
||||
case $object in
|
||||
prog)
|
||||
case $prev in
|
||||
id)
|
||||
_bpftool_get_prog_ids
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
local PROG_TYPE='id pinned tag'
|
||||
case $command in
|
||||
show)
|
||||
[[ $prev != "$command" ]] && return 0
|
||||
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
dump)
|
||||
case $prev in
|
||||
$command)
|
||||
COMPREPLY+=( $( compgen -W "xlated jited" -- \
|
||||
"$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
xlated|jited)
|
||||
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
|
||||
"$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
_bpftool_once_attr 'file'
|
||||
COMPREPLY+=( $( compgen -W 'opcodes' -- \
|
||||
"$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
pin)
|
||||
if [[ $prev == "$command" ]]; then
|
||||
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
[[ $prev == $object ]] && \
|
||||
COMPREPLY=( $( compgen -W 'dump help pin show' -- \
|
||||
"$cur" ) )
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
map)
|
||||
local MAP_TYPE='id pinned'
|
||||
case $command in
|
||||
show|dump)
|
||||
case $prev in
|
||||
$command)
|
||||
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
id)
|
||||
_bpftool_get_map_ids
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
lookup|getnext|delete)
|
||||
case $prev in
|
||||
$command)
|
||||
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
id)
|
||||
_bpftool_get_map_ids
|
||||
return 0
|
||||
;;
|
||||
key)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
_bpftool_once_attr 'key'
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
update)
|
||||
case $prev in
|
||||
$command)
|
||||
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
id)
|
||||
_bpftool_map_update_get_id
|
||||
return 0
|
||||
;;
|
||||
key)
|
||||
return 0
|
||||
;;
|
||||
value)
|
||||
# We can have bytes, or references to a prog or a
|
||||
# map, depending on the type of the map to update.
|
||||
case $(_bpftool_map_update_map_type) in
|
||||
array_of_maps|hash_of_maps)
|
||||
local MAP_TYPE='id pinned'
|
||||
COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
|
||||
-- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
prog_array)
|
||||
local PROG_TYPE='id pinned tag'
|
||||
COMPREPLY+=( $( compgen -W "$PROG_TYPE" \
|
||||
-- "$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
_bpftool_once_attr 'key'
|
||||
local UPDATE_FLAGS='any exist noexist'
|
||||
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
|
||||
if [[ ${words[idx]} == 'value' ]]; then
|
||||
# 'value' is present, but is not the last
|
||||
# word i.e. we can now have UPDATE_FLAGS.
|
||||
_bpftool_one_of_list "$UPDATE_FLAGS"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
|
||||
if [[ ${words[idx]} == 'key' ]]; then
|
||||
# 'key' is present, but is not the last
|
||||
# word i.e. we can now have 'value'.
|
||||
_bpftool_once_attr 'value'
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
pin)
|
||||
if [[ $prev == "$command" ]]; then
|
||||
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
|
||||
else
|
||||
_filedir
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
[[ $prev == $object ]] && \
|
||||
COMPREPLY=( $( compgen -W 'delete dump getnext help \
|
||||
lookup pin show update' -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
} &&
|
||||
complete -F _bpftool bpftool
|
||||
|
||||
# ex: ts=4 sw=4 et filetype=sh
|
||||
405
tools/bpf/bpftool/common.c
Normal file
405
tools/bpf/bpftool/common.c
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||
*
|
||||
* This software is dual licensed under the GNU General License Version 2,
|
||||
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||
* source tree or the BSD 2-Clause License provided below. You have the
|
||||
* option to license this software under the complete terms of either license.
|
||||
*
|
||||
* The BSD 2-Clause License:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Author: Jakub Kicinski <[email protected]> */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fts.h>
|
||||
#include <libgen.h>
|
||||
#include <mntent.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include <bpf.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
void p_err(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "error");
|
||||
jsonw_vprintf_enquote(json_wtr, fmt, ap);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
fprintf(stderr, "Error: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void p_info(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (json_output)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static bool is_bpffs(char *path)
|
||||
{
|
||||
struct statfs st_fs;
|
||||
|
||||
if (statfs(path, &st_fs) < 0)
|
||||
return false;
|
||||
|
||||
return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
|
||||
}
|
||||
|
||||
static int mnt_bpffs(const char *target, char *buff, size_t bufflen)
|
||||
{
|
||||
bool bind_done = false;
|
||||
|
||||
while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
|
||||
if (errno != EINVAL || bind_done) {
|
||||
snprintf(buff, bufflen,
|
||||
"mount --make-private %s failed: %s",
|
||||
target, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mount(target, target, "none", MS_BIND, NULL)) {
|
||||
snprintf(buff, bufflen,
|
||||
"mount --bind %s %s failed: %s",
|
||||
target, target, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
bind_done = true;
|
||||
}
|
||||
|
||||
if (mount("bpf", target, "bpf", 0, "mode=0700")) {
|
||||
snprintf(buff, bufflen, "mount -t bpf bpf %s failed: %s",
|
||||
target, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_obj_pinned(char *path)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = bpf_obj_get(path);
|
||||
if (fd < 0) {
|
||||
p_err("bpf obj get (%s): %s", path,
|
||||
errno == EACCES && !is_bpffs(dirname(path)) ?
|
||||
"directory not in bpf file system (bpffs)" :
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
|
||||
{
|
||||
enum bpf_obj_type type;
|
||||
int fd;
|
||||
|
||||
fd = open_obj_pinned(path);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
type = get_fd_type(fd);
|
||||
if (type < 0) {
|
||||
close(fd);
|
||||
return type;
|
||||
}
|
||||
if (type != exp_type) {
|
||||
p_err("incorrect object type: %s", get_fd_type_name(type));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
||||
{
|
||||
char err_str[ERR_MAX_LEN];
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
char *file;
|
||||
char *dir;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (!is_prefix(*argv, "id")) {
|
||||
p_err("expected 'id' got %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
id = strtoul(*argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
p_err("can't parse %s as ID", *argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
if (argc != 1)
|
||||
usage();
|
||||
|
||||
fd = get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
p_err("can't get prog by id (%u): %s", id, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_obj_pin(fd, *argv);
|
||||
if (!err)
|
||||
goto out_close;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const char *get_fd_type_name(enum bpf_obj_type type)
|
||||
{
|
||||
static const char * const names[] = {
|
||||
[BPF_OBJ_UNKNOWN] = "unknown",
|
||||
[BPF_OBJ_PROG] = "prog",
|
||||
[BPF_OBJ_MAP] = "map",
|
||||
};
|
||||
|
||||
if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
|
||||
return names[BPF_OBJ_UNKNOWN];
|
||||
|
||||
return names[type];
|
||||
}
|
||||
|
||||
int get_fd_type(int fd)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[512];
|
||||
ssize_t n;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);
|
||||
|
||||
n = readlink(path, buf, sizeof(buf));
|
||||
if (n < 0) {
|
||||
p_err("can't read link type: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (n == sizeof(path)) {
|
||||
p_err("can't read link type: path too long!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strstr(buf, "bpf-map"))
|
||||
return BPF_OBJ_MAP;
|
||||
else if (strstr(buf, "bpf-prog"))
|
||||
return BPF_OBJ_PROG;
|
||||
|
||||
return BPF_OBJ_UNKNOWN;
|
||||
}
|
||||
|
||||
char *get_fdinfo(int fd, const char *key)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char *line = NULL;
|
||||
size_t line_n = 0;
|
||||
ssize_t n;
|
||||
FILE *fdi;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);
|
||||
|
||||
fdi = fopen(path, "r");
|
||||
if (!fdi) {
|
||||
p_err("can't open fdinfo: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((n = getline(&line, &line_n, fdi))) {
|
||||
char *value;
|
||||
int len;
|
||||
|
||||
if (!strstr(line, key))
|
||||
continue;
|
||||
|
||||
fclose(fdi);
|
||||
|
||||
value = strchr(line, '\t');
|
||||
if (!value || !value[1]) {
|
||||
p_err("malformed fdinfo!?");
|
||||
free(line);
|
||||
return NULL;
|
||||
}
|
||||
value++;
|
||||
|
||||
len = strlen(value);
|
||||
memmove(line, value, len);
|
||||
line[len - 1] = '\0';
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
p_err("key '%s' not found in fdinfo", key);
|
||||
free(line);
|
||||
fclose(fdi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void print_hex_data_json(uint8_t *data, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < len; i++)
|
||||
jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
int build_pinned_obj_table(struct pinned_obj_table *tab,
|
||||
enum bpf_obj_type type)
|
||||
{
|
||||
struct bpf_prog_info pinned_info = {};
|
||||
struct pinned_obj *obj_node = NULL;
|
||||
__u32 len = sizeof(pinned_info);
|
||||
struct mntent *mntent = NULL;
|
||||
enum bpf_obj_type objtype;
|
||||
FILE *mntfile = NULL;
|
||||
FTSENT *ftse = NULL;
|
||||
FTS *fts = NULL;
|
||||
int fd, err;
|
||||
|
||||
mntfile = setmntent("/proc/mounts", "r");
|
||||
if (!mntfile)
|
||||
return -1;
|
||||
|
||||
while ((mntent = getmntent(mntfile))) {
|
||||
char *path[] = { mntent->mnt_dir, NULL };
|
||||
|
||||
if (strncmp(mntent->mnt_type, "bpf", 3) != 0)
|
||||
continue;
|
||||
|
||||
fts = fts_open(path, 0, NULL);
|
||||
if (!fts)
|
||||
continue;
|
||||
|
||||
while ((ftse = fts_read(fts))) {
|
||||
if (!(ftse->fts_info & FTS_F))
|
||||
continue;
|
||||
fd = open_obj_pinned(ftse->fts_path);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
|
||||
objtype = get_fd_type(fd);
|
||||
if (objtype != type) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
memset(&pinned_info, 0, sizeof(pinned_info));
|
||||
err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len);
|
||||
if (err) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
obj_node = malloc(sizeof(*obj_node));
|
||||
if (!obj_node) {
|
||||
close(fd);
|
||||
fts_close(fts);
|
||||
fclose(mntfile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(obj_node, 0, sizeof(*obj_node));
|
||||
obj_node->id = pinned_info.id;
|
||||
obj_node->path = strdup(ftse->fts_path);
|
||||
hash_add(tab->table, &obj_node->hash, obj_node->id);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
fts_close(fts);
|
||||
}
|
||||
fclose(mntfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void delete_pinned_obj_table(struct pinned_obj_table *tab)
|
||||
{
|
||||
struct pinned_obj *obj;
|
||||
struct hlist_node *tmp;
|
||||
unsigned int bkt;
|
||||
|
||||
hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
|
||||
hash_del(&obj->hash);
|
||||
free(obj->path);
|
||||
free(obj);
|
||||
}
|
||||
}
|
||||
162
tools/bpf/bpftool/jit_disasm.c
Normal file
162
tools/bpf/bpftool/jit_disasm.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Based on:
|
||||
*
|
||||
* Minimal BPF JIT image disassembler
|
||||
*
|
||||
* Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
|
||||
* debugging or verification purposes.
|
||||
*
|
||||
* Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
|
||||
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <bfd.h>
|
||||
#include <dis-asm.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "json_writer.h"
|
||||
#include "main.h"
|
||||
|
||||
static void get_exec_path(char *tpath, size_t size)
|
||||
{
|
||||
ssize_t len;
|
||||
char *path;
|
||||
|
||||
snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
|
||||
tpath[size - 1] = 0;
|
||||
|
||||
path = strdup(tpath);
|
||||
assert(path);
|
||||
|
||||
len = readlink(path, tpath, size - 1);
|
||||
assert(len > 0);
|
||||
tpath[len] = 0;
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
static int oper_count;
|
||||
static int fprintf_json(void *out, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *s;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (!oper_count) {
|
||||
int i;
|
||||
|
||||
s = va_arg(ap, char *);
|
||||
|
||||
/* Strip trailing spaces */
|
||||
i = strlen(s) - 1;
|
||||
while (s[i] == ' ')
|
||||
s[i--] = '\0';
|
||||
|
||||
jsonw_string_field(json_wtr, "operation", s);
|
||||
jsonw_name(json_wtr, "operands");
|
||||
jsonw_start_array(json_wtr);
|
||||
oper_count++;
|
||||
} else if (!strcmp(fmt, ",")) {
|
||||
/* Skip */
|
||||
} else {
|
||||
s = va_arg(ap, char *);
|
||||
jsonw_string(json_wtr, s);
|
||||
oper_count++;
|
||||
}
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
||||
{
|
||||
disassembler_ftype disassemble;
|
||||
struct disassemble_info info;
|
||||
int count, i, pc = 0;
|
||||
char tpath[PATH_MAX];
|
||||
bfd *bfdf;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
memset(tpath, 0, sizeof(tpath));
|
||||
get_exec_path(tpath, sizeof(tpath));
|
||||
|
||||
bfdf = bfd_openr(tpath, NULL);
|
||||
assert(bfdf);
|
||||
assert(bfd_check_format(bfdf, bfd_object));
|
||||
|
||||
if (json_output)
|
||||
init_disassemble_info(&info, stdout,
|
||||
(fprintf_ftype) fprintf_json);
|
||||
else
|
||||
init_disassemble_info(&info, stdout,
|
||||
(fprintf_ftype) fprintf);
|
||||
info.arch = bfd_get_arch(bfdf);
|
||||
info.mach = bfd_get_mach(bfdf);
|
||||
info.buffer = image;
|
||||
info.buffer_length = len;
|
||||
|
||||
disassemble_init_for_target(&info);
|
||||
|
||||
disassemble = disassembler(bfdf);
|
||||
assert(disassemble);
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
do {
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
oper_count = 0;
|
||||
jsonw_name(json_wtr, "pc");
|
||||
jsonw_printf(json_wtr, "\"0x%x\"", pc);
|
||||
} else {
|
||||
printf("%4x:\t", pc);
|
||||
}
|
||||
|
||||
count = disassemble(pc, &info);
|
||||
if (json_output) {
|
||||
/* Operand array, was started in fprintf_json. Before
|
||||
* that, make sure we have a _null_ value if no operand
|
||||
* other than operation code was present.
|
||||
*/
|
||||
if (oper_count == 1)
|
||||
jsonw_null(json_wtr);
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
if (opcodes) {
|
||||
if (json_output) {
|
||||
jsonw_name(json_wtr, "opcodes");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < count; ++i)
|
||||
jsonw_printf(json_wtr, "\"0x%02hhx\"",
|
||||
(uint8_t)image[pc + i]);
|
||||
jsonw_end_array(json_wtr);
|
||||
} else {
|
||||
printf("\n\t");
|
||||
for (i = 0; i < count; ++i)
|
||||
printf("%02x ",
|
||||
(uint8_t)image[pc + i]);
|
||||
}
|
||||
}
|
||||
if (json_output)
|
||||
jsonw_end_object(json_wtr);
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
pc += count;
|
||||
} while (count > 0 && pc < len);
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
bfd_close(bfdf);
|
||||
}
|
||||
356
tools/bpf/bpftool/json_writer.c
Normal file
356
tools/bpf/bpftool/json_writer.c
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* Simple streaming JSON writer
|
||||
*
|
||||
* This takes care of the annoying bits of JSON syntax like the commas
|
||||
* after elements
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors: Stephen Hemminger <stephen@networkplumber.org>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "json_writer.h"
|
||||
|
||||
struct json_writer {
|
||||
FILE *out; /* output file */
|
||||
unsigned depth; /* nesting */
|
||||
bool pretty; /* optional whitepace */
|
||||
char sep; /* either nul or comma */
|
||||
};
|
||||
|
||||
/* indentation for pretty print */
|
||||
static void jsonw_indent(json_writer_t *self)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < self->depth; ++i)
|
||||
fputs(" ", self->out);
|
||||
}
|
||||
|
||||
/* end current line and indent if pretty printing */
|
||||
static void jsonw_eol(json_writer_t *self)
|
||||
{
|
||||
if (!self->pretty)
|
||||
return;
|
||||
|
||||
putc('\n', self->out);
|
||||
jsonw_indent(self);
|
||||
}
|
||||
|
||||
/* If current object is not empty print a comma */
|
||||
static void jsonw_eor(json_writer_t *self)
|
||||
{
|
||||
if (self->sep != '\0')
|
||||
putc(self->sep, self->out);
|
||||
self->sep = ',';
|
||||
}
|
||||
|
||||
|
||||
/* Output JSON encoded string */
|
||||
/* Handles C escapes, does not do Unicode */
|
||||
static void jsonw_puts(json_writer_t *self, const char *str)
|
||||
{
|
||||
putc('"', self->out);
|
||||
for (; *str; ++str)
|
||||
switch (*str) {
|
||||
case '\t':
|
||||
fputs("\\t", self->out);
|
||||
break;
|
||||
case '\n':
|
||||
fputs("\\n", self->out);
|
||||
break;
|
||||
case '\r':
|
||||
fputs("\\r", self->out);
|
||||
break;
|
||||
case '\f':
|
||||
fputs("\\f", self->out);
|
||||
break;
|
||||
case '\b':
|
||||
fputs("\\b", self->out);
|
||||
break;
|
||||
case '\\':
|
||||
fputs("\\n", self->out);
|
||||
break;
|
||||
case '"':
|
||||
fputs("\\\"", self->out);
|
||||
break;
|
||||
case '\'':
|
||||
fputs("\\\'", self->out);
|
||||
break;
|
||||
default:
|
||||
putc(*str, self->out);
|
||||
}
|
||||
putc('"', self->out);
|
||||
}
|
||||
|
||||
/* Create a new JSON stream */
|
||||
json_writer_t *jsonw_new(FILE *f)
|
||||
{
|
||||
json_writer_t *self = malloc(sizeof(*self));
|
||||
if (self) {
|
||||
self->out = f;
|
||||
self->depth = 0;
|
||||
self->pretty = false;
|
||||
self->sep = '\0';
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/* End output to JSON stream */
|
||||
void jsonw_destroy(json_writer_t **self_p)
|
||||
{
|
||||
json_writer_t *self = *self_p;
|
||||
|
||||
assert(self->depth == 0);
|
||||
fputs("\n", self->out);
|
||||
fflush(self->out);
|
||||
free(self);
|
||||
*self_p = NULL;
|
||||
}
|
||||
|
||||
void jsonw_pretty(json_writer_t *self, bool on)
|
||||
{
|
||||
self->pretty = on;
|
||||
}
|
||||
|
||||
/* Basic blocks */
|
||||
static void jsonw_begin(json_writer_t *self, int c)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
putc(c, self->out);
|
||||
++self->depth;
|
||||
self->sep = '\0';
|
||||
}
|
||||
|
||||
static void jsonw_end(json_writer_t *self, int c)
|
||||
{
|
||||
assert(self->depth > 0);
|
||||
|
||||
--self->depth;
|
||||
if (self->sep != '\0')
|
||||
jsonw_eol(self);
|
||||
putc(c, self->out);
|
||||
self->sep = ',';
|
||||
}
|
||||
|
||||
|
||||
/* Add a JSON property name */
|
||||
void jsonw_name(json_writer_t *self, const char *name)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
jsonw_eol(self);
|
||||
self->sep = '\0';
|
||||
jsonw_puts(self, name);
|
||||
putc(':', self->out);
|
||||
if (self->pretty)
|
||||
putc(' ', self->out);
|
||||
}
|
||||
|
||||
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
putc('"', self->out);
|
||||
vfprintf(self->out, fmt, ap);
|
||||
putc('"', self->out);
|
||||
}
|
||||
|
||||
void jsonw_printf(json_writer_t *self, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
jsonw_eor(self);
|
||||
vfprintf(self->out, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Collections */
|
||||
void jsonw_start_object(json_writer_t *self)
|
||||
{
|
||||
jsonw_begin(self, '{');
|
||||
}
|
||||
|
||||
void jsonw_end_object(json_writer_t *self)
|
||||
{
|
||||
jsonw_end(self, '}');
|
||||
}
|
||||
|
||||
void jsonw_start_array(json_writer_t *self)
|
||||
{
|
||||
jsonw_begin(self, '[');
|
||||
}
|
||||
|
||||
void jsonw_end_array(json_writer_t *self)
|
||||
{
|
||||
jsonw_end(self, ']');
|
||||
}
|
||||
|
||||
/* JSON value types */
|
||||
void jsonw_string(json_writer_t *self, const char *value)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
jsonw_puts(self, value);
|
||||
}
|
||||
|
||||
void jsonw_bool(json_writer_t *self, bool val)
|
||||
{
|
||||
jsonw_printf(self, "%s", val ? "true" : "false");
|
||||
}
|
||||
|
||||
void jsonw_null(json_writer_t *self)
|
||||
{
|
||||
jsonw_printf(self, "null");
|
||||
}
|
||||
|
||||
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
|
||||
{
|
||||
jsonw_printf(self, fmt, num);
|
||||
}
|
||||
|
||||
#ifdef notused
|
||||
void jsonw_float(json_writer_t *self, double num)
|
||||
{
|
||||
jsonw_printf(self, "%g", num);
|
||||
}
|
||||
#endif
|
||||
|
||||
void jsonw_hu(json_writer_t *self, unsigned short num)
|
||||
{
|
||||
jsonw_printf(self, "%hu", num);
|
||||
}
|
||||
|
||||
void jsonw_uint(json_writer_t *self, uint64_t num)
|
||||
{
|
||||
jsonw_printf(self, "%"PRIu64, num);
|
||||
}
|
||||
|
||||
void jsonw_lluint(json_writer_t *self, unsigned long long int num)
|
||||
{
|
||||
jsonw_printf(self, "%llu", num);
|
||||
}
|
||||
|
||||
void jsonw_int(json_writer_t *self, int64_t num)
|
||||
{
|
||||
jsonw_printf(self, "%"PRId64, num);
|
||||
}
|
||||
|
||||
/* Basic name/value objects */
|
||||
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_string(self, val);
|
||||
}
|
||||
|
||||
void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_bool(self, val);
|
||||
}
|
||||
|
||||
#ifdef notused
|
||||
void jsonw_float_field(json_writer_t *self, const char *prop, double val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_float(self, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
void jsonw_float_field_fmt(json_writer_t *self,
|
||||
const char *prop,
|
||||
const char *fmt,
|
||||
double val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_float_fmt(self, fmt, val);
|
||||
}
|
||||
|
||||
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_uint(self, num);
|
||||
}
|
||||
|
||||
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_hu(self, num);
|
||||
}
|
||||
|
||||
void jsonw_lluint_field(json_writer_t *self,
|
||||
const char *prop,
|
||||
unsigned long long int num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_lluint(self, num);
|
||||
}
|
||||
|
||||
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_int(self, num);
|
||||
}
|
||||
|
||||
void jsonw_null_field(json_writer_t *self, const char *prop)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_null(self);
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
json_writer_t *wr = jsonw_new(stdout);
|
||||
|
||||
jsonw_start_object(wr);
|
||||
jsonw_pretty(wr, true);
|
||||
jsonw_name(wr, "Vyatta");
|
||||
jsonw_start_object(wr);
|
||||
jsonw_string_field(wr, "url", "http://vyatta.com");
|
||||
jsonw_uint_field(wr, "downloads", 2000000ul);
|
||||
jsonw_float_field(wr, "stock", 8.16);
|
||||
|
||||
jsonw_name(wr, "ARGV");
|
||||
jsonw_start_array(wr);
|
||||
while (--argc)
|
||||
jsonw_string(wr, *++argv);
|
||||
jsonw_end_array(wr);
|
||||
|
||||
jsonw_name(wr, "empty");
|
||||
jsonw_start_array(wr);
|
||||
jsonw_end_array(wr);
|
||||
|
||||
jsonw_name(wr, "NIL");
|
||||
jsonw_start_object(wr);
|
||||
jsonw_end_object(wr);
|
||||
|
||||
jsonw_null_field(wr, "my_null");
|
||||
|
||||
jsonw_name(wr, "special chars");
|
||||
jsonw_start_array(wr);
|
||||
jsonw_string_field(wr, "slash", "/");
|
||||
jsonw_string_field(wr, "newline", "\n");
|
||||
jsonw_string_field(wr, "tab", "\t");
|
||||
jsonw_string_field(wr, "ff", "\f");
|
||||
jsonw_string_field(wr, "quote", "\"");
|
||||
jsonw_string_field(wr, "tick", "\'");
|
||||
jsonw_string_field(wr, "backslash", "\\");
|
||||
jsonw_end_array(wr);
|
||||
|
||||
jsonw_end_object(wr);
|
||||
|
||||
jsonw_end_object(wr);
|
||||
jsonw_destroy(&wr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
72
tools/bpf/bpftool/json_writer.h
Normal file
72
tools/bpf/bpftool/json_writer.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Simple streaming JSON writer
|
||||
*
|
||||
* This takes care of the annoying bits of JSON syntax like the commas
|
||||
* after elements
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors: Stephen Hemminger <stephen@networkplumber.org>
|
||||
*/
|
||||
|
||||
#ifndef _JSON_WRITER_H_
|
||||
#define _JSON_WRITER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Opaque class structure */
|
||||
typedef struct json_writer json_writer_t;
|
||||
|
||||
/* Create a new JSON stream */
|
||||
json_writer_t *jsonw_new(FILE *f);
|
||||
/* End output to JSON stream */
|
||||
void jsonw_destroy(json_writer_t **self_p);
|
||||
|
||||
/* Cause output to have pretty whitespace */
|
||||
void jsonw_pretty(json_writer_t *self, bool on);
|
||||
|
||||
/* Add property name */
|
||||
void jsonw_name(json_writer_t *self, const char *name);
|
||||
|
||||
/* Add value */
|
||||
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap);
|
||||
void jsonw_printf(json_writer_t *self, const char *fmt, ...);
|
||||
void jsonw_string(json_writer_t *self, const char *value);
|
||||
void jsonw_bool(json_writer_t *self, bool value);
|
||||
void jsonw_float(json_writer_t *self, double number);
|
||||
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
|
||||
void jsonw_uint(json_writer_t *self, uint64_t number);
|
||||
void jsonw_hu(json_writer_t *self, unsigned short number);
|
||||
void jsonw_int(json_writer_t *self, int64_t number);
|
||||
void jsonw_null(json_writer_t *self);
|
||||
void jsonw_lluint(json_writer_t *self, unsigned long long int num);
|
||||
|
||||
/* Useful Combinations of name and value */
|
||||
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
|
||||
void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
|
||||
void jsonw_float_field(json_writer_t *self, const char *prop, double num);
|
||||
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
|
||||
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
|
||||
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
|
||||
void jsonw_null_field(json_writer_t *self, const char *prop);
|
||||
void jsonw_lluint_field(json_writer_t *self, const char *prop,
|
||||
unsigned long long int num);
|
||||
void jsonw_float_field_fmt(json_writer_t *self, const char *prop,
|
||||
const char *fmt, double val);
|
||||
|
||||
/* Collections */
|
||||
void jsonw_start_object(json_writer_t *self);
|
||||
void jsonw_end_object(json_writer_t *self);
|
||||
|
||||
void jsonw_start_array(json_writer_t *self);
|
||||
void jsonw_end_array(json_writer_t *self);
|
||||
|
||||
/* Override default exception handling */
|
||||
typedef void (jsonw_err_handler_fn)(const char *);
|
||||
|
||||
#endif /* _JSON_WRITER_H_ */
|
||||
343
tools/bpf/bpftool/main.c
Normal file
343
tools/bpf/bpftool/main.c
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||
*
|
||||
* This software is dual licensed under the GNU General License Version 2,
|
||||
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||
* source tree or the BSD 2-Clause License provided below. You have the
|
||||
* option to license this software under the complete terms of either license.
|
||||
*
|
||||
* The BSD 2-Clause License:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Author: Jakub Kicinski <[email protected]> */
|
||||
|
||||
#include <bfd.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/version.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bpf.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
const char *bin_name;
|
||||
static int last_argc;
|
||||
static char **last_argv;
|
||||
static int (*last_do_help)(int argc, char **argv);
|
||||
json_writer_t *json_wtr;
|
||||
bool pretty_output;
|
||||
bool json_output;
|
||||
bool show_pinned;
|
||||
struct pinned_obj_table prog_table;
|
||||
struct pinned_obj_table map_table;
|
||||
|
||||
static void __noreturn clean_and_exit(int i)
|
||||
{
|
||||
if (json_output)
|
||||
jsonw_destroy(&json_wtr);
|
||||
|
||||
exit(i);
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
last_do_help(last_argc - 1, last_argv + 1);
|
||||
|
||||
clean_and_exit(-1);
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
|
||||
" %s batch file FILE\n"
|
||||
" %s version\n"
|
||||
"\n"
|
||||
" OBJECT := { prog | map }\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, bin_name, bin_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_end_object(json_wtr);
|
||||
} else {
|
||||
printf("%s v%u.%u.%u\n", bin_name,
|
||||
version[0], version[1], version[2]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_select(const struct cmd *cmds, int argc, char **argv,
|
||||
int (*help)(int argc, char **argv))
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
last_argc = argc;
|
||||
last_argv = argv;
|
||||
last_do_help = help;
|
||||
|
||||
if (argc < 1 && cmds[0].func)
|
||||
return cmds[0].func(argc, argv);
|
||||
|
||||
for (i = 0; cmds[i].func; i++)
|
||||
if (is_prefix(*argv, cmds[i].cmd))
|
||||
return cmds[i].func(argc - 1, argv + 1);
|
||||
|
||||
help(argc - 1, argv + 1);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool is_prefix(const char *pfx, const char *str)
|
||||
{
|
||||
if (!pfx)
|
||||
return false;
|
||||
if (strlen(str) < strlen(pfx))
|
||||
return false;
|
||||
|
||||
return !memcmp(str, pfx, strlen(pfx));
|
||||
}
|
||||
|
||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
|
||||
{
|
||||
unsigned char *data = arg;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
const char *pfx = "";
|
||||
|
||||
if (!i)
|
||||
/* nothing */;
|
||||
else if (!(i % 16))
|
||||
fprintf(f, "\n");
|
||||
else if (!(i % 8))
|
||||
fprintf(f, " ");
|
||||
else
|
||||
pfx = sep;
|
||||
|
||||
fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_batch(int argc, char **argv);
|
||||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "help", do_help },
|
||||
{ "batch", do_batch },
|
||||
{ "prog", do_prog },
|
||||
{ "map", do_map },
|
||||
{ "version", do_version },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static int do_batch(int argc, char **argv)
|
||||
{
|
||||
unsigned int lines = 0;
|
||||
char *n_argv[4096];
|
||||
char buf[65536];
|
||||
int n_argc;
|
||||
FILE *fp;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
p_err("too few parameters for batch");
|
||||
return -1;
|
||||
} else if (!is_prefix(*argv, "file")) {
|
||||
p_err("expected 'file', got: %s", *argv);
|
||||
return -1;
|
||||
} else if (argc > 2) {
|
||||
p_err("too many parameters for batch");
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
fp = fopen(*argv, "r");
|
||||
if (!fp) {
|
||||
p_err("Can't open file (%s): %s", *argv, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (strlen(buf) == sizeof(buf) - 1) {
|
||||
errno = E2BIG;
|
||||
break;
|
||||
}
|
||||
|
||||
n_argc = 0;
|
||||
n_argv[n_argc] = strtok(buf, " \t\n");
|
||||
|
||||
while (n_argv[n_argc]) {
|
||||
n_argc++;
|
||||
if (n_argc == ARRAY_SIZE(n_argv)) {
|
||||
p_err("line %d has too many arguments, skip",
|
||||
lines);
|
||||
n_argc = 0;
|
||||
break;
|
||||
}
|
||||
n_argv[n_argc] = strtok(NULL, " \t\n");
|
||||
}
|
||||
|
||||
if (!n_argc)
|
||||
continue;
|
||||
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "command");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < n_argc; i++)
|
||||
jsonw_string(json_wtr, n_argv[i]);
|
||||
jsonw_end_array(json_wtr);
|
||||
jsonw_name(json_wtr, "output");
|
||||
}
|
||||
|
||||
err = cmd_select(cmds, n_argc, n_argv, do_help);
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_object(json_wtr);
|
||||
|
||||
if (err)
|
||||
goto err_close;
|
||||
|
||||
lines++;
|
||||
}
|
||||
|
||||
if (errno && errno != ENOENT) {
|
||||
perror("reading batch file failed");
|
||||
err = -1;
|
||||
} else {
|
||||
p_info("processed %d lines", lines);
|
||||
err = 0;
|
||||
}
|
||||
err_close:
|
||||
fclose(fp);
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const struct option options[] = {
|
||||
{ "json", no_argument, NULL, 'j' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "pretty", no_argument, NULL, 'p' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "bpffs", no_argument, NULL, 'f' },
|
||||
{ 0 }
|
||||
};
|
||||
int opt, ret;
|
||||
|
||||
last_do_help = do_help;
|
||||
pretty_output = false;
|
||||
json_output = false;
|
||||
show_pinned = false;
|
||||
bin_name = argv[0];
|
||||
|
||||
hash_init(prog_table.table);
|
||||
hash_init(map_table.table);
|
||||
|
||||
opterr = 0;
|
||||
while ((opt = getopt_long(argc, argv, "Vhpjf",
|
||||
options, NULL)) >= 0) {
|
||||
switch (opt) {
|
||||
case 'V':
|
||||
return do_version(argc, argv);
|
||||
case 'h':
|
||||
return do_help(argc, argv);
|
||||
case 'p':
|
||||
pretty_output = true;
|
||||
/* fall through */
|
||||
case 'j':
|
||||
if (!json_output) {
|
||||
json_wtr = jsonw_new(stdout);
|
||||
if (!json_wtr) {
|
||||
p_err("failed to create JSON writer");
|
||||
return -1;
|
||||
}
|
||||
json_output = true;
|
||||
}
|
||||
jsonw_pretty(json_wtr, pretty_output);
|
||||
break;
|
||||
case 'f':
|
||||
show_pinned = true;
|
||||
break;
|
||||
default:
|
||||
p_err("unrecognized option '%s'", argv[optind - 1]);
|
||||
if (json_output)
|
||||
clean_and_exit(-1);
|
||||
else
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc < 0)
|
||||
usage();
|
||||
|
||||
bfd_init();
|
||||
|
||||
ret = cmd_select(cmds, argc, argv, do_help);
|
||||
|
||||
if (json_output)
|
||||
jsonw_destroy(&json_wtr);
|
||||
|
||||
if (show_pinned) {
|
||||
delete_pinned_obj_table(&prog_table);
|
||||
delete_pinned_obj_table(&map_table);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
123
tools/bpf/bpftool/main.h
Normal file
123
tools/bpf/bpftool/main.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||
*
|
||||
* This software is dual licensed under the GNU General License Version 2,
|
||||
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||
* source tree or the BSD 2-Clause License provided below. You have the
|
||||
* option to license this software under the complete terms of either license.
|
||||
*
|
||||
* The BSD 2-Clause License:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Author: Jakub Kicinski <[email protected]> */
|
||||
|
||||
#ifndef __BPF_TOOL_H
|
||||
#define __BPF_TOOL_H
|
||||
|
||||
/* BFD and kernel.h both define GCC_VERSION, differently */
|
||||
#undef GCC_VERSION
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "json_writer.h"
|
||||
|
||||
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
|
||||
|
||||
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
|
||||
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
|
||||
#define BAD_ARG() ({ p_err("what is '%s'?", *argv); -1; })
|
||||
|
||||
#define ERR_MAX_LEN 1024
|
||||
|
||||
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
||||
|
||||
#define HELP_SPEC_PROGRAM \
|
||||
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
|
||||
#define HELP_SPEC_OPTIONS \
|
||||
"OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }"
|
||||
|
||||
enum bpf_obj_type {
|
||||
BPF_OBJ_UNKNOWN,
|
||||
BPF_OBJ_PROG,
|
||||
BPF_OBJ_MAP,
|
||||
};
|
||||
|
||||
extern const char *bin_name;
|
||||
|
||||
extern json_writer_t *json_wtr;
|
||||
extern bool json_output;
|
||||
extern bool show_pinned;
|
||||
extern struct pinned_obj_table prog_table;
|
||||
extern struct pinned_obj_table map_table;
|
||||
|
||||
void p_err(const char *fmt, ...);
|
||||
void p_info(const char *fmt, ...);
|
||||
|
||||
bool is_prefix(const char *pfx, const char *str);
|
||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
|
||||
void usage(void) __noreturn;
|
||||
|
||||
struct pinned_obj_table {
|
||||
DECLARE_HASHTABLE(table, 16);
|
||||
};
|
||||
|
||||
struct pinned_obj {
|
||||
__u32 id;
|
||||
char *path;
|
||||
struct hlist_node hash;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
struct cmd {
|
||||
const char *cmd;
|
||||
int (*func)(int argc, char **argv);
|
||||
};
|
||||
|
||||
int cmd_select(const struct cmd *cmds, int argc, char **argv,
|
||||
int (*help)(int argc, char **argv));
|
||||
|
||||
int get_fd_type(int fd);
|
||||
const char *get_fd_type_name(enum bpf_obj_type type);
|
||||
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_prog(int argc, char **arg);
|
||||
int do_map(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 print_hex_data_json(uint8_t *data, size_t len);
|
||||
|
||||
#endif
|
||||
899
tools/bpf/bpftool/map.c
Normal file
899
tools/bpf/bpftool/map.c
Normal file
|
|
@ -0,0 +1,899 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||
*
|
||||
* This software is dual licensed under the GNU General License Version 2,
|
||||
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||
* source tree or the BSD 2-Clause License provided below. You have the
|
||||
* option to license this software under the complete terms of either license.
|
||||
*
|
||||
* The BSD 2-Clause License:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Author: Jakub Kicinski <[email protected]> */
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bpf.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
static const char * const map_type_name[] = {
|
||||
[BPF_MAP_TYPE_UNSPEC] = "unspec",
|
||||
[BPF_MAP_TYPE_HASH] = "hash",
|
||||
[BPF_MAP_TYPE_ARRAY] = "array",
|
||||
[BPF_MAP_TYPE_PROG_ARRAY] = "prog_array",
|
||||
[BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array",
|
||||
[BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash",
|
||||
[BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array",
|
||||
[BPF_MAP_TYPE_STACK_TRACE] = "stack_trace",
|
||||
[BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array",
|
||||
[BPF_MAP_TYPE_LRU_HASH] = "lru_hash",
|
||||
[BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash",
|
||||
[BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie",
|
||||
[BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps",
|
||||
[BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps",
|
||||
[BPF_MAP_TYPE_DEVMAP] = "devmap",
|
||||
[BPF_MAP_TYPE_SOCKMAP] = "sockmap",
|
||||
};
|
||||
|
||||
static unsigned int get_possible_cpus(void)
|
||||
{
|
||||
static unsigned int result;
|
||||
char buf[128];
|
||||
long int n;
|
||||
char *ptr;
|
||||
int fd;
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
fd = open("/sys/devices/system/cpu/possible", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
p_err("can't open sysfs possible cpus");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
n = read(fd, buf, sizeof(buf));
|
||||
if (n < 2) {
|
||||
p_err("can't read sysfs possible cpus");
|
||||
exit(-1);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (n == sizeof(buf)) {
|
||||
p_err("read sysfs possible cpus overflow");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
ptr = buf;
|
||||
n = 0;
|
||||
while (*ptr && *ptr != '\n') {
|
||||
unsigned int a, b;
|
||||
|
||||
if (sscanf(ptr, "%u-%u", &a, &b) == 2) {
|
||||
n += b - a + 1;
|
||||
|
||||
ptr = strchr(ptr, '-') + 1;
|
||||
} else if (sscanf(ptr, "%u", &a) == 1) {
|
||||
n++;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
while (isdigit(*ptr))
|
||||
ptr++;
|
||||
if (*ptr == ',')
|
||||
ptr++;
|
||||
}
|
||||
|
||||
result = n;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool map_is_per_cpu(__u32 type)
|
||||
{
|
||||
return type == BPF_MAP_TYPE_PERCPU_HASH ||
|
||||
type == BPF_MAP_TYPE_PERCPU_ARRAY ||
|
||||
type == BPF_MAP_TYPE_LRU_PERCPU_HASH;
|
||||
}
|
||||
|
||||
static bool map_is_map_of_maps(__u32 type)
|
||||
{
|
||||
return type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
|
||||
type == BPF_MAP_TYPE_HASH_OF_MAPS;
|
||||
}
|
||||
|
||||
static bool map_is_map_of_progs(__u32 type)
|
||||
{
|
||||
return type == BPF_MAP_TYPE_PROG_ARRAY;
|
||||
}
|
||||
|
||||
static void *alloc_value(struct bpf_map_info *info)
|
||||
{
|
||||
if (map_is_per_cpu(info->type))
|
||||
return malloc(info->value_size * get_possible_cpus());
|
||||
else
|
||||
return malloc(info->value_size);
|
||||
}
|
||||
|
||||
static int map_parse_fd(int *argc, char ***argv)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (is_prefix(**argv, "id")) {
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
id = strtoul(**argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
p_err("can't parse %s as ID", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
fd = bpf_map_get_fd_by_id(id);
|
||||
if (fd < 0)
|
||||
p_err("get map by id (%u): %s", id, strerror(errno));
|
||||
return fd;
|
||||
} else if (is_prefix(**argv, "pinned")) {
|
||||
char *path;
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
path = **argv;
|
||||
NEXT_ARGP();
|
||||
|
||||
return open_obj_pinned_any(path, BPF_OBJ_MAP);
|
||||
}
|
||||
|
||||
p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
|
||||
{
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
fd = map_parse_fd(argc, argv);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, info, info_len);
|
||||
if (err) {
|
||||
p_err("can't get map info: %s", strerror(errno));
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
|
||||
unsigned char *value)
|
||||
{
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
if (!map_is_per_cpu(info->type)) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info->key_size);
|
||||
jsonw_name(json_wtr, "value");
|
||||
print_hex_data_json(value, info->value_size);
|
||||
} else {
|
||||
unsigned int i, n;
|
||||
|
||||
n = get_possible_cpus();
|
||||
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info->key_size);
|
||||
|
||||
jsonw_name(json_wtr, "values");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < n; i++) {
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
jsonw_int_field(json_wtr, "cpu", i);
|
||||
|
||||
jsonw_name(json_wtr, "value");
|
||||
print_hex_data_json(value + i * info->value_size,
|
||||
info->value_size);
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
|
||||
static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
|
||||
unsigned char *value)
|
||||
{
|
||||
if (!map_is_per_cpu(info->type)) {
|
||||
bool single_line, break_names;
|
||||
|
||||
break_names = info->key_size > 16 || info->value_size > 16;
|
||||
single_line = info->key_size + info->value_size <= 24 &&
|
||||
!break_names;
|
||||
|
||||
printf("key:%c", break_names ? '\n' : ' ');
|
||||
fprint_hex(stdout, key, info->key_size, " ");
|
||||
|
||||
printf(single_line ? " " : "\n");
|
||||
|
||||
printf("value:%c", break_names ? '\n' : ' ');
|
||||
fprint_hex(stdout, value, info->value_size, " ");
|
||||
|
||||
printf("\n");
|
||||
} else {
|
||||
unsigned int i, n;
|
||||
|
||||
n = get_possible_cpus();
|
||||
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info->key_size, " ");
|
||||
printf("\n");
|
||||
for (i = 0; i < n; i++) {
|
||||
printf("value (CPU %02d):%c",
|
||||
i, info->value_size > 16 ? '\n' : ' ');
|
||||
fprint_hex(stdout, value + i * info->value_size,
|
||||
info->value_size, " ");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char **parse_bytes(char **argv, const char *name, unsigned char *val,
|
||||
unsigned int n)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
char *endptr;
|
||||
|
||||
while (i < n && argv[i]) {
|
||||
val[i] = strtoul(argv[i], &endptr, 0);
|
||||
if (*endptr) {
|
||||
p_err("error parsing byte: %s", argv[i]);
|
||||
return NULL;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != n) {
|
||||
p_err("%s expected %d bytes got %d", name, n, i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return argv + i;
|
||||
}
|
||||
|
||||
static int parse_elem(char **argv, struct bpf_map_info *info,
|
||||
void *key, void *value, __u32 key_size, __u32 value_size,
|
||||
__u32 *flags, __u32 **value_fd)
|
||||
{
|
||||
if (!*argv) {
|
||||
if (!key && !value)
|
||||
return 0;
|
||||
p_err("did not find %s", key ? "key" : "value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_prefix(*argv, "key")) {
|
||||
if (!key) {
|
||||
if (key_size)
|
||||
p_err("duplicate key");
|
||||
else
|
||||
p_err("unnecessary key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv = parse_bytes(argv + 1, "key", key, key_size);
|
||||
if (!argv)
|
||||
return -1;
|
||||
|
||||
return parse_elem(argv, info, NULL, value, key_size, value_size,
|
||||
flags, value_fd);
|
||||
} else if (is_prefix(*argv, "value")) {
|
||||
int fd;
|
||||
|
||||
if (!value) {
|
||||
if (value_size)
|
||||
p_err("duplicate value");
|
||||
else
|
||||
p_err("unnecessary value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv++;
|
||||
|
||||
if (map_is_map_of_maps(info->type)) {
|
||||
int argc = 2;
|
||||
|
||||
if (value_size != 4) {
|
||||
p_err("value smaller than 4B for map in map?");
|
||||
return -1;
|
||||
}
|
||||
if (!argv[0] || !argv[1]) {
|
||||
p_err("not enough value arguments for map in map");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = map_parse_fd(&argc, &argv);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
*value_fd = value;
|
||||
**value_fd = fd;
|
||||
} else if (map_is_map_of_progs(info->type)) {
|
||||
int argc = 2;
|
||||
|
||||
if (value_size != 4) {
|
||||
p_err("value smaller than 4B for map of progs?");
|
||||
return -1;
|
||||
}
|
||||
if (!argv[0] || !argv[1]) {
|
||||
p_err("not enough value arguments for map of progs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = prog_parse_fd(&argc, &argv);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
*value_fd = value;
|
||||
**value_fd = fd;
|
||||
} else {
|
||||
argv = parse_bytes(argv, "value", value, value_size);
|
||||
if (!argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return parse_elem(argv, info, key, NULL, key_size, value_size,
|
||||
flags, NULL);
|
||||
} else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") ||
|
||||
is_prefix(*argv, "exist")) {
|
||||
if (!flags) {
|
||||
p_err("flags specified multiple times: %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_prefix(*argv, "any"))
|
||||
*flags = BPF_ANY;
|
||||
else if (is_prefix(*argv, "noexist"))
|
||||
*flags = BPF_NOEXIST;
|
||||
else if (is_prefix(*argv, "exist"))
|
||||
*flags = BPF_EXIST;
|
||||
|
||||
return parse_elem(argv + 1, info, key, value, key_size,
|
||||
value_size, NULL, value_fd);
|
||||
}
|
||||
|
||||
p_err("expected key or value, got: %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int show_map_close_json(int fd, struct bpf_map_info *info)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
memlock = get_fdinfo(fd, "memlock");
|
||||
close(fd);
|
||||
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
jsonw_uint_field(json_wtr, "id", info->id);
|
||||
if (info->type < ARRAY_SIZE(map_type_name))
|
||||
jsonw_string_field(json_wtr, "type",
|
||||
map_type_name[info->type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "type", info->type);
|
||||
|
||||
if (*info->name)
|
||||
jsonw_string_field(json_wtr, "name", info->name);
|
||||
|
||||
jsonw_name(json_wtr, "flags");
|
||||
jsonw_printf(json_wtr, "%#x", info->map_flags);
|
||||
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);
|
||||
|
||||
if (memlock)
|
||||
jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
|
||||
free(memlock);
|
||||
|
||||
if (!hash_empty(map_table.table)) {
|
||||
struct pinned_obj *obj;
|
||||
|
||||
jsonw_name(json_wtr, "pinned");
|
||||
jsonw_start_array(json_wtr);
|
||||
hash_for_each_possible(map_table.table, obj, hash, info->id) {
|
||||
if (obj->id == info->id)
|
||||
jsonw_string(json_wtr, obj->path);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_map_close_plain(int fd, struct bpf_map_info *info)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
memlock = get_fdinfo(fd, "memlock");
|
||||
close(fd);
|
||||
|
||||
printf("%u: ", info->id);
|
||||
if (info->type < ARRAY_SIZE(map_type_name))
|
||||
printf("%s ", map_type_name[info->type]);
|
||||
else
|
||||
printf("type %u ", info->type);
|
||||
|
||||
if (*info->name)
|
||||
printf("name %s ", info->name);
|
||||
|
||||
printf("flags 0x%x\n", info->map_flags);
|
||||
printf("\tkey %uB value %uB max_entries %u",
|
||||
info->key_size, info->value_size, info->max_entries);
|
||||
|
||||
if (memlock)
|
||||
printf(" memlock %sB", memlock);
|
||||
free(memlock);
|
||||
|
||||
printf("\n");
|
||||
if (!hash_empty(map_table.table)) {
|
||||
struct pinned_obj *obj;
|
||||
|
||||
hash_for_each_possible(map_table.table, obj, hash, info->id) {
|
||||
if (obj->id == info->id)
|
||||
printf("\tpinned %s\n", obj->path);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
__u32 id = 0;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (show_pinned)
|
||||
build_pinned_obj_table(&map_table, BPF_OBJ_MAP);
|
||||
|
||||
if (argc == 2) {
|
||||
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (json_output)
|
||||
return show_map_close_json(fd, &info);
|
||||
else
|
||||
return show_map_close_plain(fd, &info);
|
||||
}
|
||||
|
||||
if (argc)
|
||||
return BAD_ARG();
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_map_get_next_id(id, &id);
|
||||
if (err) {
|
||||
if (errno == ENOENT)
|
||||
break;
|
||||
p_err("can't get next map: %s%s", strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = bpf_map_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
p_err("can't get map by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get map info: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
show_map_close_json(fd, &info);
|
||||
else
|
||||
show_map_close_plain(fd, &info);
|
||||
}
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return errno == ENOENT ? 0 : -1;
|
||||
}
|
||||
|
||||
static int do_dump(int argc, char **argv)
|
||||
{
|
||||
void *key, *value, *prev_key;
|
||||
unsigned int num_elems = 0;
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (argc != 2)
|
||||
usage();
|
||||
|
||||
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) {
|
||||
p_err("Dumping maps of maps and program maps not supported");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
key = malloc(info.key_size);
|
||||
value = alloc_value(&info);
|
||||
if (!key || !value) {
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
prev_key = NULL;
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_map_get_next_key(fd, prev_key, key);
|
||||
if (err) {
|
||||
if (errno == ENOENT)
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bpf_map_lookup_elem(fd, key, value)) {
|
||||
if (json_output)
|
||||
print_entry_json(&info, key, value);
|
||||
else
|
||||
print_entry_plain(&info, key, value);
|
||||
} else {
|
||||
if (json_output) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info.key_size);
|
||||
jsonw_name(json_wtr, "value");
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_string_field(json_wtr, "error",
|
||||
"can't lookup element");
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
p_info("can't lookup element with key: ");
|
||||
fprint_hex(stderr, key, info.key_size, " ");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
prev_key = key;
|
||||
num_elems++;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
else
|
||||
printf("Found %u element%s\n", num_elems,
|
||||
num_elems != 1 ? "s" : "");
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
free(value);
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_update(int argc, char **argv)
|
||||
{
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
__u32 *value_fd = NULL;
|
||||
__u32 flags = BPF_ANY;
|
||||
void *key, *value;
|
||||
int fd, err;
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
key = malloc(info.key_size);
|
||||
value = alloc_value(&info);
|
||||
if (!key || !value) {
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
err = parse_elem(argv, &info, key, value, info.key_size,
|
||||
info.value_size, &flags, &value_fd);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
err = bpf_map_update_elem(fd, key, value, flags);
|
||||
if (err) {
|
||||
p_err("update failed: %s", strerror(errno));
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
exit_free:
|
||||
if (value_fd)
|
||||
close(*value_fd);
|
||||
free(key);
|
||||
free(value);
|
||||
close(fd);
|
||||
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_lookup(int argc, char **argv)
|
||||
{
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
void *key, *value;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
key = malloc(info.key_size);
|
||||
value = alloc_value(&info);
|
||||
if (!key || !value) {
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
err = bpf_map_lookup_elem(fd, key, value);
|
||||
if (!err) {
|
||||
if (json_output)
|
||||
print_entry_json(&info, key, value);
|
||||
else
|
||||
print_entry_plain(&info, key, value);
|
||||
} else if (errno == ENOENT) {
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
} else {
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info.key_size, " ");
|
||||
printf("\n\nNot found\n");
|
||||
}
|
||||
} else {
|
||||
p_err("lookup failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
free(value);
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_getnext(int argc, char **argv)
|
||||
{
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
void *key, *nextkey;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
key = malloc(info.key_size);
|
||||
nextkey = malloc(info.key_size);
|
||||
if (!key || !nextkey) {
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
err = parse_elem(argv, &info, key, NULL, info.key_size, 0,
|
||||
NULL, NULL);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
} else {
|
||||
free(key);
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
err = bpf_map_get_next_key(fd, key, nextkey);
|
||||
if (err) {
|
||||
p_err("can't get next key: %s", strerror(errno));
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
if (key) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info.key_size);
|
||||
} else {
|
||||
jsonw_null_field(json_wtr, "key");
|
||||
}
|
||||
jsonw_name(json_wtr, "next_key");
|
||||
print_hex_data_json(nextkey, info.key_size);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
if (key) {
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info.key_size, " ");
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("key: None\n");
|
||||
}
|
||||
printf("next key:\n");
|
||||
fprint_hex(stdout, nextkey, info.key_size, " ");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
exit_free:
|
||||
free(nextkey);
|
||||
free(key);
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_delete(int argc, char **argv)
|
||||
{
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
void *key;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
key = malloc(info.key_size);
|
||||
if (!key) {
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
err = bpf_map_delete_elem(fd, key);
|
||||
if (err)
|
||||
p_err("delete failed: %s", strerror(errno));
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
close(fd);
|
||||
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_pin(int argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = do_pin_any(argc, argv, bpf_map_get_fd_by_id);
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s show [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"
|
||||
" %s %s getnext MAP [key BYTES]\n"
|
||||
" %s %s delete MAP key BYTES\n"
|
||||
" %s %s pin MAP FILE\n"
|
||||
" %s %s help\n"
|
||||
"\n"
|
||||
" MAP := { id MAP_ID | pinned FILE }\n"
|
||||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" VALUE := { BYTES | MAP | PROG }\n"
|
||||
" UPDATE_FLAGS := { any | exist | noexist }\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 },
|
||||
{ "help", do_help },
|
||||
{ "dump", do_dump },
|
||||
{ "update", do_update },
|
||||
{ "lookup", do_lookup },
|
||||
{ "getnext", do_getnext },
|
||||
{ "delete", do_delete },
|
||||
{ "pin", do_pin },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
int do_map(int argc, char **argv)
|
||||
{
|
||||
return cmd_select(cmds, argc, argv, do_help);
|
||||
}
|
||||
672
tools/bpf/bpftool/prog.c
Normal file
672
tools/bpf/bpftool/prog.c
Normal file
|
|
@ -0,0 +1,672 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Netronome Systems, Inc.
|
||||
*
|
||||
* This software is dual licensed under the GNU General License Version 2,
|
||||
* June 1991 as shown in the file COPYING in the top-level directory of this
|
||||
* source tree or the BSD 2-Clause License provided below. You have the
|
||||
* option to license this software under the complete terms of either license.
|
||||
*
|
||||
* The BSD 2-Clause License:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Author: Jakub Kicinski <[email protected]> */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bpf.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "disasm.h"
|
||||
|
||||
static const char * const prog_type_name[] = {
|
||||
[BPF_PROG_TYPE_UNSPEC] = "unspec",
|
||||
[BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter",
|
||||
[BPF_PROG_TYPE_KPROBE] = "kprobe",
|
||||
[BPF_PROG_TYPE_SCHED_CLS] = "sched_cls",
|
||||
[BPF_PROG_TYPE_SCHED_ACT] = "sched_act",
|
||||
[BPF_PROG_TYPE_TRACEPOINT] = "tracepoint",
|
||||
[BPF_PROG_TYPE_XDP] = "xdp",
|
||||
[BPF_PROG_TYPE_PERF_EVENT] = "perf_event",
|
||||
[BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb",
|
||||
[BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock",
|
||||
[BPF_PROG_TYPE_LWT_IN] = "lwt_in",
|
||||
[BPF_PROG_TYPE_LWT_OUT] = "lwt_out",
|
||||
[BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit",
|
||||
[BPF_PROG_TYPE_SOCK_OPS] = "sock_ops",
|
||||
[BPF_PROG_TYPE_SK_SKB] = "sk_skb",
|
||||
};
|
||||
|
||||
static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
|
||||
{
|
||||
struct timespec real_time_ts, boot_time_ts;
|
||||
time_t wallclock_secs;
|
||||
struct tm load_tm;
|
||||
|
||||
buf[--size] = '\0';
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
|
||||
clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
|
||||
perror("Can't read clocks");
|
||||
snprintf(buf, size, "%llu", nsecs / 1000000000);
|
||||
return;
|
||||
}
|
||||
|
||||
wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
|
||||
nsecs / 1000000000;
|
||||
|
||||
if (!localtime_r(&wallclock_secs, &load_tm)) {
|
||||
snprintf(buf, size, "%llu", nsecs / 1000000000);
|
||||
return;
|
||||
}
|
||||
|
||||
strftime(buf, size, "%b %d/%H:%M", &load_tm);
|
||||
}
|
||||
|
||||
static int prog_fd_by_tag(unsigned char *tag)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
unsigned int id = 0;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
while (true) {
|
||||
err = bpf_prog_get_next_id(id, &id);
|
||||
if (err) {
|
||||
p_err("%s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
p_err("can't get prog by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get prog info (%u): %s",
|
||||
id, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!memcmp(tag, info.tag, BPF_TAG_SIZE))
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int prog_parse_fd(int *argc, char ***argv)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (is_prefix(**argv, "id")) {
|
||||
unsigned int id;
|
||||
char *endptr;
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
id = strtoul(**argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
p_err("can't parse %s as ID", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
if (fd < 0)
|
||||
p_err("get by id (%u): %s", id, strerror(errno));
|
||||
return fd;
|
||||
} else if (is_prefix(**argv, "tag")) {
|
||||
unsigned char tag[BPF_TAG_SIZE];
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
|
||||
tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
|
||||
!= BPF_TAG_SIZE) {
|
||||
p_err("can't parse tag");
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
return prog_fd_by_tag(tag);
|
||||
} else if (is_prefix(**argv, "pinned")) {
|
||||
char *path;
|
||||
|
||||
NEXT_ARGP();
|
||||
|
||||
path = **argv;
|
||||
NEXT_ARGP();
|
||||
|
||||
return open_obj_pinned_any(path, BPF_OBJ_PROG);
|
||||
}
|
||||
|
||||
p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void show_prog_maps(int fd, u32 num_maps)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
__u32 map_ids[num_maps];
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
info.nr_map_ids = num_maps;
|
||||
info.map_ids = ptr_to_u64(map_ids);
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err || !info.nr_map_ids)
|
||||
return;
|
||||
|
||||
if (json_output) {
|
||||
jsonw_name(json_wtr, "map_ids");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < info.nr_map_ids; i++)
|
||||
jsonw_uint(json_wtr, map_ids[i]);
|
||||
jsonw_end_array(json_wtr);
|
||||
} else {
|
||||
printf(" map_ids ");
|
||||
for (i = 0; i < info.nr_map_ids; i++)
|
||||
printf("%u%s", map_ids[i],
|
||||
i == info.nr_map_ids - 1 ? "" : ",");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_prog_json(struct bpf_prog_info *info, int fd)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_uint_field(json_wtr, "id", info->id);
|
||||
if (info->type < ARRAY_SIZE(prog_type_name))
|
||||
jsonw_string_field(json_wtr, "type",
|
||||
prog_type_name[info->type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "type", info->type);
|
||||
|
||||
if (*info->name)
|
||||
jsonw_string_field(json_wtr, "name", info->name);
|
||||
|
||||
jsonw_name(json_wtr, "tag");
|
||||
jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
|
||||
info->tag[0], info->tag[1], info->tag[2], info->tag[3],
|
||||
info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
|
||||
|
||||
if (info->load_time) {
|
||||
char buf[32];
|
||||
|
||||
print_boot_time(info->load_time, buf, sizeof(buf));
|
||||
|
||||
/* Piggy back on load_time, since 0 uid is a valid one */
|
||||
jsonw_string_field(json_wtr, "loaded_at", buf);
|
||||
jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
|
||||
}
|
||||
|
||||
jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
|
||||
|
||||
if (info->jited_prog_len) {
|
||||
jsonw_bool_field(json_wtr, "jited", true);
|
||||
jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
|
||||
} else {
|
||||
jsonw_bool_field(json_wtr, "jited", false);
|
||||
}
|
||||
|
||||
memlock = get_fdinfo(fd, "memlock");
|
||||
if (memlock)
|
||||
jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
|
||||
free(memlock);
|
||||
|
||||
if (info->nr_map_ids)
|
||||
show_prog_maps(fd, info->nr_map_ids);
|
||||
|
||||
if (!hash_empty(prog_table.table)) {
|
||||
struct pinned_obj *obj;
|
||||
|
||||
jsonw_name(json_wtr, "pinned");
|
||||
jsonw_start_array(json_wtr);
|
||||
hash_for_each_possible(prog_table.table, obj, hash, info->id) {
|
||||
if (obj->id == info->id)
|
||||
jsonw_string(json_wtr, obj->path);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
|
||||
static void print_prog_plain(struct bpf_prog_info *info, int fd)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
printf("%u: ", info->id);
|
||||
if (info->type < ARRAY_SIZE(prog_type_name))
|
||||
printf("%s ", prog_type_name[info->type]);
|
||||
else
|
||||
printf("type %u ", info->type);
|
||||
|
||||
if (*info->name)
|
||||
printf("name %s ", info->name);
|
||||
|
||||
printf("tag ");
|
||||
fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
|
||||
printf("\n");
|
||||
|
||||
if (info->load_time) {
|
||||
char buf[32];
|
||||
|
||||
print_boot_time(info->load_time, buf, sizeof(buf));
|
||||
|
||||
/* Piggy back on load_time, since 0 uid is a valid one */
|
||||
printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid);
|
||||
}
|
||||
|
||||
printf("\txlated %uB", info->xlated_prog_len);
|
||||
|
||||
if (info->jited_prog_len)
|
||||
printf(" jited %uB", info->jited_prog_len);
|
||||
else
|
||||
printf(" not jited");
|
||||
|
||||
memlock = get_fdinfo(fd, "memlock");
|
||||
if (memlock)
|
||||
printf(" memlock %sB", memlock);
|
||||
free(memlock);
|
||||
|
||||
if (info->nr_map_ids)
|
||||
show_prog_maps(fd, info->nr_map_ids);
|
||||
|
||||
if (!hash_empty(prog_table.table)) {
|
||||
struct pinned_obj *obj;
|
||||
|
||||
printf("\n");
|
||||
hash_for_each_possible(prog_table.table, obj, hash, info->id) {
|
||||
if (obj->id == info->id)
|
||||
printf("\tpinned %s\n", obj->path);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int show_prog(int fd)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
int err;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
print_prog_json(&info, fd);
|
||||
else
|
||||
print_prog_plain(&info, fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{
|
||||
__u32 id = 0;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (show_pinned)
|
||||
build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
|
||||
|
||||
if (argc == 2) {
|
||||
fd = prog_parse_fd(&argc, &argv);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
return show_prog(fd);
|
||||
}
|
||||
|
||||
if (argc)
|
||||
return BAD_ARG();
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_prog_get_next_id(id, &id);
|
||||
if (err) {
|
||||
if (errno == ENOENT) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
p_err("can't get next program: %s%s", strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
p_err("can't get prog by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
err = show_prog(fd);
|
||||
close(fd);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes)
|
||||
{
|
||||
struct bpf_insn *insn = buf;
|
||||
bool double_insn = false;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len / sizeof(*insn); i++) {
|
||||
if (double_insn) {
|
||||
double_insn = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
|
||||
|
||||
printf("% 4d: ", i);
|
||||
print_bpf_insn(print_insn, NULL, insn + i, true);
|
||||
|
||||
if (opcodes) {
|
||||
printf(" ");
|
||||
fprint_hex(stdout, insn + i, 8, " ");
|
||||
if (double_insn && i < len - 1) {
|
||||
printf(" ");
|
||||
fprint_hex(stdout, insn + i + 1, 8, " ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...)
|
||||
{
|
||||
unsigned int l = strlen(fmt);
|
||||
char chomped_fmt[l];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
if (l > 0) {
|
||||
strncpy(chomped_fmt, fmt, l - 1);
|
||||
chomped_fmt[l - 1] = '\0';
|
||||
}
|
||||
jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)
|
||||
{
|
||||
struct bpf_insn *insn = buf;
|
||||
bool double_insn = false;
|
||||
unsigned int i;
|
||||
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < len / sizeof(*insn); i++) {
|
||||
if (double_insn) {
|
||||
double_insn = false;
|
||||
continue;
|
||||
}
|
||||
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
|
||||
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "disasm");
|
||||
print_bpf_insn(print_insn_json, NULL, insn + i, true);
|
||||
|
||||
if (opcodes) {
|
||||
jsonw_name(json_wtr, "opcodes");
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
jsonw_name(json_wtr, "code");
|
||||
jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
|
||||
|
||||
jsonw_name(json_wtr, "src_reg");
|
||||
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
|
||||
|
||||
jsonw_name(json_wtr, "dst_reg");
|
||||
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
|
||||
|
||||
jsonw_name(json_wtr, "off");
|
||||
print_hex_data_json((uint8_t *)(&insn[i].off), 2);
|
||||
|
||||
jsonw_name(json_wtr, "imm");
|
||||
if (double_insn && i < len - 1)
|
||||
print_hex_data_json((uint8_t *)(&insn[i].imm),
|
||||
12);
|
||||
else
|
||||
print_hex_data_json((uint8_t *)(&insn[i].imm),
|
||||
4);
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
static int do_dump(int argc, char **argv)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
unsigned int buf_size;
|
||||
char *filepath = NULL;
|
||||
bool opcodes = false;
|
||||
unsigned char *buf;
|
||||
__u32 *member_len;
|
||||
__u64 *member_ptr;
|
||||
ssize_t n;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
if (is_prefix(*argv, "jited")) {
|
||||
member_len = &info.jited_prog_len;
|
||||
member_ptr = &info.jited_prog_insns;
|
||||
} else if (is_prefix(*argv, "xlated")) {
|
||||
member_len = &info.xlated_prog_len;
|
||||
member_ptr = &info.xlated_prog_insns;
|
||||
} else {
|
||||
p_err("expected 'xlated' or 'jited', got: %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
fd = prog_parse_fd(&argc, &argv);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (is_prefix(*argv, "file")) {
|
||||
NEXT_ARG();
|
||||
if (!argc) {
|
||||
p_err("expected file path");
|
||||
return -1;
|
||||
}
|
||||
|
||||
filepath = *argv;
|
||||
NEXT_ARG();
|
||||
} else if (is_prefix(*argv, "opcodes")) {
|
||||
opcodes = true;
|
||||
NEXT_ARG();
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*member_len) {
|
||||
p_info("no instructions returned");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf_size = *member_len;
|
||||
|
||||
buf = malloc(buf_size);
|
||||
if (!buf) {
|
||||
p_err("mem alloc failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
*member_ptr = ptr_to_u64(buf);
|
||||
*member_len = buf_size;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
close(fd);
|
||||
if (err) {
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (*member_len > buf_size) {
|
||||
p_err("too many instructions returned");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (filepath) {
|
||||
fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
if (fd < 0) {
|
||||
p_err("can't open file %s: %s", filepath,
|
||||
strerror(errno));
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
n = write(fd, buf, *member_len);
|
||||
close(fd);
|
||||
if (n != *member_len) {
|
||||
p_err("error writing output file: %s",
|
||||
n < 0 ? strerror(errno) : "short write");
|
||||
goto err_free;
|
||||
}
|
||||
} else {
|
||||
if (member_len == &info.jited_prog_len)
|
||||
disasm_print_insn(buf, *member_len, opcodes);
|
||||
else
|
||||
if (json_output)
|
||||
dump_xlated_json(buf, *member_len, opcodes);
|
||||
else
|
||||
dump_xlated_plain(buf, *member_len, opcodes);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int do_pin(int argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s show [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 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]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cmd cmds[] = {
|
||||
{ "show", do_show },
|
||||
{ "help", do_help },
|
||||
{ "dump", do_dump },
|
||||
{ "pin", do_pin },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
int do_prog(int argc, char **argv)
|
||||
{
|
||||
return cmd_select(cmds, argc, argv, do_help);
|
||||
}
|
||||
|
|
@ -76,7 +76,8 @@ int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
|
|||
fd = open(chrdev_name, 0);
|
||||
if (fd == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to open %s\n", chrdev_name);
|
||||
fprintf(stderr, "Failed to open %s, %s\n",
|
||||
chrdev_name, strerror(errno));
|
||||
goto exit_close_error;
|
||||
}
|
||||
|
||||
|
|
@ -92,8 +93,8 @@ int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
|
|||
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GET LINEHANDLE IOCTL (%d)\n",
|
||||
ret);
|
||||
fprintf(stderr, "Failed to issue %s (%d), %s\n",
|
||||
"GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno));
|
||||
}
|
||||
|
||||
exit_close_error:
|
||||
|
|
@ -118,8 +119,9 @@ int gpiotools_set_values(const int fd, struct gpiohandle_data *data)
|
|||
ret = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue %s (%d)\n",
|
||||
"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);
|
||||
fprintf(stderr, "Failed to issue %s (%d), %s\n",
|
||||
"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -141,8 +143,9 @@ int gpiotools_get_values(const int fd, struct gpiohandle_data *data)
|
|||
ret = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue %s (%d)\n",
|
||||
"GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret);
|
||||
fprintf(stderr, "Failed to issue %s (%d), %s\n",
|
||||
"GPIOHANDLE_GET_LINE_VALUES_IOCTL", ret,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -193,11 +193,14 @@ static void kvp_update_mem_state(int pool)
|
|||
for (;;) {
|
||||
readp = &record[records_read];
|
||||
records_read += fread(readp, sizeof(struct kvp_record),
|
||||
ENTRIES_PER_BLOCK * num_blocks,
|
||||
filep);
|
||||
ENTRIES_PER_BLOCK * num_blocks - records_read,
|
||||
filep);
|
||||
|
||||
if (ferror(filep)) {
|
||||
syslog(LOG_ERR, "Failed to read file, pool: %d", pool);
|
||||
syslog(LOG_ERR,
|
||||
"Failed to read file, pool: %d; error: %d %s",
|
||||
pool, errno, strerror(errno));
|
||||
kvp_release_lock(pool);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
|
@ -210,6 +213,7 @@ static void kvp_update_mem_state(int pool)
|
|||
|
||||
if (record == NULL) {
|
||||
syslog(LOG_ERR, "malloc failed");
|
||||
kvp_release_lock(pool);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
continue;
|
||||
|
|
@ -224,15 +228,11 @@ static void kvp_update_mem_state(int pool)
|
|||
fclose(filep);
|
||||
kvp_release_lock(pool);
|
||||
}
|
||||
|
||||
static int kvp_file_init(void)
|
||||
{
|
||||
int fd;
|
||||
FILE *filep;
|
||||
size_t records_read;
|
||||
char *fname;
|
||||
struct kvp_record *record;
|
||||
struct kvp_record *readp;
|
||||
int num_blocks;
|
||||
int i;
|
||||
int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
|
||||
|
||||
|
|
@ -246,61 +246,19 @@ static int kvp_file_init(void)
|
|||
|
||||
for (i = 0; i < KVP_POOL_COUNT; i++) {
|
||||
fname = kvp_file_info[i].fname;
|
||||
records_read = 0;
|
||||
num_blocks = 1;
|
||||
sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
|
||||
fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);
|
||||
|
||||
if (fd == -1)
|
||||
return 1;
|
||||
|
||||
|
||||
filep = fopen(fname, "re");
|
||||
if (!filep) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
record = malloc(alloc_unit * num_blocks);
|
||||
if (record == NULL) {
|
||||
fclose(filep);
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
for (;;) {
|
||||
readp = &record[records_read];
|
||||
records_read += fread(readp, sizeof(struct kvp_record),
|
||||
ENTRIES_PER_BLOCK,
|
||||
filep);
|
||||
|
||||
if (ferror(filep)) {
|
||||
syslog(LOG_ERR, "Failed to read file, pool: %d",
|
||||
i);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!feof(filep)) {
|
||||
/*
|
||||
* We have more data to read.
|
||||
*/
|
||||
num_blocks++;
|
||||
record = realloc(record, alloc_unit *
|
||||
num_blocks);
|
||||
if (record == NULL) {
|
||||
fclose(filep);
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
kvp_file_info[i].fd = fd;
|
||||
kvp_file_info[i].num_blocks = num_blocks;
|
||||
kvp_file_info[i].records = record;
|
||||
kvp_file_info[i].num_records = records_read;
|
||||
fclose(filep);
|
||||
|
||||
kvp_file_info[i].num_blocks = 1;
|
||||
kvp_file_info[i].records = malloc(alloc_unit);
|
||||
if (kvp_file_info[i].records == NULL)
|
||||
return 1;
|
||||
kvp_file_info[i].num_records = 0;
|
||||
kvp_update_mem_state(i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,9 +1 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LIBLOCKDEP_LINUX_KMEMCHECK_H_
|
||||
#define _LIBLOCKDEP_LINUX_KMEMCHECK_H_
|
||||
|
||||
static inline void kmemcheck_mark_initialized(void *address, unsigned int n)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#define MAP_SHARED 0x01 /* Share changes */
|
||||
#define MAP_PRIVATE 0x02 /* Changes are private */
|
||||
#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */
|
||||
#define MAP_TYPE 0x0f /* Mask for type of mapping */
|
||||
#define MAP_FIXED 0x10 /* Interpret addr exactly */
|
||||
#define MAP_ANONYMOUS 0x20 /* don't use a file */
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ enum bpf_cmd {
|
|||
BPF_PROG_GET_FD_BY_ID,
|
||||
BPF_MAP_GET_FD_BY_ID,
|
||||
BPF_OBJ_GET_INFO_BY_FD,
|
||||
BPF_PROG_QUERY,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
|
|
@ -112,6 +113,7 @@ enum bpf_map_type {
|
|||
BPF_MAP_TYPE_HASH_OF_MAPS,
|
||||
BPF_MAP_TYPE_DEVMAP,
|
||||
BPF_MAP_TYPE_SOCKMAP,
|
||||
BPF_MAP_TYPE_CPUMAP,
|
||||
};
|
||||
|
||||
enum bpf_prog_type {
|
||||
|
|
@ -130,6 +132,7 @@ enum bpf_prog_type {
|
|||
BPF_PROG_TYPE_LWT_XMIT,
|
||||
BPF_PROG_TYPE_SOCK_OPS,
|
||||
BPF_PROG_TYPE_SK_SKB,
|
||||
BPF_PROG_TYPE_CGROUP_DEVICE,
|
||||
};
|
||||
|
||||
enum bpf_attach_type {
|
||||
|
|
@ -139,16 +142,53 @@ enum bpf_attach_type {
|
|||
BPF_CGROUP_SOCK_OPS,
|
||||
BPF_SK_SKB_STREAM_PARSER,
|
||||
BPF_SK_SKB_STREAM_VERDICT,
|
||||
BPF_CGROUP_DEVICE,
|
||||
__MAX_BPF_ATTACH_TYPE
|
||||
};
|
||||
|
||||
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
|
||||
|
||||
/* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command
|
||||
* to the given target_fd cgroup the descendent cgroup will be able to
|
||||
* override effective bpf program that was inherited from this cgroup
|
||||
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
|
||||
*
|
||||
* NONE(default): No further bpf programs allowed in the subtree.
|
||||
*
|
||||
* BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program,
|
||||
* the program in this cgroup yields to sub-cgroup program.
|
||||
*
|
||||
* BPF_F_ALLOW_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
|
||||
* NONE or BPF_F_ALLOW_OVERRIDE flag.
|
||||
* Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will
|
||||
* release old program and attach the new one. Attach flags has to match.
|
||||
*
|
||||
* Multiple programs are allowed to be attached to a cgroup with
|
||||
* BPF_F_ALLOW_MULTI flag. They are executed in FIFO order
|
||||
* (those that were attached first, run first)
|
||||
* The programs of sub-cgroup are executed first, then programs of
|
||||
* this cgroup and then programs of parent cgroup.
|
||||
* When children program makes decision (like picking TCP CA or sock bind)
|
||||
* parent program has a chance to override it.
|
||||
*
|
||||
* A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups.
|
||||
* A cgroup with NONE doesn't allow any programs in sub-cgroups.
|
||||
* Ex1:
|
||||
* cgrp1 (MULTI progs A, B) ->
|
||||
* cgrp2 (OVERRIDE prog C) ->
|
||||
* cgrp3 (MULTI prog D) ->
|
||||
* cgrp4 (OVERRIDE prog E) ->
|
||||
* cgrp5 (NONE prog F)
|
||||
* the event in cgrp5 triggers execution of F,D,A,B in that order.
|
||||
* if prog F is detached, the execution is E,D,A,B
|
||||
* if prog F and D are detached, the execution is E,A,B
|
||||
* if prog F, E and D are detached, the execution is C,A,B
|
||||
*
|
||||
* All eligible programs are executed regardless of return code from
|
||||
* earlier programs.
|
||||
*/
|
||||
#define BPF_F_ALLOW_OVERRIDE (1U << 0)
|
||||
#define BPF_F_ALLOW_MULTI (1U << 1)
|
||||
|
||||
/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the
|
||||
* verifier will perform strict alignment checking as if the kernel
|
||||
|
|
@ -176,6 +216,15 @@ enum bpf_attach_type {
|
|||
/* Specify numa node during map creation */
|
||||
#define BPF_F_NUMA_NODE (1U << 2)
|
||||
|
||||
/* flags for BPF_PROG_QUERY */
|
||||
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
||||
|
||||
#define BPF_OBJ_NAME_LEN 16U
|
||||
|
||||
/* Flags for accessing BPF object */
|
||||
#define BPF_F_RDONLY (1U << 3)
|
||||
#define BPF_F_WRONLY (1U << 4)
|
||||
|
||||
union bpf_attr {
|
||||
struct { /* anonymous struct used by BPF_MAP_CREATE command */
|
||||
__u32 map_type; /* one of enum bpf_map_type */
|
||||
|
|
@ -189,6 +238,7 @@ union bpf_attr {
|
|||
__u32 numa_node; /* numa node (effective only if
|
||||
* BPF_F_NUMA_NODE is set).
|
||||
*/
|
||||
char map_name[BPF_OBJ_NAME_LEN];
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
|
||||
|
|
@ -211,11 +261,14 @@ union bpf_attr {
|
|||
__aligned_u64 log_buf; /* user supplied buffer */
|
||||
__u32 kern_version; /* checked when prog_type=kprobe */
|
||||
__u32 prog_flags;
|
||||
char prog_name[BPF_OBJ_NAME_LEN];
|
||||
__u32 prog_ifindex; /* ifindex of netdev to prep for */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||
__aligned_u64 pathname;
|
||||
__u32 bpf_fd;
|
||||
__u32 file_flags;
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
||||
|
|
@ -243,6 +296,7 @@ union bpf_attr {
|
|||
__u32 map_id;
|
||||
};
|
||||
__u32 next_id;
|
||||
__u32 open_flags;
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
|
||||
|
|
@ -250,6 +304,15 @@ union bpf_attr {
|
|||
__u32 info_len;
|
||||
__aligned_u64 info;
|
||||
} info;
|
||||
|
||||
struct { /* anonymous struct used by BPF_PROG_QUERY command */
|
||||
__u32 target_fd; /* container object to query */
|
||||
__u32 attach_type;
|
||||
__u32 query_flags;
|
||||
__u32 attach_flags;
|
||||
__aligned_u64 prog_ids;
|
||||
__u32 prog_cnt;
|
||||
} query;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* BPF helper function descriptions:
|
||||
|
|
@ -554,12 +617,22 @@ union bpf_attr {
|
|||
* int bpf_setsockopt(bpf_socket, level, optname, optval, optlen)
|
||||
* Calls setsockopt. Not all opts are available, only those with
|
||||
* integer optvals plus TCP_CONGESTION.
|
||||
* Supported levels: SOL_SOCKET and IPROTO_TCP
|
||||
* Supported levels: SOL_SOCKET and IPPROTO_TCP
|
||||
* @bpf_socket: pointer to bpf_socket
|
||||
* @level: SOL_SOCKET or IPROTO_TCP
|
||||
* @level: SOL_SOCKET or IPPROTO_TCP
|
||||
* @optname: option name
|
||||
* @optval: pointer to option value
|
||||
* @optlen: length of optval in byes
|
||||
* @optlen: length of optval in bytes
|
||||
* Return: 0 or negative error
|
||||
*
|
||||
* int bpf_getsockopt(bpf_socket, level, optname, optval, optlen)
|
||||
* Calls getsockopt. Not all opts are available.
|
||||
* Supported levels: IPPROTO_TCP
|
||||
* @bpf_socket: pointer to bpf_socket
|
||||
* @level: IPPROTO_TCP
|
||||
* @optname: option name
|
||||
* @optval: pointer to option value
|
||||
* @optlen: length of optval in bytes
|
||||
* Return: 0 or negative error
|
||||
*
|
||||
* int bpf_skb_adjust_room(skb, len_diff, mode, flags)
|
||||
|
|
@ -583,6 +656,27 @@ union bpf_attr {
|
|||
* @map: pointer to sockmap to update
|
||||
* @key: key to insert/update sock in map
|
||||
* @flags: same flags as map update elem
|
||||
*
|
||||
* int bpf_xdp_adjust_meta(xdp_md, delta)
|
||||
* Adjust the xdp_md.data_meta by delta
|
||||
* @xdp_md: pointer to xdp_md
|
||||
* @delta: An positive/negative integer to be added to xdp_md.data_meta
|
||||
* Return: 0 on success or negative on error
|
||||
*
|
||||
* int bpf_perf_event_read_value(map, flags, buf, buf_size)
|
||||
* read perf event counter value and perf event enabled/running time
|
||||
* @map: pointer to perf_event_array map
|
||||
* @flags: index of event in the map or bitmask flags
|
||||
* @buf: buf to fill
|
||||
* @buf_size: size of the buf
|
||||
* Return: 0 on success or negative error code
|
||||
*
|
||||
* int bpf_perf_prog_read_value(ctx, buf, buf_size)
|
||||
* read perf prog attached perf event counter and enabled/running time
|
||||
* @ctx: pointer to ctx
|
||||
* @buf: buf to fill
|
||||
* @buf_size: size of the buf
|
||||
* Return : 0 on success or negative error code
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
|
|
@ -639,6 +733,10 @@ union bpf_attr {
|
|||
FN(redirect_map), \
|
||||
FN(sk_redirect_map), \
|
||||
FN(sock_map_update), \
|
||||
FN(xdp_adjust_meta), \
|
||||
FN(perf_event_read_value), \
|
||||
FN(perf_prog_read_value), \
|
||||
FN(getsockopt),
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
* function eBPF program intends to call
|
||||
|
|
@ -682,7 +780,9 @@ enum bpf_func_id {
|
|||
#define BPF_F_ZERO_CSUM_TX (1ULL << 1)
|
||||
#define BPF_F_DONT_FRAGMENT (1ULL << 2)
|
||||
|
||||
/* BPF_FUNC_perf_event_output and BPF_FUNC_perf_event_read flags. */
|
||||
/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
|
||||
* BPF_FUNC_perf_event_read_value flags.
|
||||
*/
|
||||
#define BPF_F_INDEX_MASK 0xffffffffULL
|
||||
#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
|
||||
/* BPF_FUNC_perf_event_output for sk_buff input context. */
|
||||
|
|
@ -716,7 +816,7 @@ struct __sk_buff {
|
|||
__u32 data_end;
|
||||
__u32 napi_id;
|
||||
|
||||
/* accessed by BPF_PROG_TYPE_sk_skb types */
|
||||
/* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
|
||||
__u32 family;
|
||||
__u32 remote_ip4; /* Stored in network byte order */
|
||||
__u32 local_ip4; /* Stored in network byte order */
|
||||
|
|
@ -724,6 +824,9 @@ struct __sk_buff {
|
|||
__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 */
|
||||
/* ... here. */
|
||||
|
||||
__u32 data_meta;
|
||||
};
|
||||
|
||||
struct bpf_tunnel_key {
|
||||
|
|
@ -784,6 +887,7 @@ enum xdp_action {
|
|||
struct xdp_md {
|
||||
__u32 data;
|
||||
__u32 data_end;
|
||||
__u32 data_meta;
|
||||
};
|
||||
|
||||
enum sk_action {
|
||||
|
|
@ -801,6 +905,11 @@ struct bpf_prog_info {
|
|||
__u32 xlated_prog_len;
|
||||
__aligned_u64 jited_prog_insns;
|
||||
__aligned_u64 xlated_prog_insns;
|
||||
__u64 load_time; /* ns since boottime */
|
||||
__u32 created_by_uid;
|
||||
__u32 nr_map_ids;
|
||||
__aligned_u64 map_ids;
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_map_info {
|
||||
|
|
@ -810,6 +919,7 @@ struct bpf_map_info {
|
|||
__u32 value_size;
|
||||
__u32 max_entries;
|
||||
__u32 map_flags;
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* User bpf_sock_ops struct to access socket values and specify request ops
|
||||
|
|
@ -859,9 +969,35 @@ enum {
|
|||
BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control
|
||||
* needs ECN
|
||||
*/
|
||||
BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is
|
||||
* based on the path and may be
|
||||
* dependent on the congestion control
|
||||
* algorithm. In general it indicates
|
||||
* a congestion threshold. RTTs above
|
||||
* this indicate congestion
|
||||
*/
|
||||
};
|
||||
|
||||
#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
|
||||
#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */
|
||||
|
||||
struct bpf_perf_event_value {
|
||||
__u64 counter;
|
||||
__u64 enabled;
|
||||
__u64 running;
|
||||
};
|
||||
|
||||
#define BPF_DEVCG_ACC_MKNOD (1ULL << 0)
|
||||
#define BPF_DEVCG_ACC_READ (1ULL << 1)
|
||||
#define BPF_DEVCG_ACC_WRITE (1ULL << 2)
|
||||
|
||||
#define BPF_DEVCG_DEV_BLOCK (1ULL << 0)
|
||||
#define BPF_DEVCG_DEV_CHAR (1ULL << 1)
|
||||
|
||||
struct bpf_cgroup_dev_ctx {
|
||||
__u32 access_type; /* (access << 16) | type */
|
||||
__u32 major;
|
||||
__u32 minor;
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BPF_H__ */
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ Three different ways of output formatting are available:
|
|||
|
||||
The data is sampled from the KVM's debugfs entries and its perf events.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import curses
|
||||
import sys
|
||||
import locale
|
||||
import os
|
||||
import time
|
||||
import optparse
|
||||
|
|
@ -225,6 +227,8 @@ IOCTL_NUMBERS = {
|
|||
'RESET': 0x00002403,
|
||||
}
|
||||
|
||||
ENCODING = locale.getpreferredencoding(False)
|
||||
|
||||
|
||||
class Arch(object):
|
||||
"""Encapsulates global architecture specific data.
|
||||
|
|
@ -666,7 +670,7 @@ class TracepointProvider(Provider):
|
|||
"""Returns 'event name: current value' for all enabled events."""
|
||||
ret = defaultdict(int)
|
||||
for group in self.group_leaders:
|
||||
for name, val in group.read().iteritems():
|
||||
for name, val in group.read().items():
|
||||
if name in self._fields:
|
||||
ret[name] += val
|
||||
return ret
|
||||
|
|
@ -955,7 +959,7 @@ class Tui(object):
|
|||
except:
|
||||
raise Exception
|
||||
for line in child.stdout:
|
||||
line = line.lstrip().split(' ', 1)
|
||||
line = line.decode(ENCODING).lstrip().split(' ', 1)
|
||||
# perform a sanity check before calling the more expensive
|
||||
# function to possibly extract the guest name
|
||||
if ' -name ' in line[1]:
|
||||
|
|
@ -1005,7 +1009,7 @@ class Tui(object):
|
|||
name = ''
|
||||
try:
|
||||
line = open('/proc/{}/cmdline'
|
||||
.format(pid), 'rb').read().split('\0')
|
||||
.format(pid), 'r').read().split('\0')
|
||||
parms = line[line.index('-name') + 1].split(',')
|
||||
while '' in parms:
|
||||
# commas are escaped (i.e. ',,'), hence e.g. 'foo,bar' results
|
||||
|
|
@ -1170,7 +1174,7 @@ class Tui(object):
|
|||
.format(self.stats.fields_filter))
|
||||
self.screen.addstr(3, 0, "New regex: ")
|
||||
curses.echo()
|
||||
regex = self.screen.getstr()
|
||||
regex = self.screen.getstr().decode(ENCODING)
|
||||
curses.noecho()
|
||||
if len(regex) == 0:
|
||||
self.stats.fields_filter = DEFAULT_REGEX
|
||||
|
|
@ -1204,7 +1208,7 @@ class Tui(object):
|
|||
|
||||
curses.echo()
|
||||
self.screen.addstr(3, 0, "Pid [0 or pid]: ")
|
||||
pid = self.screen.getstr()
|
||||
pid = self.screen.getstr().decode(ENCODING)
|
||||
curses.noecho()
|
||||
|
||||
try:
|
||||
|
|
@ -1233,7 +1237,7 @@ class Tui(object):
|
|||
self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
|
||||
self._delay_regular)
|
||||
curses.echo()
|
||||
val = self.screen.getstr()
|
||||
val = self.screen.getstr().decode(ENCODING)
|
||||
curses.noecho()
|
||||
|
||||
try:
|
||||
|
|
@ -1273,7 +1277,7 @@ class Tui(object):
|
|||
self.print_all_gnames(7)
|
||||
curses.echo()
|
||||
self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
|
||||
gname = self.screen.getstr()
|
||||
gname = self.screen.getstr().decode(ENCODING)
|
||||
curses.noecho()
|
||||
|
||||
if not gname:
|
||||
|
|
@ -1369,25 +1373,25 @@ def batch(stats):
|
|||
s = stats.get()
|
||||
for key in sorted(s.keys()):
|
||||
values = s[key]
|
||||
print '%-42s%10d%10d' % (key, values[0], values[1])
|
||||
print('%-42s%10d%10d' % (key, values[0], values[1]))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
||||
def log(stats):
|
||||
"""Prints statistics as reiterating key block, multiple value blocks."""
|
||||
keys = sorted(stats.get().iterkeys())
|
||||
keys = sorted(stats.get().keys())
|
||||
|
||||
def banner():
|
||||
for k in keys:
|
||||
print '%s' % k,
|
||||
print
|
||||
print(k, end=' ')
|
||||
print()
|
||||
|
||||
def statline():
|
||||
s = stats.get()
|
||||
for k in keys:
|
||||
print ' %9d' % s[k][1],
|
||||
print
|
||||
print(' %9d' % s[k][1], end=' ')
|
||||
print()
|
||||
line = 0
|
||||
banner_repeat = 20
|
||||
while True:
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
static inline __u64 ptr_to_u64(const void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
|
|
@ -57,10 +59,11 @@ static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
|||
return syscall(__NR_bpf, cmd, attr, size);
|
||||
}
|
||||
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, int key_size,
|
||||
int value_size, int max_entries, __u32 map_flags,
|
||||
int node)
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node)
|
||||
{
|
||||
__u32 name_len = name ? strlen(name) : 0;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
|
@ -70,6 +73,8 @@ int bpf_create_map_node(enum bpf_map_type map_type, int key_size,
|
|||
attr.value_size = value_size;
|
||||
attr.max_entries = max_entries;
|
||||
attr.map_flags = map_flags;
|
||||
memcpy(attr.map_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
|
||||
if (node >= 0) {
|
||||
attr.map_flags |= BPF_F_NUMA_NODE;
|
||||
attr.numa_node = node;
|
||||
|
|
@ -81,14 +86,23 @@ int bpf_create_map_node(enum bpf_map_type map_type, int key_size,
|
|||
int bpf_create_map(enum bpf_map_type map_type, int key_size,
|
||||
int value_size, int max_entries, __u32 map_flags)
|
||||
{
|
||||
return bpf_create_map_node(map_type, key_size, value_size,
|
||||
return bpf_create_map_node(map_type, NULL, key_size, value_size,
|
||||
max_entries, map_flags, -1);
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, int key_size,
|
||||
int inner_map_fd, int max_entries,
|
||||
int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags)
|
||||
{
|
||||
return bpf_create_map_node(map_type, name, key_size, value_size,
|
||||
max_entries, map_flags, -1);
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags, int node)
|
||||
{
|
||||
__u32 name_len = name ? strlen(name) : 0;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
|
@ -99,6 +113,8 @@ int bpf_create_map_in_map_node(enum bpf_map_type map_type, int key_size,
|
|||
attr.inner_map_fd = inner_map_fd;
|
||||
attr.max_entries = max_entries;
|
||||
attr.map_flags = map_flags;
|
||||
memcpy(attr.map_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
|
||||
if (node >= 0) {
|
||||
attr.map_flags |= BPF_F_NUMA_NODE;
|
||||
attr.numa_node = node;
|
||||
|
|
@ -107,19 +123,24 @@ int bpf_create_map_in_map_node(enum bpf_map_type map_type, int key_size,
|
|||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
|
||||
int inner_map_fd, int max_entries, __u32 map_flags)
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags)
|
||||
{
|
||||
return bpf_create_map_in_map_node(map_type, key_size, inner_map_fd,
|
||||
max_entries, map_flags, -1);
|
||||
return bpf_create_map_in_map_node(map_type, name, key_size,
|
||||
inner_map_fd, max_entries, map_flags,
|
||||
-1);
|
||||
}
|
||||
|
||||
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, const char *license,
|
||||
__u32 kern_version, char *log_buf, size_t log_buf_sz)
|
||||
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,
|
||||
__u32 kern_version, char *log_buf,
|
||||
size_t log_buf_sz)
|
||||
{
|
||||
int fd;
|
||||
union bpf_attr attr;
|
||||
__u32 name_len = name ? strlen(name) : 0;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_type = type;
|
||||
|
|
@ -130,6 +151,7 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
|||
attr.log_size = 0;
|
||||
attr.log_level = 0;
|
||||
attr.kern_version = kern_version;
|
||||
memcpy(attr.prog_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
|
||||
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
if (fd >= 0 || !log_buf || !log_buf_sz)
|
||||
|
|
@ -143,6 +165,15 @@ int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
|||
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, const char *license,
|
||||
__u32 kern_version, char *log_buf,
|
||||
size_t log_buf_sz)
|
||||
{
|
||||
return bpf_load_program_name(type, NULL, insns, insns_cnt, license,
|
||||
kern_version, log_buf, log_buf_sz);
|
||||
}
|
||||
|
||||
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, int strict_alignment,
|
||||
const char *license, __u32 kern_version,
|
||||
|
|
@ -260,6 +291,38 @@ int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
|
|||
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
attr.attach_bpf_fd = prog_fd;
|
||||
attr.attach_type = type;
|
||||
|
||||
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
__u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.query.target_fd = target_fd;
|
||||
attr.query.attach_type = type;
|
||||
attr.query.query_flags = query_flags;
|
||||
attr.query.prog_cnt = *prog_cnt;
|
||||
attr.query.prog_ids = ptr_to_u64(prog_ids);
|
||||
|
||||
ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
|
||||
if (attach_flags)
|
||||
*attach_flags = attr.query.attach_flags;
|
||||
*prog_cnt = attr.query.prog_cnt;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
||||
void *data_out, __u32 *size_out, __u32 *retval,
|
||||
__u32 *duration)
|
||||
|
|
|
|||
|
|
@ -24,19 +24,28 @@
|
|||
#include <linux/bpf.h>
|
||||
#include <stddef.h>
|
||||
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, int key_size,
|
||||
int value_size, int max_entries, __u32 map_flags,
|
||||
int node);
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node);
|
||||
int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags);
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries, __u32 map_flags);
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, int key_size,
|
||||
int inner_map_fd, int max_entries,
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags, int node);
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
|
||||
int inner_map_fd, int max_entries, __u32 map_flags);
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags);
|
||||
|
||||
/* Recommend log buffer size */
|
||||
#define BPF_LOG_BUF_SIZE 65536
|
||||
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,
|
||||
__u32 kern_version, char *log_buf,
|
||||
size_t log_buf_sz);
|
||||
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, const char *license,
|
||||
__u32 kern_version, char *log_buf,
|
||||
|
|
@ -57,6 +66,7 @@ int bpf_obj_get(const char *pathname);
|
|||
int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type,
|
||||
unsigned int flags);
|
||||
int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
|
||||
int bpf_prog_detach2(int prog_fd, int attachable_fd, enum bpf_attach_type type);
|
||||
int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
||||
void *data_out, __u32 *size_out, __u32 *retval,
|
||||
__u32 *duration);
|
||||
|
|
@ -65,5 +75,6 @@ int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
|
|||
int bpf_prog_get_fd_by_id(__u32 id);
|
||||
int bpf_map_get_fd_by_id(__u32 id);
|
||||
int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
|
||||
|
||||
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
__u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ int libbpf_strerror(int err, char *buf, size_t size)
|
|||
struct bpf_program {
|
||||
/* Index in elf obj file, for relocation use. */
|
||||
int idx;
|
||||
char *name;
|
||||
char *section_name;
|
||||
struct bpf_insn *insns;
|
||||
size_t insns_cnt;
|
||||
|
|
@ -283,6 +284,7 @@ static void bpf_program__exit(struct bpf_program *prog)
|
|||
prog->clear_priv = NULL;
|
||||
|
||||
bpf_program__unload(prog);
|
||||
zfree(&prog->name);
|
||||
zfree(&prog->section_name);
|
||||
zfree(&prog->insns);
|
||||
zfree(&prog->reloc_desc);
|
||||
|
|
@ -293,26 +295,27 @@ static void bpf_program__exit(struct bpf_program *prog)
|
|||
}
|
||||
|
||||
static int
|
||||
bpf_program__init(void *data, size_t size, char *name, int idx,
|
||||
struct bpf_program *prog)
|
||||
bpf_program__init(void *data, size_t size, char *section_name, int idx,
|
||||
struct bpf_program *prog)
|
||||
{
|
||||
if (size < sizeof(struct bpf_insn)) {
|
||||
pr_warning("corrupted section '%s'\n", name);
|
||||
pr_warning("corrupted section '%s'\n", section_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bzero(prog, sizeof(*prog));
|
||||
|
||||
prog->section_name = strdup(name);
|
||||
prog->section_name = strdup(section_name);
|
||||
if (!prog->section_name) {
|
||||
pr_warning("failed to alloc name for prog %s\n",
|
||||
name);
|
||||
pr_warning("failed to alloc name for prog under section %s\n",
|
||||
section_name);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
prog->insns = malloc(size);
|
||||
if (!prog->insns) {
|
||||
pr_warning("failed to alloc insns for %s\n", name);
|
||||
pr_warning("failed to alloc insns for prog under section %s\n",
|
||||
section_name);
|
||||
goto errout;
|
||||
}
|
||||
prog->insns_cnt = size / sizeof(struct bpf_insn);
|
||||
|
|
@ -331,12 +334,12 @@ errout:
|
|||
|
||||
static int
|
||||
bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
|
||||
char *name, int idx)
|
||||
char *section_name, int idx)
|
||||
{
|
||||
struct bpf_program prog, *progs;
|
||||
int nr_progs, err;
|
||||
|
||||
err = bpf_program__init(data, size, name, idx, &prog);
|
||||
err = bpf_program__init(data, size, section_name, idx, &prog);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -350,8 +353,8 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
|
|||
* is still valid, so don't need special treat for
|
||||
* bpf_close_object().
|
||||
*/
|
||||
pr_warning("failed to alloc a new program '%s'\n",
|
||||
name);
|
||||
pr_warning("failed to alloc a new program under section '%s'\n",
|
||||
section_name);
|
||||
bpf_program__exit(&prog);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -364,6 +367,54 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_object__init_prog_names(struct bpf_object *obj)
|
||||
{
|
||||
Elf_Data *symbols = obj->efile.symbols;
|
||||
struct bpf_program *prog;
|
||||
size_t pi, si;
|
||||
|
||||
for (pi = 0; pi < obj->nr_programs; pi++) {
|
||||
char *name = NULL;
|
||||
|
||||
prog = &obj->programs[pi];
|
||||
|
||||
for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name;
|
||||
si++) {
|
||||
GElf_Sym sym;
|
||||
|
||||
if (!gelf_getsym(symbols, si, &sym))
|
||||
continue;
|
||||
if (sym.st_shndx != prog->idx)
|
||||
continue;
|
||||
|
||||
name = elf_strptr(obj->efile.elf,
|
||||
obj->efile.strtabidx,
|
||||
sym.st_name);
|
||||
if (!name) {
|
||||
pr_warning("failed to get sym name string for prog %s\n",
|
||||
prog->section_name);
|
||||
return -LIBBPF_ERRNO__LIBELF;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
pr_warning("failed to find sym for prog %s\n",
|
||||
prog->section_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prog->name = strdup(name);
|
||||
if (!prog->name) {
|
||||
pr_warning("failed to allocate memory for prog sym %s\n",
|
||||
name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bpf_object *bpf_object__new(const char *path,
|
||||
void *obj_buf,
|
||||
size_t obj_buf_sz)
|
||||
|
|
@ -528,31 +579,6 @@ bpf_object__init_kversion(struct bpf_object *obj,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_object__validate_maps(struct bpf_object *obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If there's only 1 map, the only error case should have been
|
||||
* catched in bpf_object__init_maps().
|
||||
*/
|
||||
if (!obj->maps || !obj->nr_maps || (obj->nr_maps == 1))
|
||||
return 0;
|
||||
|
||||
for (i = 1; i < obj->nr_maps; i++) {
|
||||
const struct bpf_map *a = &obj->maps[i - 1];
|
||||
const struct bpf_map *b = &obj->maps[i];
|
||||
|
||||
if (b->offset - a->offset < sizeof(struct bpf_map_def)) {
|
||||
pr_warning("corrupted map section in %s: map \"%s\" too small\n",
|
||||
obj->path, a->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compare_bpf_map(const void *_a, const void *_b)
|
||||
{
|
||||
const struct bpf_map *a = _a;
|
||||
|
|
@ -564,7 +590,7 @@ static int compare_bpf_map(const void *_a, const void *_b)
|
|||
static int
|
||||
bpf_object__init_maps(struct bpf_object *obj)
|
||||
{
|
||||
int i, map_idx, nr_maps = 0;
|
||||
int i, map_idx, map_def_sz, nr_maps = 0;
|
||||
Elf_Scn *scn;
|
||||
Elf_Data *data;
|
||||
Elf_Data *symbols = obj->efile.symbols;
|
||||
|
|
@ -607,6 +633,15 @@ bpf_object__init_maps(struct bpf_object *obj)
|
|||
if (!nr_maps)
|
||||
return 0;
|
||||
|
||||
/* Assume equally sized map definitions */
|
||||
map_def_sz = data->d_size / nr_maps;
|
||||
if (!data->d_size || (data->d_size % nr_maps) != 0) {
|
||||
pr_warning("unable to determine map definition size "
|
||||
"section %s, %d maps in %zd bytes\n",
|
||||
obj->path, nr_maps, data->d_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
|
||||
if (!obj->maps) {
|
||||
pr_warning("alloc maps for object failed\n");
|
||||
|
|
@ -639,7 +674,7 @@ bpf_object__init_maps(struct bpf_object *obj)
|
|||
obj->efile.strtabidx,
|
||||
sym.st_name);
|
||||
obj->maps[map_idx].offset = sym.st_value;
|
||||
if (sym.st_value + sizeof(struct bpf_map_def) > data->d_size) {
|
||||
if (sym.st_value + map_def_sz > data->d_size) {
|
||||
pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
|
||||
obj->path, map_name);
|
||||
return -EINVAL;
|
||||
|
|
@ -653,12 +688,40 @@ bpf_object__init_maps(struct bpf_object *obj)
|
|||
pr_debug("map %d is \"%s\"\n", map_idx,
|
||||
obj->maps[map_idx].name);
|
||||
def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
|
||||
obj->maps[map_idx].def = *def;
|
||||
/*
|
||||
* If the definition of the map in the object file fits in
|
||||
* bpf_map_def, copy it. Any extra fields in our version
|
||||
* of bpf_map_def will default to zero as a result of the
|
||||
* calloc above.
|
||||
*/
|
||||
if (map_def_sz <= sizeof(struct bpf_map_def)) {
|
||||
memcpy(&obj->maps[map_idx].def, def, map_def_sz);
|
||||
} else {
|
||||
/*
|
||||
* Here the map structure being read is bigger than what
|
||||
* we expect, truncate if the excess bits are all zero.
|
||||
* If they are not zero, reject this map as
|
||||
* incompatible.
|
||||
*/
|
||||
char *b;
|
||||
for (b = ((char *)def) + sizeof(struct bpf_map_def);
|
||||
b < ((char *)def) + map_def_sz; b++) {
|
||||
if (*b != 0) {
|
||||
pr_warning("maps section in %s: \"%s\" "
|
||||
"has unrecognized, non-zero "
|
||||
"options\n",
|
||||
obj->path, map_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
memcpy(&obj->maps[map_idx].def, def,
|
||||
sizeof(struct bpf_map_def));
|
||||
}
|
||||
map_idx++;
|
||||
}
|
||||
|
||||
qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map);
|
||||
return bpf_object__validate_maps(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpf_object__elf_collect(struct bpf_object *obj)
|
||||
|
|
@ -766,8 +829,12 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
|
|||
pr_warning("Corrupted ELF file: index of strtab invalid\n");
|
||||
return LIBBPF_ERRNO__FORMAT;
|
||||
}
|
||||
if (obj->efile.maps_shndx >= 0)
|
||||
if (obj->efile.maps_shndx >= 0) {
|
||||
err = bpf_object__init_maps(obj);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = bpf_object__init_prog_names(obj);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
|
@ -870,11 +937,12 @@ bpf_object__create_maps(struct bpf_object *obj)
|
|||
struct bpf_map_def *def = &obj->maps[i].def;
|
||||
int *pfd = &obj->maps[i].fd;
|
||||
|
||||
*pfd = bpf_create_map(def->type,
|
||||
def->key_size,
|
||||
def->value_size,
|
||||
def->max_entries,
|
||||
0);
|
||||
*pfd = bpf_create_map_name(def->type,
|
||||
obj->maps[i].name,
|
||||
def->key_size,
|
||||
def->value_size,
|
||||
def->max_entries,
|
||||
def->map_flags);
|
||||
if (*pfd < 0) {
|
||||
size_t j;
|
||||
int err = *pfd;
|
||||
|
|
@ -982,7 +1050,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
|
|||
}
|
||||
|
||||
static int
|
||||
load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
||||
load_program(enum bpf_prog_type type, const char *name, struct bpf_insn *insns,
|
||||
int insns_cnt, char *license, u32 kern_version, int *pfd)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -995,8 +1063,8 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
|||
if (!log_buf)
|
||||
pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
|
||||
|
||||
ret = bpf_load_program(type, insns, insns_cnt, license,
|
||||
kern_version, log_buf, BPF_LOG_BUF_SIZE);
|
||||
ret = bpf_load_program_name(type, name, insns, insns_cnt, license,
|
||||
kern_version, log_buf, BPF_LOG_BUF_SIZE);
|
||||
|
||||
if (ret >= 0) {
|
||||
*pfd = ret;
|
||||
|
|
@ -1021,9 +1089,9 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
|||
if (type != BPF_PROG_TYPE_KPROBE) {
|
||||
int fd;
|
||||
|
||||
fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
|
||||
insns_cnt, license, kern_version,
|
||||
NULL, 0);
|
||||
fd = bpf_load_program_name(BPF_PROG_TYPE_KPROBE, name,
|
||||
insns, insns_cnt, license,
|
||||
kern_version, NULL, 0);
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
ret = -LIBBPF_ERRNO__PROGTYPE;
|
||||
|
|
@ -1067,8 +1135,8 @@ bpf_program__load(struct bpf_program *prog,
|
|||
pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
|
||||
prog->section_name, prog->instances.nr);
|
||||
}
|
||||
err = load_program(prog->type, prog->insns, prog->insns_cnt,
|
||||
license, kern_version, &fd);
|
||||
err = load_program(prog->type, prog->name, prog->insns,
|
||||
prog->insns_cnt, license, kern_version, &fd);
|
||||
if (!err)
|
||||
prog->instances.fds[0] = fd;
|
||||
goto out;
|
||||
|
|
@ -1096,7 +1164,8 @@ bpf_program__load(struct bpf_program *prog,
|
|||
continue;
|
||||
}
|
||||
|
||||
err = load_program(prog->type, result.new_insn_ptr,
|
||||
err = load_program(prog->type, prog->name,
|
||||
result.new_insn_ptr,
|
||||
result.new_insn_cnt,
|
||||
license, kern_version, &fd);
|
||||
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ struct bpf_map_def {
|
|||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -436,13 +436,13 @@ create_arg_exp(enum filter_exp_type etype)
|
|||
return NULL;
|
||||
|
||||
arg->type = FILTER_ARG_EXP;
|
||||
arg->op.type = etype;
|
||||
arg->exp.type = etype;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
static struct filter_arg *
|
||||
create_arg_cmp(enum filter_exp_type etype)
|
||||
create_arg_cmp(enum filter_cmp_type ctype)
|
||||
{
|
||||
struct filter_arg *arg;
|
||||
|
||||
|
|
@ -452,7 +452,7 @@ create_arg_cmp(enum filter_exp_type etype)
|
|||
|
||||
/* Use NUM and change if necessary */
|
||||
arg->type = FILTER_ARG_NUM;
|
||||
arg->op.type = etype;
|
||||
arg->num.type = ctype;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
|
|
|||
2
tools/objtool/.gitignore
vendored
2
tools/objtool/.gitignore
vendored
|
|
@ -1,3 +1,3 @@
|
|||
arch/x86/insn/inat-tables.c
|
||||
arch/x86/lib/inat-tables.c
|
||||
objtool
|
||||
fixdep
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
|
|||
|
||||
all: $(OBJTOOL)
|
||||
|
||||
INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
|
||||
INCLUDES := -I$(srctree)/tools/include \
|
||||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||
-I$(srctree)/tools/objtool/arch/$(ARCH)/include
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
|
||||
CFLAGS += -Wall -Werror $(WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
|
||||
LDFLAGS += -lelf $(LIBSUBCMD)
|
||||
|
|
@ -41,22 +43,8 @@ include $(srctree)/tools/build/Makefile.include
|
|||
$(OBJTOOL_IN): fixdep FORCE
|
||||
@$(MAKE) $(build)=objtool
|
||||
|
||||
# Busybox's diff doesn't have -I, avoid warning in that case
|
||||
#
|
||||
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
|
||||
@(diff -I 2>&1 | grep -q 'option requires an argument' && \
|
||||
test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
|
||||
diff -I'^#include' arch/x86/insn/insn.c ../../arch/x86/lib/insn.c >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/inat.c ../../arch/x86/lib/inat.c >/dev/null && \
|
||||
diff arch/x86/insn/x86-opcode-map.txt ../../arch/x86/lib/x86-opcode-map.txt >/dev/null && \
|
||||
diff arch/x86/insn/gen-insn-attr-x86.awk ../../arch/x86/tools/gen-insn-attr-x86.awk >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/insn.h ../../arch/x86/include/asm/insn.h >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
|
||||
|| echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true
|
||||
@(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
|
||||
diff ../../arch/x86/include/asm/orc_types.h orc_types.h >/dev/null) \
|
||||
|| echo "warning: objtool: orc_types.h differs from kernel" >&2 )) || true
|
||||
@./sync-check.sh
|
||||
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
|
||||
|
||||
|
||||
|
|
@ -66,7 +54,7 @@ $(LIBSUBCMD): fixdep FORCE
|
|||
clean:
|
||||
$(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
|
||||
$(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT)arch/x86/insn/inat-tables.c $(OUTPUT)fixdep
|
||||
$(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
|
||||
|
||||
FORCE:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
objtool-y += decode.o
|
||||
|
||||
inat_tables_script = arch/x86/insn/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = arch/x86/insn/x86-opcode-map.txt
|
||||
inat_tables_script = arch/x86/tools/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = arch/x86/lib/x86-opcode-map.txt
|
||||
|
||||
$(OUTPUT)arch/x86/insn/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
|
||||
$(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,gen)$(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@
|
||||
|
||||
$(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/insn/inat-tables.c
|
||||
$(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
|
||||
|
||||
CFLAGS_decode.o += -I$(OUTPUT)arch/x86/insn
|
||||
CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#define unlikely(cond) (cond)
|
||||
#include "insn/insn.h"
|
||||
#include "insn/inat.c"
|
||||
#include "insn/insn.c"
|
||||
#include <asm/insn.h>
|
||||
#include "lib/inat.c"
|
||||
#include "lib/insn.c"
|
||||
|
||||
#include "../../elf.h"
|
||||
#include "../../arch.h"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include "inat_types.h"
|
||||
#include <asm/inat_types.h>
|
||||
|
||||
/*
|
||||
* Internal bits. Don't use bitmasks directly, because these bits are
|
||||
|
|
@ -97,6 +97,16 @@
|
|||
#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM)
|
||||
#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS)
|
||||
|
||||
/* Identifiers for segment registers */
|
||||
#define INAT_SEG_REG_IGNORE 0
|
||||
#define INAT_SEG_REG_DEFAULT 1
|
||||
#define INAT_SEG_REG_CS 2
|
||||
#define INAT_SEG_REG_SS 3
|
||||
#define INAT_SEG_REG_DS 4
|
||||
#define INAT_SEG_REG_ES 5
|
||||
#define INAT_SEG_REG_FS 6
|
||||
#define INAT_SEG_REG_GS 7
|
||||
|
||||
/* Attribute search APIs */
|
||||
extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode);
|
||||
extern int inat_get_last_prefix_id(insn_byte_t last_pfx);
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
|
||||
/* insn_attr_t is defined in inat.h */
|
||||
#include "inat.h"
|
||||
#include <asm/inat.h>
|
||||
|
||||
struct insn_field {
|
||||
union {
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include "insn.h"
|
||||
#include <asm/insn.h>
|
||||
|
||||
/* Attribute tables are generated from opcode map */
|
||||
#include "inat-tables.c"
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
#else
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include "inat.h"
|
||||
#include "insn.h"
|
||||
#include <asm/inat.h>
|
||||
#include <asm/insn.h>
|
||||
|
||||
/* Verify next sizeof(t) bytes can be on the same instruction */
|
||||
#define validate_next(t, insn, n) \
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef _ORC_H
|
||||
#define _ORC_H
|
||||
|
||||
#include "orc_types.h"
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
struct objtool_file;
|
||||
|
||||
|
|
|
|||
29
tools/objtool/sync-check.sh
Executable file
29
tools/objtool/sync-check.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
FILES='
|
||||
arch/x86/lib/insn.c
|
||||
arch/x86/lib/inat.c
|
||||
arch/x86/lib/x86-opcode-map.txt
|
||||
arch/x86/tools/gen-insn-attr-x86.awk
|
||||
arch/x86/include/asm/insn.h
|
||||
arch/x86/include/asm/inat.h
|
||||
arch/x86/include/asm/inat_types.h
|
||||
arch/x86/include/asm/orc_types.h
|
||||
'
|
||||
|
||||
check()
|
||||
{
|
||||
local file=$1
|
||||
|
||||
diff $file ../../$file > /dev/null ||
|
||||
echo "Warning: synced file at 'tools/objtool/$file' differs from latest kernel version at '$file'"
|
||||
}
|
||||
|
||||
if [ ! -d ../../kernel ] || [ ! -d ../../tools ] || [ ! -d ../objtool ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for i in $FILES; do
|
||||
check $i
|
||||
done
|
||||
|
|
@ -53,6 +53,10 @@ ifeq ($(SRCARCH),arm64)
|
|||
LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),s390)
|
||||
NO_PERF_REGS := 0
|
||||
endif
|
||||
|
||||
ifeq ($(NO_PERF_REGS),0)
|
||||
$(call detected,CONFIG_PERF_REGS)
|
||||
endif
|
||||
|
|
@ -61,7 +65,7 @@ endif
|
|||
# Disable it on all other architectures in case libdw unwind
|
||||
# support is detected in system. Add supported architectures
|
||||
# to the check.
|
||||
ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm powerpc))
|
||||
ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm powerpc s390))
|
||||
NO_LIBDW_DWARF_UNWIND := 1
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,72 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef S390_DWARF_REGS_TABLE_H
|
||||
#define S390_DWARF_REGS_TABLE_H
|
||||
|
||||
#define REG_DWARFNUM_NAME(reg, idx) [idx] = "%" #reg
|
||||
|
||||
/*
|
||||
* For reference, see DWARF register mapping:
|
||||
* http://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_s390/x1542.html
|
||||
*/
|
||||
static const char * const s390_dwarf_regs[] = {
|
||||
"%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
|
||||
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
|
||||
REG_DWARFNUM_NAME(f0, 16),
|
||||
REG_DWARFNUM_NAME(f1, 20),
|
||||
REG_DWARFNUM_NAME(f2, 17),
|
||||
REG_DWARFNUM_NAME(f3, 21),
|
||||
REG_DWARFNUM_NAME(f4, 18),
|
||||
REG_DWARFNUM_NAME(f5, 22),
|
||||
REG_DWARFNUM_NAME(f6, 19),
|
||||
REG_DWARFNUM_NAME(f7, 23),
|
||||
REG_DWARFNUM_NAME(f8, 24),
|
||||
REG_DWARFNUM_NAME(f9, 28),
|
||||
REG_DWARFNUM_NAME(f10, 25),
|
||||
REG_DWARFNUM_NAME(f11, 29),
|
||||
REG_DWARFNUM_NAME(f12, 26),
|
||||
REG_DWARFNUM_NAME(f13, 30),
|
||||
REG_DWARFNUM_NAME(f14, 27),
|
||||
REG_DWARFNUM_NAME(f15, 31),
|
||||
REG_DWARFNUM_NAME(c0, 32),
|
||||
REG_DWARFNUM_NAME(c1, 33),
|
||||
REG_DWARFNUM_NAME(c2, 34),
|
||||
REG_DWARFNUM_NAME(c3, 35),
|
||||
REG_DWARFNUM_NAME(c4, 36),
|
||||
REG_DWARFNUM_NAME(c5, 37),
|
||||
REG_DWARFNUM_NAME(c6, 38),
|
||||
REG_DWARFNUM_NAME(c7, 39),
|
||||
REG_DWARFNUM_NAME(c8, 40),
|
||||
REG_DWARFNUM_NAME(c9, 41),
|
||||
REG_DWARFNUM_NAME(c10, 42),
|
||||
REG_DWARFNUM_NAME(c11, 43),
|
||||
REG_DWARFNUM_NAME(c12, 44),
|
||||
REG_DWARFNUM_NAME(c13, 45),
|
||||
REG_DWARFNUM_NAME(c14, 46),
|
||||
REG_DWARFNUM_NAME(c15, 47),
|
||||
REG_DWARFNUM_NAME(a0, 48),
|
||||
REG_DWARFNUM_NAME(a1, 49),
|
||||
REG_DWARFNUM_NAME(a2, 50),
|
||||
REG_DWARFNUM_NAME(a3, 51),
|
||||
REG_DWARFNUM_NAME(a4, 52),
|
||||
REG_DWARFNUM_NAME(a5, 53),
|
||||
REG_DWARFNUM_NAME(a6, 54),
|
||||
REG_DWARFNUM_NAME(a7, 55),
|
||||
REG_DWARFNUM_NAME(a8, 56),
|
||||
REG_DWARFNUM_NAME(a9, 57),
|
||||
REG_DWARFNUM_NAME(a10, 58),
|
||||
REG_DWARFNUM_NAME(a11, 59),
|
||||
REG_DWARFNUM_NAME(a12, 60),
|
||||
REG_DWARFNUM_NAME(a13, 61),
|
||||
REG_DWARFNUM_NAME(a14, 62),
|
||||
REG_DWARFNUM_NAME(a15, 63),
|
||||
REG_DWARFNUM_NAME(pswm, 64),
|
||||
REG_DWARFNUM_NAME(pswa, 65),
|
||||
};
|
||||
|
||||
#ifdef DEFINE_DWARF_REGSTR_TABLE
|
||||
/* This is included in perf/util/dwarf-regs.c */
|
||||
|
||||
static const char * const s390_regstr_tbl[] = {
|
||||
"%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
|
||||
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
|
||||
};
|
||||
#endif
|
||||
#define s390_regstr_tbl s390_dwarf_regs
|
||||
|
||||
#endif /* DEFINE_DWARF_REGSTR_TABLE */
|
||||
#endif /* S390_DWARF_REGS_TABLE_H */
|
||||
|
|
|
|||
95
tools/perf/arch/s390/include/perf_regs.h
Normal file
95
tools/perf/arch/s390/include/perf_regs.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#ifndef ARCH_PERF_REGS_H
|
||||
#define ARCH_PERF_REGS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <linux/types.h>
|
||||
#include <../../../../arch/s390/include/uapi/asm/perf_regs.h>
|
||||
|
||||
void perf_regs_load(u64 *regs);
|
||||
|
||||
#define PERF_REGS_MASK ((1ULL << PERF_REG_S390_MAX) - 1)
|
||||
#define PERF_REGS_MAX PERF_REG_S390_MAX
|
||||
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
|
||||
|
||||
#define PERF_REG_IP PERF_REG_S390_PC
|
||||
#define PERF_REG_SP PERF_REG_S390_R15
|
||||
|
||||
static inline const char *perf_reg_name(int id)
|
||||
{
|
||||
switch (id) {
|
||||
case PERF_REG_S390_R0:
|
||||
return "R0";
|
||||
case PERF_REG_S390_R1:
|
||||
return "R1";
|
||||
case PERF_REG_S390_R2:
|
||||
return "R2";
|
||||
case PERF_REG_S390_R3:
|
||||
return "R3";
|
||||
case PERF_REG_S390_R4:
|
||||
return "R4";
|
||||
case PERF_REG_S390_R5:
|
||||
return "R5";
|
||||
case PERF_REG_S390_R6:
|
||||
return "R6";
|
||||
case PERF_REG_S390_R7:
|
||||
return "R7";
|
||||
case PERF_REG_S390_R8:
|
||||
return "R8";
|
||||
case PERF_REG_S390_R9:
|
||||
return "R9";
|
||||
case PERF_REG_S390_R10:
|
||||
return "R10";
|
||||
case PERF_REG_S390_R11:
|
||||
return "R11";
|
||||
case PERF_REG_S390_R12:
|
||||
return "R12";
|
||||
case PERF_REG_S390_R13:
|
||||
return "R13";
|
||||
case PERF_REG_S390_R14:
|
||||
return "R14";
|
||||
case PERF_REG_S390_R15:
|
||||
return "R15";
|
||||
case PERF_REG_S390_FP0:
|
||||
return "FP0";
|
||||
case PERF_REG_S390_FP1:
|
||||
return "FP1";
|
||||
case PERF_REG_S390_FP2:
|
||||
return "FP2";
|
||||
case PERF_REG_S390_FP3:
|
||||
return "FP3";
|
||||
case PERF_REG_S390_FP4:
|
||||
return "FP4";
|
||||
case PERF_REG_S390_FP5:
|
||||
return "FP5";
|
||||
case PERF_REG_S390_FP6:
|
||||
return "FP6";
|
||||
case PERF_REG_S390_FP7:
|
||||
return "FP7";
|
||||
case PERF_REG_S390_FP8:
|
||||
return "FP8";
|
||||
case PERF_REG_S390_FP9:
|
||||
return "FP9";
|
||||
case PERF_REG_S390_FP10:
|
||||
return "FP10";
|
||||
case PERF_REG_S390_FP11:
|
||||
return "FP11";
|
||||
case PERF_REG_S390_FP12:
|
||||
return "FP12";
|
||||
case PERF_REG_S390_FP13:
|
||||
return "FP13";
|
||||
case PERF_REG_S390_FP14:
|
||||
return "FP14";
|
||||
case PERF_REG_S390_FP15:
|
||||
return "FP15";
|
||||
case PERF_REG_S390_MASK:
|
||||
return "MASK";
|
||||
case PERF_REG_S390_PC:
|
||||
return "PC";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* ARCH_PERF_REGS_H */
|
||||
|
|
@ -2,5 +2,8 @@ libperf-y += header.o
|
|||
libperf-y += kvm-stat.o
|
||||
|
||||
libperf-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
|
||||
|
||||
libperf-y += machine.o
|
||||
|
||||
libperf-$(CONFIG_AUXTRACE) += auxtrace.o
|
||||
|
|
|
|||
118
tools/perf/arch/s390/util/auxtrace.c
Normal file
118
tools/perf/arch/s390/util/auxtrace.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#include <stdbool.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include "../../util/evlist.h"
|
||||
#include "../../util/auxtrace.h"
|
||||
#include "../../util/evsel.h"
|
||||
|
||||
#define PERF_EVENT_CPUM_SF 0xB0000 /* Event: Basic-sampling */
|
||||
#define PERF_EVENT_CPUM_SF_DIAG 0xBD000 /* Event: Combined-sampling */
|
||||
#define DEFAULT_AUX_PAGES 128
|
||||
#define DEFAULT_FREQ 4000
|
||||
|
||||
static void cpumsf_free(struct auxtrace_record *itr)
|
||||
{
|
||||
free(itr);
|
||||
}
|
||||
|
||||
static size_t cpumsf_info_priv_size(struct auxtrace_record *itr __maybe_unused,
|
||||
struct perf_evlist *evlist __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cpumsf_info_fill(struct auxtrace_record *itr __maybe_unused,
|
||||
struct perf_session *session __maybe_unused,
|
||||
struct auxtrace_info_event *auxtrace_info __maybe_unused,
|
||||
size_t priv_size __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
cpumsf_reference(struct auxtrace_record *itr __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cpumsf_recording_options(struct auxtrace_record *ar __maybe_unused,
|
||||
struct perf_evlist *evlist __maybe_unused,
|
||||
struct record_opts *opts)
|
||||
{
|
||||
unsigned int factor = 1;
|
||||
unsigned int pages;
|
||||
|
||||
opts->full_auxtrace = true;
|
||||
|
||||
/*
|
||||
* The AUX buffer size should be set properly to avoid
|
||||
* overflow of samples if it is not set explicitly.
|
||||
* DEFAULT_AUX_PAGES is an proper size when sampling frequency
|
||||
* is DEFAULT_FREQ. It is expected to hold about 1/2 second
|
||||
* of sampling data. The size used for AUX buffer will scale
|
||||
* according to the specified frequency and DEFAULT_FREQ.
|
||||
*/
|
||||
if (!opts->auxtrace_mmap_pages) {
|
||||
if (opts->user_freq != UINT_MAX)
|
||||
factor = (opts->user_freq + DEFAULT_FREQ
|
||||
- 1) / DEFAULT_FREQ;
|
||||
pages = DEFAULT_AUX_PAGES * factor;
|
||||
opts->auxtrace_mmap_pages = roundup_pow_of_two(pages);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cpumsf_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
|
||||
struct record_opts *opts __maybe_unused,
|
||||
const char *str __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* auxtrace_record__init is called when perf record
|
||||
* check if the event really need auxtrace
|
||||
*/
|
||||
struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
|
||||
int *err)
|
||||
{
|
||||
struct auxtrace_record *aux;
|
||||
struct perf_evsel *pos;
|
||||
int diagnose = 0;
|
||||
|
||||
if (evlist->nr_entries == 0)
|
||||
return NULL;
|
||||
|
||||
evlist__for_each_entry(evlist, pos) {
|
||||
if (pos->attr.config == PERF_EVENT_CPUM_SF_DIAG) {
|
||||
diagnose = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!diagnose)
|
||||
return NULL;
|
||||
|
||||
/* sampling in diagnose mode. alloc aux buffer */
|
||||
aux = zalloc(sizeof(*aux));
|
||||
if (aux == NULL) {
|
||||
*err = -ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
aux->parse_snapshot_options = cpumsf_parse_snapshot_options;
|
||||
aux->recording_options = cpumsf_recording_options;
|
||||
aux->info_priv_size = cpumsf_info_priv_size;
|
||||
aux->info_fill = cpumsf_info_fill;
|
||||
aux->free = cpumsf_free;
|
||||
aux->reference = cpumsf_reference;
|
||||
|
||||
return aux;
|
||||
}
|
||||
|
|
@ -9,15 +9,10 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#define NUM_GPRS 16
|
||||
|
||||
static const char *gpr_names[NUM_GPRS] = {
|
||||
"%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
|
||||
"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
|
||||
};
|
||||
#include <linux/kernel.h>
|
||||
#include "dwarf-regs-table.h"
|
||||
|
||||
const char *get_arch_regstr(unsigned int n)
|
||||
{
|
||||
return (n >= NUM_GPRS) ? NULL : gpr_names[n];
|
||||
return (n >= ARRAY_SIZE(s390_dwarf_regs)) ? NULL : s390_dwarf_regs[n];
|
||||
}
|
||||
|
|
|
|||
63
tools/perf/arch/s390/util/unwind-libdw.c
Normal file
63
tools/perf/arch/s390/util/unwind-libdw.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/event.h"
|
||||
#include "dwarf-regs-table.h"
|
||||
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
struct unwind_info *ui = arg;
|
||||
struct regs_dump *user_regs = &ui->sample->user_regs;
|
||||
Dwarf_Word dwarf_regs[ARRAY_SIZE(s390_dwarf_regs)];
|
||||
|
||||
#define REG(r) ({ \
|
||||
Dwarf_Word val = 0; \
|
||||
perf_reg_value(&val, user_regs, PERF_REG_S390_##r); \
|
||||
val; \
|
||||
})
|
||||
/*
|
||||
* For DWARF register mapping details,
|
||||
* see also perf/arch/s390/include/dwarf-regs-table.h
|
||||
*/
|
||||
dwarf_regs[0] = REG(R0);
|
||||
dwarf_regs[1] = REG(R1);
|
||||
dwarf_regs[2] = REG(R2);
|
||||
dwarf_regs[3] = REG(R3);
|
||||
dwarf_regs[4] = REG(R4);
|
||||
dwarf_regs[5] = REG(R5);
|
||||
dwarf_regs[6] = REG(R6);
|
||||
dwarf_regs[7] = REG(R7);
|
||||
dwarf_regs[8] = REG(R8);
|
||||
dwarf_regs[9] = REG(R9);
|
||||
dwarf_regs[10] = REG(R10);
|
||||
dwarf_regs[11] = REG(R11);
|
||||
dwarf_regs[12] = REG(R12);
|
||||
dwarf_regs[13] = REG(R13);
|
||||
dwarf_regs[14] = REG(R14);
|
||||
dwarf_regs[15] = REG(R15);
|
||||
|
||||
dwarf_regs[16] = REG(FP0);
|
||||
dwarf_regs[17] = REG(FP2);
|
||||
dwarf_regs[18] = REG(FP4);
|
||||
dwarf_regs[19] = REG(FP6);
|
||||
dwarf_regs[20] = REG(FP1);
|
||||
dwarf_regs[21] = REG(FP3);
|
||||
dwarf_regs[22] = REG(FP5);
|
||||
dwarf_regs[23] = REG(FP7);
|
||||
dwarf_regs[24] = REG(FP8);
|
||||
dwarf_regs[25] = REG(FP10);
|
||||
dwarf_regs[26] = REG(FP12);
|
||||
dwarf_regs[27] = REG(FP14);
|
||||
dwarf_regs[28] = REG(FP9);
|
||||
dwarf_regs[29] = REG(FP11);
|
||||
dwarf_regs[30] = REG(FP13);
|
||||
dwarf_regs[31] = REG(FP15);
|
||||
|
||||
dwarf_regs[64] = REG(MASK);
|
||||
dwarf_regs[65] = REG(PC);
|
||||
|
||||
dwfl_thread_state_register_pc(thread, dwarf_regs[65]);
|
||||
return dwfl_thread_state_registers(thread, 0, 32, dwarf_regs);
|
||||
}
|
||||
|
|
@ -641,7 +641,6 @@ static const struct {
|
|||
{ "__GFP_ATOMIC", "_A" },
|
||||
{ "__GFP_IO", "I" },
|
||||
{ "__GFP_FS", "F" },
|
||||
{ "__GFP_COLD", "CO" },
|
||||
{ "__GFP_NOWARN", "NWR" },
|
||||
{ "__GFP_RETRY_MAYFAIL", "R" },
|
||||
{ "__GFP_NOFAIL", "NF" },
|
||||
|
|
@ -655,7 +654,6 @@ static const struct {
|
|||
{ "__GFP_RECLAIMABLE", "RC" },
|
||||
{ "__GFP_MOVABLE", "M" },
|
||||
{ "__GFP_ACCOUNT", "AC" },
|
||||
{ "__GFP_NOTRACK", "NT" },
|
||||
{ "__GFP_WRITE", "WR" },
|
||||
{ "__GFP_RECLAIM", "R" },
|
||||
{ "__GFP_DIRECT_RECLAIM", "DR" },
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ TOOL_OBJS = \
|
|||
utnonansi.o\
|
||||
utprint.o\
|
||||
utstring.o\
|
||||
utstrsuppt.o\
|
||||
utstrtoul64.o\
|
||||
utxferror.o\
|
||||
oslinuxtbl.o\
|
||||
|
|
|
|||
|
|
@ -287,8 +287,7 @@ int ap_dump_table_by_address(char *ascii_address)
|
|||
|
||||
/* Convert argument to an integer physical address */
|
||||
|
||||
status = acpi_ut_strtoul64(ascii_address, ACPI_STRTOUL_64BIT,
|
||||
&long_address);
|
||||
status = acpi_ut_strtoul64(ascii_address, &long_address);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
fprintf(stderr, "%s: Could not convert to a physical address\n",
|
||||
ascii_address);
|
||||
|
|
|
|||
|
|
@ -208,9 +208,7 @@ static int ap_do_options(int argc, char **argv)
|
|||
case 'r': /* Dump tables from specified RSDP */
|
||||
|
||||
status =
|
||||
acpi_ut_strtoul64(acpi_gbl_optarg,
|
||||
ACPI_STRTOUL_64BIT,
|
||||
&gbl_rsdp_base);
|
||||
acpi_ut_strtoul64(acpi_gbl_optarg, &gbl_rsdp_base);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
fprintf(stderr,
|
||||
"%s: Could not convert to a physical address\n",
|
||||
|
|
|
|||
3
tools/power/cpupower/.gitignore
vendored
3
tools/power/cpupower/.gitignore
vendored
|
|
@ -1,7 +1,6 @@
|
|||
.libs
|
||||
libcpupower.so
|
||||
libcpupower.so.0
|
||||
libcpupower.so.0.0.0
|
||||
libcpupower.so.*
|
||||
build/ccdv
|
||||
cpufreq-info
|
||||
cpufreq-set
|
||||
|
|
|
|||
|
|
@ -26,10 +26,12 @@ endif
|
|||
|
||||
ifneq ($(OUTPUT),)
|
||||
# check that the output directory actually exists
|
||||
OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
|
||||
OUTDIR := $(shell cd $(OUTPUT) && pwd)
|
||||
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
|
||||
endif
|
||||
|
||||
include ../../scripts/Makefile.arch
|
||||
|
||||
# --- CONFIGURATION BEGIN ---
|
||||
|
||||
# Set the following to `true' to make a unstripped, unoptimized
|
||||
|
|
@ -79,7 +81,11 @@ bindir ?= /usr/bin
|
|||
sbindir ?= /usr/sbin
|
||||
mandir ?= /usr/man
|
||||
includedir ?= /usr/include
|
||||
ifeq ($(IS_64_BIT), 1)
|
||||
libdir ?= /usr/lib64
|
||||
else
|
||||
libdir ?= /usr/lib
|
||||
endif
|
||||
localedir ?= /usr/share/locale
|
||||
docdir ?= /usr/share/doc/packages/cpupower
|
||||
confdir ?= /etc/
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ int set_cpufreq_governor(char *governor, unsigned int cpu)
|
|||
|
||||
dprintf("set %s as cpufreq governor\n", governor);
|
||||
|
||||
if (cpupower_is_cpu_online(cpu) != 0) {
|
||||
if (cpupower_is_cpu_online(cpu) != 1) {
|
||||
perror("cpufreq_cpu_exists");
|
||||
fprintf(stderr, "error: cpu %u does not exist\n", cpu);
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -93,8 +93,6 @@ static void print_speed(unsigned long speed)
|
|||
if (speed > 1000000)
|
||||
printf("%u.%06u GHz", ((unsigned int) speed/1000000),
|
||||
((unsigned int) speed%1000000));
|
||||
else if (speed > 100000)
|
||||
printf("%u MHz", (unsigned int) speed);
|
||||
else if (speed > 1000)
|
||||
printf("%u.%03u MHz", ((unsigned int) speed/1000),
|
||||
(unsigned int) (speed%1000));
|
||||
|
|
|
|||
|
|
@ -130,15 +130,18 @@ static struct cpuidle_monitor *cpuidle_register(void)
|
|||
{
|
||||
int num;
|
||||
char *tmp;
|
||||
int this_cpu;
|
||||
|
||||
this_cpu = sched_getcpu();
|
||||
|
||||
/* Assume idle state count is the same for all CPUs */
|
||||
cpuidle_sysfs_monitor.hw_states_num = cpuidle_state_count(0);
|
||||
cpuidle_sysfs_monitor.hw_states_num = cpuidle_state_count(this_cpu);
|
||||
|
||||
if (cpuidle_sysfs_monitor.hw_states_num <= 0)
|
||||
return NULL;
|
||||
|
||||
for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
|
||||
tmp = cpuidle_state_name(0, num);
|
||||
tmp = cpuidle_state_name(this_cpu, num);
|
||||
if (tmp == NULL)
|
||||
continue;
|
||||
|
||||
|
|
@ -146,7 +149,7 @@ static struct cpuidle_monitor *cpuidle_register(void)
|
|||
strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
|
||||
free(tmp);
|
||||
|
||||
tmp = cpuidle_state_desc(0, num);
|
||||
tmp = cpuidle_state_desc(this_cpu, num);
|
||||
if (tmp == NULL)
|
||||
continue;
|
||||
strncpy(cpuidle_cstates[num].desc, tmp, CSTATE_DESC_LEN - 1);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ endif
|
|||
|
||||
# check that the output directory actually exists
|
||||
ifneq ($(OUTPUT),)
|
||||
OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
|
||||
OUTDIR := $(shell cd $(OUTPUT) && pwd)
|
||||
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ libnvdimm-y += $(NVDIMM_SRC)/region_devs.o
|
|||
libnvdimm-y += $(NVDIMM_SRC)/region.o
|
||||
libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o
|
||||
libnvdimm-y += $(NVDIMM_SRC)/label.o
|
||||
libnvdimm-y += $(NVDIMM_SRC)/badrange.o
|
||||
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
|
||||
|
|
|
|||
|
|
@ -168,8 +168,12 @@ struct nfit_test {
|
|||
spinlock_t lock;
|
||||
} ars_state;
|
||||
struct device *dimm_dev[NUM_DCR];
|
||||
struct badrange badrange;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *nfit_wq;
|
||||
|
||||
static struct nfit_test *to_nfit_test(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
|
@ -234,48 +238,68 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define NFIT_TEST_ARS_RECORDS 4
|
||||
#define NFIT_TEST_CLEAR_ERR_UNIT 256
|
||||
|
||||
static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
int ars_recs;
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
/* for testing, only store up to n records that fit within 4k */
|
||||
ars_recs = SZ_4K / sizeof(struct nd_ars_record);
|
||||
|
||||
nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status)
|
||||
+ NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record);
|
||||
+ ars_recs * sizeof(struct nd_ars_record);
|
||||
nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
|
||||
nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the ars_state to return an ars_result 1 second in the future with
|
||||
* a 4K error range in the middle of the requested address range.
|
||||
*/
|
||||
static void post_ars_status(struct ars_state *ars_state, u64 addr, u64 len)
|
||||
static void post_ars_status(struct ars_state *ars_state,
|
||||
struct badrange *badrange, u64 addr, u64 len)
|
||||
{
|
||||
struct nd_cmd_ars_status *ars_status;
|
||||
struct nd_ars_record *ars_record;
|
||||
struct badrange_entry *be;
|
||||
u64 end = addr + len - 1;
|
||||
int i = 0;
|
||||
|
||||
ars_state->deadline = jiffies + 1*HZ;
|
||||
ars_status = ars_state->ars_status;
|
||||
ars_status->status = 0;
|
||||
ars_status->out_length = sizeof(struct nd_cmd_ars_status)
|
||||
+ sizeof(struct nd_ars_record);
|
||||
ars_status->address = addr;
|
||||
ars_status->length = len;
|
||||
ars_status->type = ND_ARS_PERSISTENT;
|
||||
ars_status->num_records = 1;
|
||||
ars_record = &ars_status->records[0];
|
||||
ars_record->handle = 0;
|
||||
ars_record->err_address = addr + len / 2;
|
||||
ars_record->length = SZ_4K;
|
||||
|
||||
spin_lock(&badrange->lock);
|
||||
list_for_each_entry(be, &badrange->list, list) {
|
||||
u64 be_end = be->start + be->length - 1;
|
||||
u64 rstart, rend;
|
||||
|
||||
/* skip entries outside the range */
|
||||
if (be_end < addr || be->start > end)
|
||||
continue;
|
||||
|
||||
rstart = (be->start < addr) ? addr : be->start;
|
||||
rend = (be_end < end) ? be_end : end;
|
||||
ars_record = &ars_status->records[i];
|
||||
ars_record->handle = 0;
|
||||
ars_record->err_address = rstart;
|
||||
ars_record->length = rend - rstart + 1;
|
||||
i++;
|
||||
}
|
||||
spin_unlock(&badrange->lock);
|
||||
ars_status->num_records = i;
|
||||
ars_status->out_length = sizeof(struct nd_cmd_ars_status)
|
||||
+ i * sizeof(struct nd_ars_record);
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_ars_start(struct ars_state *ars_state,
|
||||
static int nfit_test_cmd_ars_start(struct nfit_test *t,
|
||||
struct ars_state *ars_state,
|
||||
struct nd_cmd_ars_start *ars_start, unsigned int buf_len,
|
||||
int *cmd_rc)
|
||||
{
|
||||
|
|
@ -289,7 +313,7 @@ static int nfit_test_cmd_ars_start(struct ars_state *ars_state,
|
|||
} else {
|
||||
ars_start->status = 0;
|
||||
ars_start->scrub_time = 1;
|
||||
post_ars_status(ars_state, ars_start->address,
|
||||
post_ars_status(ars_state, &t->badrange, ars_start->address,
|
||||
ars_start->length);
|
||||
*cmd_rc = 0;
|
||||
}
|
||||
|
|
@ -320,7 +344,8 @@ static int nfit_test_cmd_ars_status(struct ars_state *ars_state,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err,
|
||||
static int nfit_test_cmd_clear_error(struct nfit_test *t,
|
||||
struct nd_cmd_clear_error *clear_err,
|
||||
unsigned int buf_len, int *cmd_rc)
|
||||
{
|
||||
const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1;
|
||||
|
|
@ -330,18 +355,91 @@ static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err,
|
|||
if ((clear_err->address & mask) || (clear_err->length & mask))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Report 'all clear' success for all commands even though a new
|
||||
* scrub will find errors again. This is enough to have the
|
||||
* error removed from the 'badblocks' tracking in the pmem
|
||||
* driver.
|
||||
*/
|
||||
badrange_forget(&t->badrange, clear_err->address, clear_err->length);
|
||||
clear_err->status = 0;
|
||||
clear_err->cleared = clear_err->length;
|
||||
*cmd_rc = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct region_search_spa {
|
||||
u64 addr;
|
||||
struct nd_region *region;
|
||||
};
|
||||
|
||||
static int is_region_device(struct device *dev)
|
||||
{
|
||||
return !strncmp(dev->kobj.name, "region", 6);
|
||||
}
|
||||
|
||||
static int nfit_test_search_region_spa(struct device *dev, void *data)
|
||||
{
|
||||
struct region_search_spa *ctx = data;
|
||||
struct nd_region *nd_region;
|
||||
resource_size_t ndr_end;
|
||||
|
||||
if (!is_region_device(dev))
|
||||
return 0;
|
||||
|
||||
nd_region = to_nd_region(dev);
|
||||
ndr_end = nd_region->ndr_start + nd_region->ndr_size;
|
||||
|
||||
if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) {
|
||||
ctx->region = nd_region;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_search_spa(struct nvdimm_bus *bus,
|
||||
struct nd_cmd_translate_spa *spa)
|
||||
{
|
||||
int ret;
|
||||
struct nd_region *nd_region = NULL;
|
||||
struct nvdimm *nvdimm = NULL;
|
||||
struct nd_mapping *nd_mapping = NULL;
|
||||
struct region_search_spa ctx = {
|
||||
.addr = spa->spa,
|
||||
.region = NULL,
|
||||
};
|
||||
u64 dpa;
|
||||
|
||||
ret = device_for_each_child(&bus->dev, &ctx,
|
||||
nfit_test_search_region_spa);
|
||||
|
||||
if (!ret)
|
||||
return -ENODEV;
|
||||
|
||||
nd_region = ctx.region;
|
||||
|
||||
dpa = ctx.addr - nd_region->ndr_start;
|
||||
|
||||
/*
|
||||
* last dimm is selected for test
|
||||
*/
|
||||
nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1];
|
||||
nvdimm = nd_mapping->nvdimm;
|
||||
|
||||
spa->devices[0].nfit_device_handle = handle[nvdimm->id];
|
||||
spa->num_nvdimms = 1;
|
||||
spa->devices[0].dpa = dpa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus,
|
||||
struct nd_cmd_translate_spa *spa, unsigned int buf_len)
|
||||
{
|
||||
if (buf_len < spa->translate_length)
|
||||
return -EINVAL;
|
||||
|
||||
if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms)
|
||||
spa->status = 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_smart(struct nd_cmd_smart *smart, unsigned int buf_len)
|
||||
{
|
||||
static const struct nd_smart_payload smart_data = {
|
||||
|
|
@ -378,6 +476,93 @@ static int nfit_test_cmd_smart_threshold(struct nd_cmd_smart_threshold *smart_t,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void uc_error_notify(struct work_struct *work)
|
||||
{
|
||||
struct nfit_test *t = container_of(work, typeof(*t), work);
|
||||
|
||||
__acpi_nfit_notify(&t->pdev.dev, t, NFIT_NOTIFY_UC_MEMORY_ERROR);
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_ars_error_inject(struct nfit_test *t,
|
||||
struct nd_cmd_ars_err_inj *err_inj, unsigned int buf_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (buf_len != sizeof(*err_inj)) {
|
||||
rc = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (err_inj->err_inj_spa_range_length <= 0) {
|
||||
rc = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = badrange_add(&t->badrange, err_inj->err_inj_spa_range_base,
|
||||
err_inj->err_inj_spa_range_length);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
if (err_inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY))
|
||||
queue_work(nfit_wq, &t->work);
|
||||
|
||||
err_inj->status = 0;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err_inj->status = NFIT_ARS_INJECT_INVALID;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_ars_inject_clear(struct nfit_test *t,
|
||||
struct nd_cmd_ars_err_inj_clr *err_clr, unsigned int buf_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (buf_len != sizeof(*err_clr)) {
|
||||
rc = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (err_clr->err_inj_clr_spa_range_length <= 0) {
|
||||
rc = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
badrange_forget(&t->badrange, err_clr->err_inj_clr_spa_range_base,
|
||||
err_clr->err_inj_clr_spa_range_length);
|
||||
|
||||
err_clr->status = 0;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
err_clr->status = NFIT_ARS_INJECT_INVALID;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_ars_inject_status(struct nfit_test *t,
|
||||
struct nd_cmd_ars_err_inj_stat *err_stat,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
struct badrange_entry *be;
|
||||
int max = SZ_4K / sizeof(struct nd_error_stat_query_record);
|
||||
int i = 0;
|
||||
|
||||
err_stat->status = 0;
|
||||
spin_lock(&t->badrange.lock);
|
||||
list_for_each_entry(be, &t->badrange.list, list) {
|
||||
err_stat->record[i].err_inj_stat_spa_range_base = be->start;
|
||||
err_stat->record[i].err_inj_stat_spa_range_length = be->length;
|
||||
i++;
|
||||
if (i > max)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&t->badrange.lock);
|
||||
err_stat->inj_err_rec_count = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -449,6 +634,38 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
}
|
||||
} else {
|
||||
struct ars_state *ars_state = &t->ars_state;
|
||||
struct nd_cmd_pkg *call_pkg = buf;
|
||||
|
||||
if (!nd_desc)
|
||||
return -ENOTTY;
|
||||
|
||||
if (cmd == ND_CMD_CALL) {
|
||||
func = call_pkg->nd_command;
|
||||
|
||||
buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
|
||||
buf = (void *) call_pkg->nd_payload;
|
||||
|
||||
switch (func) {
|
||||
case NFIT_CMD_TRANSLATE_SPA:
|
||||
rc = nfit_test_cmd_translate_spa(
|
||||
acpi_desc->nvdimm_bus, buf, buf_len);
|
||||
return rc;
|
||||
case NFIT_CMD_ARS_INJECT_SET:
|
||||
rc = nfit_test_cmd_ars_error_inject(t, buf,
|
||||
buf_len);
|
||||
return rc;
|
||||
case NFIT_CMD_ARS_INJECT_CLEAR:
|
||||
rc = nfit_test_cmd_ars_inject_clear(t, buf,
|
||||
buf_len);
|
||||
return rc;
|
||||
case NFIT_CMD_ARS_INJECT_GET:
|
||||
rc = nfit_test_cmd_ars_inject_status(t, buf,
|
||||
buf_len);
|
||||
return rc;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask))
|
||||
return -ENOTTY;
|
||||
|
|
@ -458,15 +675,15 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||
rc = nfit_test_cmd_ars_cap(buf, buf_len);
|
||||
break;
|
||||
case ND_CMD_ARS_START:
|
||||
rc = nfit_test_cmd_ars_start(ars_state, buf, buf_len,
|
||||
cmd_rc);
|
||||
rc = nfit_test_cmd_ars_start(t, ars_state, buf,
|
||||
buf_len, cmd_rc);
|
||||
break;
|
||||
case ND_CMD_ARS_STATUS:
|
||||
rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len,
|
||||
cmd_rc);
|
||||
break;
|
||||
case ND_CMD_CLEAR_ERROR:
|
||||
rc = nfit_test_cmd_clear_error(buf, buf_len, cmd_rc);
|
||||
rc = nfit_test_cmd_clear_error(t, buf, buf_len, cmd_rc);
|
||||
break;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
|
|
@ -566,10 +783,9 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr)
|
|||
|
||||
static int ars_state_init(struct device *dev, struct ars_state *ars_state)
|
||||
{
|
||||
/* for testing, only store up to n records that fit within 4k */
|
||||
ars_state->ars_status = devm_kzalloc(dev,
|
||||
sizeof(struct nd_cmd_ars_status)
|
||||
+ sizeof(struct nd_ars_record) * NFIT_TEST_ARS_RECORDS,
|
||||
GFP_KERNEL);
|
||||
sizeof(struct nd_cmd_ars_status) + SZ_4K, GFP_KERNEL);
|
||||
if (!ars_state->ars_status)
|
||||
return -ENOMEM;
|
||||
spin_lock_init(&ars_state->lock);
|
||||
|
|
@ -1419,7 +1635,8 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||
+ i * sizeof(u64);
|
||||
}
|
||||
|
||||
post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE);
|
||||
post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0],
|
||||
SPA0_SIZE);
|
||||
|
||||
acpi_desc = &t->acpi_desc;
|
||||
set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
|
||||
|
|
@ -1430,7 +1647,12 @@ static void nfit_test0_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_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);
|
||||
}
|
||||
|
||||
static void nfit_test1_setup(struct nfit_test *t)
|
||||
|
|
@ -1520,7 +1742,8 @@ static void nfit_test1_setup(struct nfit_test *t)
|
|||
dcr->code = NFIT_FIC_BYTE;
|
||||
dcr->windows = 0;
|
||||
|
||||
post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE);
|
||||
post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0],
|
||||
SPA2_SIZE);
|
||||
|
||||
acpi_desc = &t->acpi_desc;
|
||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
|
||||
|
|
@ -1589,6 +1812,7 @@ static int nfit_ctl_test(struct device *dev)
|
|||
unsigned long mask, cmd_size, offset;
|
||||
union {
|
||||
struct nd_cmd_get_config_size cfg_size;
|
||||
struct nd_cmd_clear_error clear_err;
|
||||
struct nd_cmd_ars_status ars_stat;
|
||||
struct nd_cmd_ars_cap ars_cap;
|
||||
char buf[sizeof(struct nd_cmd_ars_status)
|
||||
|
|
@ -1613,10 +1837,15 @@ static int nfit_ctl_test(struct device *dev)
|
|||
.cmd_mask = 1UL << ND_CMD_ARS_CAP
|
||||
| 1UL << ND_CMD_ARS_START
|
||||
| 1UL << ND_CMD_ARS_STATUS
|
||||
| 1UL << ND_CMD_CLEAR_ERROR,
|
||||
| 1UL << ND_CMD_CLEAR_ERROR
|
||||
| 1UL << ND_CMD_CALL,
|
||||
.module = THIS_MODULE,
|
||||
.provider_name = "ACPI.NFIT",
|
||||
.ndctl = acpi_nfit_ctl,
|
||||
.bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA
|
||||
| 1UL << NFIT_CMD_ARS_INJECT_SET
|
||||
| 1UL << NFIT_CMD_ARS_INJECT_CLEAR
|
||||
| 1UL << NFIT_CMD_ARS_INJECT_GET,
|
||||
},
|
||||
.dev = &adev->dev,
|
||||
};
|
||||
|
|
@ -1767,6 +1996,23 @@ static int nfit_ctl_test(struct device *dev)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* test clear error */
|
||||
cmd_size = sizeof(cmds.clear_err);
|
||||
cmds.clear_err = (struct nd_cmd_clear_error) {
|
||||
.length = 512,
|
||||
.cleared = 512,
|
||||
};
|
||||
rc = setup_result(cmds.buf, cmd_size);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CLEAR_ERROR,
|
||||
cmds.buf, cmd_size, &cmd_rc);
|
||||
if (rc < 0 || cmd_rc) {
|
||||
dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
|
||||
__func__, __LINE__, rc, cmd_rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1915,6 +2161,10 @@ static __init int nfit_test_init(void)
|
|||
|
||||
nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm);
|
||||
|
||||
nfit_wq = create_singlethread_workqueue("nfit");
|
||||
if (!nfit_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm");
|
||||
if (IS_ERR(nfit_test_dimm)) {
|
||||
rc = PTR_ERR(nfit_test_dimm);
|
||||
|
|
@ -1931,6 +2181,7 @@ static __init int nfit_test_init(void)
|
|||
goto err_register;
|
||||
}
|
||||
INIT_LIST_HEAD(&nfit_test->resources);
|
||||
badrange_init(&nfit_test->badrange);
|
||||
switch (i) {
|
||||
case 0:
|
||||
nfit_test->num_pm = NUM_PM;
|
||||
|
|
@ -1966,6 +2217,7 @@ static __init int nfit_test_init(void)
|
|||
goto err_register;
|
||||
|
||||
instances[i] = nfit_test;
|
||||
INIT_WORK(&nfit_test->work, uc_error_notify);
|
||||
}
|
||||
|
||||
rc = platform_driver_register(&nfit_test_driver);
|
||||
|
|
@ -1974,6 +2226,7 @@ static __init int nfit_test_init(void)
|
|||
return 0;
|
||||
|
||||
err_register:
|
||||
destroy_workqueue(nfit_wq);
|
||||
for (i = 0; i < NUM_NFITS; i++)
|
||||
if (instances[i])
|
||||
platform_device_unregister(&instances[i]->pdev);
|
||||
|
|
@ -1989,6 +2242,8 @@ static __exit void nfit_test_exit(void)
|
|||
{
|
||||
int i;
|
||||
|
||||
flush_workqueue(nfit_wq);
|
||||
destroy_workqueue(nfit_wq);
|
||||
for (i = 0; i < NUM_NFITS; i++)
|
||||
platform_device_unregister(&instances[i]->pdev);
|
||||
platform_driver_unregister(&nfit_test_driver);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,58 @@ struct nfit_test_resource {
|
|||
void *buf;
|
||||
};
|
||||
|
||||
#define ND_TRANSLATE_SPA_STATUS_INVALID_SPA 2
|
||||
#define NFIT_ARS_INJECT_INVALID 2
|
||||
|
||||
enum err_inj_options {
|
||||
ND_ARS_ERR_INJ_OPT_NOTIFY = 0,
|
||||
};
|
||||
|
||||
/* nfit commands */
|
||||
enum nfit_cmd_num {
|
||||
NFIT_CMD_TRANSLATE_SPA = 5,
|
||||
NFIT_CMD_ARS_INJECT_SET = 7,
|
||||
NFIT_CMD_ARS_INJECT_CLEAR = 8,
|
||||
NFIT_CMD_ARS_INJECT_GET = 9,
|
||||
};
|
||||
|
||||
struct nd_cmd_translate_spa {
|
||||
__u64 spa;
|
||||
__u32 status;
|
||||
__u8 flags;
|
||||
__u8 _reserved[3];
|
||||
__u64 translate_length;
|
||||
__u32 num_nvdimms;
|
||||
struct nd_nvdimm_device {
|
||||
__u32 nfit_device_handle;
|
||||
__u32 _reserved;
|
||||
__u64 dpa;
|
||||
} __packed devices[0];
|
||||
|
||||
} __packed;
|
||||
|
||||
struct nd_cmd_ars_err_inj {
|
||||
__u64 err_inj_spa_range_base;
|
||||
__u64 err_inj_spa_range_length;
|
||||
__u8 err_inj_options;
|
||||
__u32 status;
|
||||
} __packed;
|
||||
|
||||
struct nd_cmd_ars_err_inj_clr {
|
||||
__u64 err_inj_clr_spa_range_base;
|
||||
__u64 err_inj_clr_spa_range_length;
|
||||
__u32 status;
|
||||
} __packed;
|
||||
|
||||
struct nd_cmd_ars_err_inj_stat {
|
||||
__u32 status;
|
||||
__u32 inj_err_rec_count;
|
||||
struct nd_error_stat_query_record {
|
||||
__u64 err_inj_stat_spa_range_base;
|
||||
__u64 err_inj_stat_spa_range_length;
|
||||
} __packed record[0];
|
||||
} __packed;
|
||||
|
||||
union acpi_object;
|
||||
typedef void *acpi_handle;
|
||||
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ static void multiorder_account(void)
|
|||
__radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12);
|
||||
__radix_tree_lookup(&tree, 1 << 5, &node, &slot);
|
||||
assert(node->count == node->exceptional * 2);
|
||||
__radix_tree_replace(&tree, node, slot, NULL, NULL, NULL);
|
||||
__radix_tree_replace(&tree, node, slot, NULL, NULL);
|
||||
assert(node->exceptional == 0);
|
||||
|
||||
item_kill_tree(&tree);
|
||||
|
|
|
|||
30
tools/testing/scatterlist/Makefile
Normal file
30
tools/testing/scatterlist/Makefile
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
CFLAGS += -I. -I../../include -g -O2 -Wall -fsanitize=address
|
||||
LDFLAGS += -fsanitize=address -fsanitize=undefined
|
||||
TARGETS = main
|
||||
OFILES = main.o scatterlist.o
|
||||
|
||||
ifeq ($(BUILD), 32)
|
||||
CFLAGS += -m32
|
||||
LDFLAGS += -m32
|
||||
endif
|
||||
|
||||
targets: include $(TARGETS)
|
||||
|
||||
main: $(OFILES)
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGETS) $(OFILES) scatterlist.c linux/scatterlist.h linux/highmem.h linux/kmemleak.h asm/io.h
|
||||
@rmdir asm
|
||||
|
||||
scatterlist.c: ../../../lib/scatterlist.c
|
||||
@sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@
|
||||
|
||||
.PHONY: include
|
||||
|
||||
include: ../../../include/linux/scatterlist.h
|
||||
@mkdir -p linux
|
||||
@mkdir -p asm
|
||||
@touch asm/io.h
|
||||
@touch linux/highmem.h
|
||||
@touch linux/kmemleak.h
|
||||
@cp $< linux/scatterlist.h
|
||||
125
tools/testing/scatterlist/linux/mm.h
Normal file
125
tools/testing/scatterlist/linux/mm.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#ifndef _LINUX_MM_H
|
||||
#define _LINUX_MM_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef unsigned long dma_addr_t;
|
||||
|
||||
#define unlikely
|
||||
|
||||
#define BUG_ON(x) assert(!(x))
|
||||
|
||||
#define WARN_ON(condition) ({ \
|
||||
int __ret_warn_on = !!(condition); \
|
||||
unlikely(__ret_warn_on); \
|
||||
})
|
||||
|
||||
#define WARN_ON_ONCE(condition) ({ \
|
||||
int __ret_warn_on = !!(condition); \
|
||||
if (unlikely(__ret_warn_on)) \
|
||||
assert(0); \
|
||||
unlikely(__ret_warn_on); \
|
||||
})
|
||||
|
||||
#define PAGE_SIZE (4096)
|
||||
#define PAGE_SHIFT (12)
|
||||
#define PAGE_MASK (~(PAGE_SIZE-1))
|
||||
|
||||
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
|
||||
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
||||
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
|
||||
|
||||
#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
|
||||
|
||||
#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK)
|
||||
|
||||
#define virt_to_page(x) ((void *)x)
|
||||
#define page_address(x) ((void *)x)
|
||||
|
||||
static inline unsigned long page_to_phys(struct page *page)
|
||||
{
|
||||
assert(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define page_to_pfn(page) ((unsigned long)(page) / PAGE_SIZE)
|
||||
#define pfn_to_page(pfn) (void *)((pfn) * PAGE_SIZE)
|
||||
#define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n))
|
||||
|
||||
#define __min(t1, t2, min1, min2, x, y) ({ \
|
||||
t1 min1 = (x); \
|
||||
t2 min2 = (y); \
|
||||
(void) (&min1 == &min2); \
|
||||
min1 < min2 ? min1 : min2; })
|
||||
|
||||
#define ___PASTE(a,b) a##b
|
||||
#define __PASTE(a,b) ___PASTE(a,b)
|
||||
|
||||
#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
|
||||
|
||||
#define min(x, y) \
|
||||
__min(typeof(x), typeof(y), \
|
||||
__UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \
|
||||
x, y)
|
||||
|
||||
#define min_t(type, x, y) \
|
||||
__min(type, type, \
|
||||
__UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \
|
||||
x, y)
|
||||
|
||||
#define preemptible() (1)
|
||||
|
||||
static inline void *kmap(struct page *page)
|
||||
{
|
||||
assert(0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void *kmap_atomic(struct page *page)
|
||||
{
|
||||
assert(0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void kunmap(void *addr)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static inline void kunmap_atomic(void *addr)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static inline unsigned long __get_free_page(unsigned int flags)
|
||||
{
|
||||
return (unsigned long)malloc(PAGE_SIZE);
|
||||
}
|
||||
|
||||
static inline void free_page(unsigned long page)
|
||||
{
|
||||
free((void *)page);
|
||||
}
|
||||
|
||||
static inline void *kmalloc(unsigned int size, unsigned int flags)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
#define kfree(x) free(x)
|
||||
|
||||
#define kmemleak_alloc(a, b, c, d)
|
||||
#define kmemleak_free(a)
|
||||
|
||||
#define PageSlab(p) (0)
|
||||
#define flush_kernel_dcache_page(p)
|
||||
|
||||
#endif
|
||||
79
tools/testing/scatterlist/main.c
Normal file
79
tools/testing/scatterlist/main.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#define MAX_PAGES (64)
|
||||
|
||||
static void set_pages(struct page **pages, const unsigned *array, unsigned num)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
assert(num < MAX_PAGES);
|
||||
for (i = 0; i < num; i++)
|
||||
pages[i] = (struct page *)(unsigned long)
|
||||
((1 + array[i]) * PAGE_SIZE);
|
||||
}
|
||||
|
||||
#define pfn(...) (unsigned []){ __VA_ARGS__ }
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const unsigned int sgmax = SCATTERLIST_MAX_SEGMENT;
|
||||
struct test {
|
||||
int alloc_ret;
|
||||
unsigned num_pages;
|
||||
unsigned *pfn;
|
||||
unsigned size;
|
||||
unsigned int max_seg;
|
||||
unsigned int expected_segments;
|
||||
} *test, tests[] = {
|
||||
{ -EINVAL, 1, pfn(0), PAGE_SIZE, PAGE_SIZE + 1, 1 },
|
||||
{ -EINVAL, 1, pfn(0), PAGE_SIZE, 0, 1 },
|
||||
{ -EINVAL, 1, pfn(0), PAGE_SIZE, sgmax + 1, 1 },
|
||||
{ 0, 1, pfn(0), PAGE_SIZE, sgmax, 1 },
|
||||
{ 0, 1, pfn(0), 1, sgmax, 1 },
|
||||
{ 0, 2, pfn(0, 1), 2 * PAGE_SIZE, sgmax, 1 },
|
||||
{ 0, 2, pfn(1, 0), 2 * PAGE_SIZE, sgmax, 2 },
|
||||
{ 0, 3, pfn(0, 1, 2), 3 * PAGE_SIZE, sgmax, 1 },
|
||||
{ 0, 3, pfn(0, 2, 1), 3 * PAGE_SIZE, sgmax, 3 },
|
||||
{ 0, 3, pfn(0, 1, 3), 3 * PAGE_SIZE, sgmax, 2 },
|
||||
{ 0, 3, pfn(1, 2, 4), 3 * PAGE_SIZE, sgmax, 2 },
|
||||
{ 0, 3, pfn(1, 3, 4), 3 * PAGE_SIZE, sgmax, 2 },
|
||||
{ 0, 4, pfn(0, 1, 3, 4), 4 * PAGE_SIZE, sgmax, 2 },
|
||||
{ 0, 5, pfn(0, 1, 3, 4, 5), 5 * PAGE_SIZE, sgmax, 2 },
|
||||
{ 0, 5, pfn(0, 1, 3, 4, 6), 5 * PAGE_SIZE, sgmax, 3 },
|
||||
{ 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, sgmax, 1 },
|
||||
{ 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
|
||||
{ 0, 6, pfn(0, 1, 2, 3, 4, 5), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
|
||||
{ 0, 6, pfn(0, 2, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 4 },
|
||||
{ 0, 6, pfn(0, 1, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 },
|
||||
{ 0, 0, NULL, 0, 0, 0 },
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0, test = tests; test->expected_segments; test++, i++) {
|
||||
struct page *pages[MAX_PAGES];
|
||||
struct sg_table st;
|
||||
int ret;
|
||||
|
||||
set_pages(pages, test->pfn, test->num_pages);
|
||||
|
||||
ret = __sg_alloc_table_from_pages(&st, pages, test->num_pages,
|
||||
0, test->size, test->max_seg,
|
||||
GFP_KERNEL);
|
||||
assert(ret == test->alloc_ret);
|
||||
|
||||
if (test->alloc_ret)
|
||||
continue;
|
||||
|
||||
assert(st.nents == test->expected_segments);
|
||||
assert(st.orig_nents == test->expected_segments);
|
||||
|
||||
sg_free_table(&st);
|
||||
}
|
||||
|
||||
assert(i == (sizeof(tests) / sizeof(tests[0])) - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
TARGETS = bpf
|
||||
TARGETS = android
|
||||
TARGETS += bpf
|
||||
TARGETS += breakpoints
|
||||
TARGETS += capabilities
|
||||
TARGETS += cpufreq
|
||||
|
|
|
|||
46
tools/testing/selftests/android/Makefile
Normal file
46
tools/testing/selftests/android/Makefile
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
SUBDIRS := ion
|
||||
|
||||
TEST_PROGS := run.sh
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
all:
|
||||
@for DIR in $(SUBDIRS); do \
|
||||
BUILD_TARGET=$(OUTPUT)/$$DIR; \
|
||||
mkdir $$BUILD_TARGET -p; \
|
||||
make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\
|
||||
#SUBDIR test prog name should be in the form: SUBDIR_test.sh
|
||||
TEST=$$DIR"_test.sh"; \
|
||||
if [ -e $$DIR/$$TEST ]; then
|
||||
rsync -a $$DIR/$$TEST $$BUILD_TARGET/;
|
||||
fi
|
||||
done
|
||||
|
||||
override define RUN_TESTS
|
||||
@cd $(OUTPUT); ./run.sh
|
||||
endef
|
||||
|
||||
override define INSTALL_RULE
|
||||
mkdir -p $(INSTALL_PATH)
|
||||
install -t $(INSTALL_PATH) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES)
|
||||
|
||||
@for SUBDIR in $(SUBDIRS); do \
|
||||
BUILD_TARGET=$(OUTPUT)/$$SUBDIR; \
|
||||
mkdir $$BUILD_TARGET -p; \
|
||||
$(MAKE) OUTPUT=$$BUILD_TARGET -C $$SUBDIR INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \
|
||||
done;
|
||||
endef
|
||||
|
||||
override define EMIT_TESTS
|
||||
echo "./run.sh"
|
||||
endef
|
||||
|
||||
override define CLEAN
|
||||
@for DIR in $(SUBDIRS); do \
|
||||
BUILD_TARGET=$(OUTPUT)/$$DIR; \
|
||||
mkdir $$BUILD_TARGET -p; \
|
||||
make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\
|
||||
done
|
||||
endef
|
||||
2
tools/testing/selftests/android/ion/.gitignore
vendored
Normal file
2
tools/testing/selftests/android/ion/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ionapp_export
|
||||
ionapp_import
|
||||
16
tools/testing/selftests/android/ion/Makefile
Normal file
16
tools/testing/selftests/android/ion/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
INCLUDEDIR := -I. -I../../../../../drivers/staging/android/uapi/
|
||||
CFLAGS := $(CFLAGS) $(INCLUDEDIR) -Wall -O2 -g
|
||||
|
||||
TEST_GEN_FILES := ionapp_export ionapp_import
|
||||
|
||||
all: $(TEST_GEN_FILES)
|
||||
|
||||
$(TEST_GEN_FILES): ipcsocket.c ionutils.c
|
||||
|
||||
TEST_PROGS := ion_test.sh
|
||||
|
||||
include ../../lib.mk
|
||||
|
||||
$(OUTPUT)/ionapp_export: ionapp_export.c ipcsocket.c ionutils.c
|
||||
$(OUTPUT)/ionapp_import: ionapp_import.c ipcsocket.c ionutils.c
|
||||
101
tools/testing/selftests/android/ion/README
Normal file
101
tools/testing/selftests/android/ion/README
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
ION BUFFER SHARING UTILITY
|
||||
==========================
|
||||
File: ion_test.sh : Utility to test ION driver buffer sharing mechanism.
|
||||
Author: Pintu Kumar <[email protected]>
|
||||
|
||||
Introduction:
|
||||
-------------
|
||||
This is a test utility to verify ION buffer sharing in user space
|
||||
between 2 independent processes.
|
||||
It uses unix domain socket (with SCM_RIGHTS) as IPC to transfer an FD to
|
||||
another process to share the same buffer.
|
||||
This utility demonstrates how ION buffer sharing can be implemented between
|
||||
two user space processes, using various heap types.
|
||||
The following heap types are supported by ION driver.
|
||||
ION_HEAP_TYPE_SYSTEM (0)
|
||||
ION_HEAP_TYPE_SYSTEM_CONTIG (1)
|
||||
ION_HEAP_TYPE_CARVEOUT (2)
|
||||
ION_HEAP_TYPE_CHUNK (3)
|
||||
ION_HEAP_TYPE_DMA (4)
|
||||
|
||||
By default only the SYSTEM and SYSTEM_CONTIG heaps are supported.
|
||||
Each heap is associated with the respective heap id.
|
||||
This utility is designed in the form of client/server program.
|
||||
The server part (ionapp_export) is the exporter of the buffer.
|
||||
It is responsible for creating an ION client, allocating the buffer based on
|
||||
the heap id, writing some data to this buffer and then exporting the FD
|
||||
(associated with this buffer) to another process using socket IPC.
|
||||
This FD is called as buffer FD (which is different than the ION client FD).
|
||||
|
||||
The client part (ionapp_import) is the importer of the buffer.
|
||||
It retrives the FD from the socket data and installs into its address space.
|
||||
This new FD internally points to the same kernel buffer.
|
||||
So first it reads the data that is stored in this buffer and prints it.
|
||||
Then it writes the different size of data (it could be different data) to the
|
||||
same buffer.
|
||||
Finally the buffer FD must be closed by both the exporter and importer.
|
||||
Thus the same kernel buffer is shared among two user space processes using
|
||||
ION driver and only one time allocation.
|
||||
|
||||
Prerequisite:
|
||||
-------------
|
||||
This utility works only if /dev/ion interface is present.
|
||||
The following configs needs to be enabled in kernel to include ion driver.
|
||||
CONFIG_ANDROID=y
|
||||
CONFIG_STAGING=y
|
||||
CONFIG_ION=y
|
||||
CONFIG_ION_SYSTEM_HEAP=y
|
||||
|
||||
This utility requires to be run as root user.
|
||||
|
||||
|
||||
Compile and test:
|
||||
-----------------
|
||||
This utility is made to be run as part of kselftest framework in kernel.
|
||||
To compile and run using kselftest you can simply do the following from the
|
||||
kernel top directory.
|
||||
linux$ make TARGETS=android kselftest
|
||||
Or you can also use:
|
||||
linux$ make -C tools/testing/selftests TARGETS=android run_tests
|
||||
Using the selftest it can directly execute the ion_test.sh script to test the
|
||||
buffer sharing using ion system heap.
|
||||
Currently the heap size is hard coded as just 10 bytes inside this script.
|
||||
You need to be a root user to run under selftest.
|
||||
|
||||
You can also compile and test manually using the following steps:
|
||||
ion$ make
|
||||
These will generate 2 executable: ionapp_export, ionapp_import
|
||||
Now you can run the export and import manually by specifying the heap type
|
||||
and the heap size.
|
||||
You can also directly execute the shell script to run the test automatically.
|
||||
Simply use the following command to run the test.
|
||||
ion$ sudo ./ion_test.sh
|
||||
|
||||
Test Results:
|
||||
-------------
|
||||
The utility is verified on Ubuntu-32 bit system with Linux Kernel 4.14.
|
||||
Here is the snapshot of the test result using kselftest.
|
||||
|
||||
linux# make TARGETS=android kselftest
|
||||
heap_type: 0, heap_size: 10
|
||||
--------------------------------------
|
||||
heap type: 0
|
||||
heap id: 1
|
||||
heap name: ion_system_heap
|
||||
--------------------------------------
|
||||
Fill buffer content:
|
||||
0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd
|
||||
Sharing fd: 6, Client fd: 5
|
||||
<ion_close_buffer_fd>: buffer release successfully....
|
||||
Received buffer fd: 4
|
||||
Read buffer content:
|
||||
0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0x0 0x0 0x0 0x0 0x0 0x0
|
||||
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
|
||||
Fill buffer content:
|
||||
0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd
|
||||
0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd
|
||||
0xfd 0xfd
|
||||
<ion_close_buffer_fd>: buffer release successfully....
|
||||
ion_test.sh: heap_type: 0 - [PASS]
|
||||
|
||||
ion_test.sh: done
|
||||
4
tools/testing/selftests/android/ion/config
Normal file
4
tools/testing/selftests/android/ion/config
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_ANDROID=y
|
||||
CONFIG_STAGING=y
|
||||
CONFIG_ION=y
|
||||
CONFIG_ION_SYSTEM_HEAP=y
|
||||
143
tools/testing/selftests/android/ion/ion.h
Normal file
143
tools/testing/selftests/android/ion/ion.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* ion.h
|
||||
*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file is copied from drivers/staging/android/uapi/ion.h
|
||||
* This local copy is required for the selftest to pass, when build
|
||||
* outside the kernel source tree.
|
||||
* Please keep this file in sync with its original file until the
|
||||
* ion driver is moved outside the staging tree.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_ION_H
|
||||
#define _UAPI_LINUX_ION_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* enum ion_heap_types - list of all possible types of heaps
|
||||
* @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
|
||||
* @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
|
||||
* @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
|
||||
* carveout heap, allocations are physically
|
||||
* contiguous
|
||||
* @ION_HEAP_TYPE_DMA: memory allocated via DMA API
|
||||
* @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
|
||||
* is used to identify the heaps, so only 32
|
||||
* total heap types are supported
|
||||
*/
|
||||
enum ion_heap_type {
|
||||
ION_HEAP_TYPE_SYSTEM,
|
||||
ION_HEAP_TYPE_SYSTEM_CONTIG,
|
||||
ION_HEAP_TYPE_CARVEOUT,
|
||||
ION_HEAP_TYPE_CHUNK,
|
||||
ION_HEAP_TYPE_DMA,
|
||||
ION_HEAP_TYPE_CUSTOM, /*
|
||||
* must be last so device specific heaps always
|
||||
* are at the end of this enum
|
||||
*/
|
||||
};
|
||||
|
||||
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
|
||||
|
||||
/**
|
||||
* allocation flags - the lower 16 bits are used by core ion, the upper 16
|
||||
* bits are reserved for use by the heaps themselves.
|
||||
*/
|
||||
|
||||
/*
|
||||
* mappings of this buffer should be cached, ion will do cache maintenance
|
||||
* when the buffer is mapped for dma
|
||||
*/
|
||||
#define ION_FLAG_CACHED 1
|
||||
|
||||
/**
|
||||
* DOC: Ion Userspace API
|
||||
*
|
||||
* create a client by opening /dev/ion
|
||||
* most operations handled via following ioctls
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ion_allocation_data - metadata passed from userspace for allocations
|
||||
* @len: size of the allocation
|
||||
* @heap_id_mask: mask of heap ids to allocate from
|
||||
* @flags: flags passed to heap
|
||||
* @handle: pointer that will be populated with a cookie to use to
|
||||
* refer to this allocation
|
||||
*
|
||||
* Provided by userspace as an argument to the ioctl
|
||||
*/
|
||||
struct ion_allocation_data {
|
||||
__u64 len;
|
||||
__u32 heap_id_mask;
|
||||
__u32 flags;
|
||||
__u32 fd;
|
||||
__u32 unused;
|
||||
};
|
||||
|
||||
#define MAX_HEAP_NAME 32
|
||||
|
||||
/**
|
||||
* struct ion_heap_data - data about a heap
|
||||
* @name - first 32 characters of the heap name
|
||||
* @type - heap type
|
||||
* @heap_id - heap id for the heap
|
||||
*/
|
||||
struct ion_heap_data {
|
||||
char name[MAX_HEAP_NAME];
|
||||
__u32 type;
|
||||
__u32 heap_id;
|
||||
__u32 reserved0;
|
||||
__u32 reserved1;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ion_heap_query - collection of data about all heaps
|
||||
* @cnt - total number of heaps to be copied
|
||||
* @heaps - buffer to copy heap data
|
||||
*/
|
||||
struct ion_heap_query {
|
||||
__u32 cnt; /* Total number of heaps to be copied */
|
||||
__u32 reserved0; /* align to 64bits */
|
||||
__u64 heaps; /* buffer to be populated */
|
||||
__u32 reserved1;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
#define ION_IOC_MAGIC 'I'
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_ALLOC - allocate memory
|
||||
*
|
||||
* Takes an ion_allocation_data struct and returns it with the handle field
|
||||
* populated with the opaque handle for the allocation.
|
||||
*/
|
||||
#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
|
||||
struct ion_allocation_data)
|
||||
|
||||
/**
|
||||
* DOC: ION_IOC_HEAP_QUERY - information about available heaps
|
||||
*
|
||||
* Takes an ion_heap_query structure and populates information about
|
||||
* available Ion heaps.
|
||||
*/
|
||||
#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, \
|
||||
struct ion_heap_query)
|
||||
|
||||
#endif /* _UAPI_LINUX_ION_H */
|
||||
55
tools/testing/selftests/android/ion/ion_test.sh
Executable file
55
tools/testing/selftests/android/ion/ion_test.sh
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/bash
|
||||
|
||||
heapsize=4096
|
||||
TCID="ion_test.sh"
|
||||
errcode=0
|
||||
|
||||
run_test()
|
||||
{
|
||||
heaptype=$1
|
||||
./ionapp_export -i $heaptype -s $heapsize &
|
||||
sleep 1
|
||||
./ionapp_import
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "$TCID: heap_type: $heaptype - [FAIL]"
|
||||
errcode=1
|
||||
else
|
||||
echo "$TCID: heap_type: $heaptype - [PASS]"
|
||||
fi
|
||||
sleep 1
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_root()
|
||||
{
|
||||
uid=$(id -u)
|
||||
if [ $uid -ne 0 ]; then
|
||||
echo $TCID: must be run as root >&2
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
check_device()
|
||||
{
|
||||
DEVICE=/dev/ion
|
||||
if [ ! -e $DEVICE ]; then
|
||||
echo $TCID: No $DEVICE device found >&2
|
||||
echo $TCID: May be CONFIG_ION is not set >&2
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
main_function()
|
||||
{
|
||||
check_device
|
||||
check_root
|
||||
|
||||
# ION_SYSTEM_HEAP TEST
|
||||
run_test 0
|
||||
# ION_SYSTEM_CONTIG_HEAP TEST
|
||||
run_test 1
|
||||
}
|
||||
|
||||
main_function
|
||||
echo "$TCID: done"
|
||||
exit $errcode
|
||||
135
tools/testing/selftests/android/ion/ionapp_export.c
Normal file
135
tools/testing/selftests/android/ion/ionapp_export.c
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* ionapp_export.c
|
||||
*
|
||||
* It is a user space utility to create and export android
|
||||
* ion memory buffer fd to another process using unix domain socket as IPC.
|
||||
* This acts like a server for ionapp_import(client).
|
||||
* So, this server has to be started first before the client.
|
||||
*
|
||||
* Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include "ionutils.h"
|
||||
#include "ipcsocket.h"
|
||||
|
||||
|
||||
void print_usage(int argc, char *argv[])
|
||||
{
|
||||
printf("Usage: %s [-h <help>] [-i <heap id>] [-s <size in bytes>]\n",
|
||||
argv[0]);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt, ret, status, heapid;
|
||||
int sockfd, client_fd, shared_fd;
|
||||
unsigned char *map_buf;
|
||||
unsigned long map_len, heap_type, heap_size, flags;
|
||||
struct ion_buffer_info info;
|
||||
struct socket_info skinfo;
|
||||
|
||||
if (argc < 2) {
|
||||
print_usage(argc, argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
heap_size = 0;
|
||||
flags = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "hi:s:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
print_usage(argc, argv);
|
||||
exit(0);
|
||||
break;
|
||||
case 'i':
|
||||
heapid = atoi(optarg);
|
||||
switch (heapid) {
|
||||
case 0:
|
||||
heap_type = ION_HEAP_TYPE_SYSTEM;
|
||||
break;
|
||||
case 1:
|
||||
heap_type = ION_HEAP_TYPE_SYSTEM_CONTIG;
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: heap type not supported\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
heap_size = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
print_usage(argc, argv);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (heap_size <= 0) {
|
||||
printf("heap_size cannot be 0\n");
|
||||
print_usage(argc, argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("heap_type: %ld, heap_size: %ld\n", heap_type, heap_size);
|
||||
info.heap_type = heap_type;
|
||||
info.heap_size = heap_size;
|
||||
info.flag_type = flags;
|
||||
|
||||
/* This is server: open the socket connection first */
|
||||
/* Here; 1 indicates server or exporter */
|
||||
status = opensocket(&sockfd, SOCKET_NAME, 1);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "<%s>: Failed opensocket.\n", __func__);
|
||||
goto err_socket;
|
||||
}
|
||||
skinfo.sockfd = sockfd;
|
||||
|
||||
ret = ion_export_buffer_fd(&info);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "FAILED: ion_get_buffer_fd\n");
|
||||
goto err_export;
|
||||
}
|
||||
client_fd = info.ionfd;
|
||||
shared_fd = info.buffd;
|
||||
map_buf = info.buffer;
|
||||
map_len = info.buflen;
|
||||
write_buffer(map_buf, map_len);
|
||||
|
||||
/* share ion buf fd with other user process */
|
||||
printf("Sharing fd: %d, Client fd: %d\n", shared_fd, client_fd);
|
||||
skinfo.datafd = shared_fd;
|
||||
skinfo.buflen = map_len;
|
||||
|
||||
ret = socket_send_fd(&skinfo);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "FAILED: socket_send_fd\n");
|
||||
goto err_send;
|
||||
}
|
||||
|
||||
err_send:
|
||||
err_export:
|
||||
ion_close_buffer_fd(&info);
|
||||
|
||||
err_socket:
|
||||
closesocket(sockfd, SOCKET_NAME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
88
tools/testing/selftests/android/ion/ionapp_import.c
Normal file
88
tools/testing/selftests/android/ion/ionapp_import.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* ionapp_import.c
|
||||
*
|
||||
* It is a user space utility to receive android ion memory buffer fd
|
||||
* over unix domain socket IPC that can be exported by ionapp_export.
|
||||
* This acts like a client for ionapp_export.
|
||||
*
|
||||
* Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "ionutils.h"
|
||||
#include "ipcsocket.h"
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret, status;
|
||||
int sockfd, shared_fd;
|
||||
unsigned char *map_buf;
|
||||
unsigned long map_len;
|
||||
struct ion_buffer_info info;
|
||||
struct socket_info skinfo;
|
||||
|
||||
/* This is the client part. Here 0 means client or importer */
|
||||
status = opensocket(&sockfd, SOCKET_NAME, 0);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "No exporter exists...\n");
|
||||
ret = status;
|
||||
goto err_socket;
|
||||
}
|
||||
|
||||
skinfo.sockfd = sockfd;
|
||||
|
||||
ret = socket_receive_fd(&skinfo);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed: socket_receive_fd\n");
|
||||
goto err_recv;
|
||||
}
|
||||
|
||||
shared_fd = skinfo.datafd;
|
||||
printf("Received buffer fd: %d\n", shared_fd);
|
||||
if (shared_fd <= 0) {
|
||||
fprintf(stderr, "ERROR: improper buf fd\n");
|
||||
ret = -1;
|
||||
goto err_fd;
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.buffd = shared_fd;
|
||||
info.buflen = ION_BUFFER_LEN;
|
||||
|
||||
ret = ion_import_buffer_fd(&info);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed: ion_use_buffer_fd\n");
|
||||
goto err_import;
|
||||
}
|
||||
|
||||
map_buf = info.buffer;
|
||||
map_len = info.buflen;
|
||||
read_buffer(map_buf, map_len);
|
||||
|
||||
/* Write probably new data to the same buffer again */
|
||||
map_len = ION_BUFFER_LEN;
|
||||
write_buffer(map_buf, map_len);
|
||||
|
||||
err_import:
|
||||
ion_close_buffer_fd(&info);
|
||||
err_fd:
|
||||
err_recv:
|
||||
err_socket:
|
||||
closesocket(sockfd, SOCKET_NAME);
|
||||
|
||||
return ret;
|
||||
}
|
||||
259
tools/testing/selftests/android/ion/ionutils.c
Normal file
259
tools/testing/selftests/android/ion/ionutils.c
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
//#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include "ionutils.h"
|
||||
#include "ipcsocket.h"
|
||||
|
||||
|
||||
void write_buffer(void *buffer, unsigned long len)
|
||||
{
|
||||
int i;
|
||||
unsigned char *ptr = (unsigned char *)buffer;
|
||||
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "<%s>: Invalid buffer...\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Fill buffer content:\n");
|
||||
memset(ptr, 0xfd, len);
|
||||
for (i = 0; i < len; i++)
|
||||
printf("0x%x ", ptr[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void read_buffer(void *buffer, unsigned long len)
|
||||
{
|
||||
int i;
|
||||
unsigned char *ptr = (unsigned char *)buffer;
|
||||
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "<%s>: Invalid buffer...\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Read buffer content:\n");
|
||||
for (i = 0; i < len; i++)
|
||||
printf("0x%x ", ptr[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int ion_export_buffer_fd(struct ion_buffer_info *ion_info)
|
||||
{
|
||||
int i, ret, ionfd, buffer_fd;
|
||||
unsigned int heap_id;
|
||||
unsigned long maplen;
|
||||
unsigned char *map_buffer;
|
||||
struct ion_allocation_data alloc_data;
|
||||
struct ion_heap_query query;
|
||||
struct ion_heap_data heap_data[MAX_HEAP_COUNT];
|
||||
|
||||
if (!ion_info) {
|
||||
fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create an ION client */
|
||||
ionfd = open(ION_DEVICE, O_RDWR);
|
||||
if (ionfd < 0) {
|
||||
fprintf(stderr, "<%s>: Failed to open ion client: %s\n",
|
||||
__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&query, 0, sizeof(query));
|
||||
query.cnt = MAX_HEAP_COUNT;
|
||||
query.heaps = (unsigned long int)&heap_data[0];
|
||||
/* Query ION heap_id_mask from ION heap */
|
||||
ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n",
|
||||
__func__, strerror(errno));
|
||||
goto err_query;
|
||||
}
|
||||
|
||||
heap_id = MAX_HEAP_COUNT + 1;
|
||||
for (i = 0; i < query.cnt; i++) {
|
||||
if (heap_data[i].type == ion_info->heap_type) {
|
||||
printf("--------------------------------------\n");
|
||||
printf("heap type: %d\n", heap_data[i].type);
|
||||
printf(" heap id: %d\n", heap_data[i].heap_id);
|
||||
printf("heap name: %s\n", heap_data[i].name);
|
||||
printf("--------------------------------------\n");
|
||||
heap_id = heap_data[i].heap_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (heap_id > MAX_HEAP_COUNT) {
|
||||
fprintf(stderr, "<%s>: ERROR: heap type does not exists\n",
|
||||
__func__);
|
||||
goto err_heap;
|
||||
}
|
||||
|
||||
alloc_data.len = ion_info->heap_size;
|
||||
alloc_data.heap_id_mask = 1 << heap_id;
|
||||
alloc_data.flags = ion_info->flag_type;
|
||||
|
||||
/* Allocate memory for this ION client as per heap_type */
|
||||
ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n",
|
||||
__func__, strerror(errno));
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
/* This will return a valid buffer fd */
|
||||
buffer_fd = alloc_data.fd;
|
||||
maplen = alloc_data.len;
|
||||
|
||||
if (buffer_fd < 0 || maplen <= 0) {
|
||||
fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
|
||||
__func__, buffer_fd, maplen);
|
||||
goto err_fd_data;
|
||||
}
|
||||
|
||||
/* Create memory mapped buffer for the buffer fd */
|
||||
map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, buffer_fd, 0);
|
||||
if (map_buffer == MAP_FAILED) {
|
||||
fprintf(stderr, "<%s>: Failed: mmap: %s\n",
|
||||
__func__, strerror(errno));
|
||||
goto err_mmap;
|
||||
}
|
||||
|
||||
ion_info->ionfd = ionfd;
|
||||
ion_info->buffd = buffer_fd;
|
||||
ion_info->buffer = map_buffer;
|
||||
ion_info->buflen = maplen;
|
||||
|
||||
return 0;
|
||||
|
||||
munmap(map_buffer, maplen);
|
||||
|
||||
err_fd_data:
|
||||
err_mmap:
|
||||
/* in case of error: close the buffer fd */
|
||||
if (buffer_fd)
|
||||
close(buffer_fd);
|
||||
|
||||
err_query:
|
||||
err_heap:
|
||||
err_alloc:
|
||||
/* In case of error: close the ion client fd */
|
||||
if (ionfd)
|
||||
close(ionfd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ion_import_buffer_fd(struct ion_buffer_info *ion_info)
|
||||
{
|
||||
int buffd;
|
||||
unsigned char *map_buf;
|
||||
unsigned long map_len;
|
||||
|
||||
if (!ion_info) {
|
||||
fprintf(stderr, "<%s>: Invalid ion info\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
map_len = ion_info->buflen;
|
||||
buffd = ion_info->buffd;
|
||||
|
||||
if (buffd < 0 || map_len <= 0) {
|
||||
fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n",
|
||||
__func__, buffd, map_len);
|
||||
goto err_buffd;
|
||||
}
|
||||
|
||||
map_buf = (unsigned char *)mmap(NULL, map_len, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, buffd, 0);
|
||||
if (map_buf == MAP_FAILED) {
|
||||
printf("<%s>: Failed - mmap: %s\n",
|
||||
__func__, strerror(errno));
|
||||
goto err_mmap;
|
||||
}
|
||||
|
||||
ion_info->buffer = map_buf;
|
||||
ion_info->buflen = map_len;
|
||||
|
||||
return 0;
|
||||
|
||||
err_mmap:
|
||||
if (buffd)
|
||||
close(buffd);
|
||||
|
||||
err_buffd:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ion_close_buffer_fd(struct ion_buffer_info *ion_info)
|
||||
{
|
||||
if (ion_info) {
|
||||
/* unmap the buffer properly in the end */
|
||||
munmap(ion_info->buffer, ion_info->buflen);
|
||||
/* close the buffer fd */
|
||||
if (ion_info->buffd > 0)
|
||||
close(ion_info->buffd);
|
||||
/* Finally, close the client fd */
|
||||
if (ion_info->ionfd > 0)
|
||||
close(ion_info->ionfd);
|
||||
printf("<%s>: buffer release successfully....\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
int socket_send_fd(struct socket_info *info)
|
||||
{
|
||||
int status;
|
||||
int fd, sockfd;
|
||||
struct socketdata skdata;
|
||||
|
||||
if (!info) {
|
||||
fprintf(stderr, "<%s>: Invalid socket info\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockfd = info->sockfd;
|
||||
fd = info->datafd;
|
||||
memset(&skdata, 0, sizeof(skdata));
|
||||
skdata.data = fd;
|
||||
skdata.len = sizeof(skdata.data);
|
||||
status = sendtosocket(sockfd, &skdata);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "<%s>: Failed: sendtosocket\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_receive_fd(struct socket_info *info)
|
||||
{
|
||||
int status;
|
||||
int fd, sockfd;
|
||||
struct socketdata skdata;
|
||||
|
||||
if (!info) {
|
||||
fprintf(stderr, "<%s>: Invalid socket info\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockfd = info->sockfd;
|
||||
memset(&skdata, 0, sizeof(skdata));
|
||||
status = receivefromsocket(sockfd, &skdata);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "<%s>: Failed: receivefromsocket\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = (int)skdata.data;
|
||||
info->datafd = fd;
|
||||
|
||||
return status;
|
||||
}
|
||||
55
tools/testing/selftests/android/ion/ionutils.h
Normal file
55
tools/testing/selftests/android/ion/ionutils.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef __ION_UTILS_H
|
||||
#define __ION_UTILS_H
|
||||
|
||||
#include "ion.h"
|
||||
|
||||
#define SOCKET_NAME "ion_socket"
|
||||
#define ION_DEVICE "/dev/ion"
|
||||
|
||||
#define ION_BUFFER_LEN 4096
|
||||
#define MAX_HEAP_COUNT ION_HEAP_TYPE_CUSTOM
|
||||
|
||||
struct socket_info {
|
||||
int sockfd;
|
||||
int datafd;
|
||||
unsigned long buflen;
|
||||
};
|
||||
|
||||
struct ion_buffer_info {
|
||||
int ionfd;
|
||||
int buffd;
|
||||
unsigned int heap_type;
|
||||
unsigned int flag_type;
|
||||
unsigned long heap_size;
|
||||
unsigned long buflen;
|
||||
unsigned char *buffer;
|
||||
};
|
||||
|
||||
|
||||
/* This is used to fill the data into the mapped buffer */
|
||||
void write_buffer(void *buffer, unsigned long len);
|
||||
|
||||
/* This is used to read the data from the exported buffer */
|
||||
void read_buffer(void *buffer, unsigned long len);
|
||||
|
||||
/* This is used to create an ION buffer FD for the kernel buffer
|
||||
* So you can export this same buffer to others in the form of FD
|
||||
*/
|
||||
int ion_export_buffer_fd(struct ion_buffer_info *ion_info);
|
||||
|
||||
/* This is used to import or map an exported FD.
|
||||
* So we point to same buffer without making a copy. Hence zero-copy.
|
||||
*/
|
||||
int ion_import_buffer_fd(struct ion_buffer_info *ion_info);
|
||||
|
||||
/* This is used to close all references for the ION client */
|
||||
void ion_close_buffer_fd(struct ion_buffer_info *ion_info);
|
||||
|
||||
/* This is used to send FD to another process using socket IPC */
|
||||
int socket_send_fd(struct socket_info *skinfo);
|
||||
|
||||
/* This is used to receive FD from another process using socket IPC */
|
||||
int socket_receive_fd(struct socket_info *skinfo);
|
||||
|
||||
|
||||
#endif
|
||||
227
tools/testing/selftests/android/ion/ipcsocket.c
Normal file
227
tools/testing/selftests/android/ion/ipcsocket.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ipcsocket.h"
|
||||
|
||||
|
||||
int opensocket(int *sockfd, const char *name, int connecttype)
|
||||
{
|
||||
int ret, temp = 1;
|
||||
|
||||
if (!name || strlen(name) > MAX_SOCK_NAME_LEN) {
|
||||
fprintf(stderr, "<%s>: Invalid socket name.\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = socket(PF_LOCAL, SOCK_STREAM, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed socket: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
*sockfd = ret;
|
||||
if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *)&temp, sizeof(int)) < 0) {
|
||||
fprintf(stderr, "<%s>: Failed setsockopt: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
sprintf(sock_name, "/tmp/%s", name);
|
||||
|
||||
if (connecttype == 1) {
|
||||
/* This is for Server connection */
|
||||
struct sockaddr_un skaddr;
|
||||
int clientfd;
|
||||
socklen_t sklen;
|
||||
|
||||
unlink(sock_name);
|
||||
memset(&skaddr, 0, sizeof(skaddr));
|
||||
skaddr.sun_family = AF_LOCAL;
|
||||
strcpy(skaddr.sun_path, sock_name);
|
||||
|
||||
ret = bind(*sockfd, (struct sockaddr *)&skaddr,
|
||||
SUN_LEN(&skaddr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed bind: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = listen(*sockfd, 5);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed listen: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(&skaddr, 0, sizeof(skaddr));
|
||||
sklen = sizeof(skaddr);
|
||||
|
||||
ret = accept(*sockfd, (struct sockaddr *)&skaddr,
|
||||
(socklen_t *)&sklen);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed accept: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
clientfd = ret;
|
||||
*sockfd = clientfd;
|
||||
} else {
|
||||
/* This is for client connection */
|
||||
struct sockaddr_un skaddr;
|
||||
|
||||
memset(&skaddr, 0, sizeof(skaddr));
|
||||
skaddr.sun_family = AF_LOCAL;
|
||||
strcpy(skaddr.sun_path, sock_name);
|
||||
|
||||
ret = connect(*sockfd, (struct sockaddr *)&skaddr,
|
||||
SUN_LEN(&skaddr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed connect: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (*sockfd)
|
||||
close(*sockfd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sendtosocket(int sockfd, struct socketdata *skdata)
|
||||
{
|
||||
int ret, buffd;
|
||||
unsigned int len;
|
||||
char cmsg_b[CMSG_SPACE(sizeof(int))];
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msgh;
|
||||
struct iovec iov;
|
||||
struct timeval timeout;
|
||||
fd_set selFDs;
|
||||
|
||||
if (!skdata) {
|
||||
fprintf(stderr, "<%s>: socketdata is NULL\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FD_ZERO(&selFDs);
|
||||
FD_SET(0, &selFDs);
|
||||
FD_SET(sockfd, &selFDs);
|
||||
timeout.tv_sec = 20;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
ret = select(sockfd+1, NULL, &selFDs, NULL, &timeout);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed select: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(sockfd, &selFDs)) {
|
||||
buffd = skdata->data;
|
||||
len = skdata->len;
|
||||
memset(&msgh, 0, sizeof(msgh));
|
||||
msgh.msg_control = &cmsg_b;
|
||||
msgh.msg_controllen = CMSG_LEN(len);
|
||||
iov.iov_base = "OK";
|
||||
iov.iov_len = 2;
|
||||
msgh.msg_iov = &iov;
|
||||
msgh.msg_iovlen = 1;
|
||||
cmsg = CMSG_FIRSTHDR(&msgh);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(len);
|
||||
memcpy(CMSG_DATA(cmsg), &buffd, len);
|
||||
|
||||
ret = sendmsg(sockfd, &msgh, MSG_DONTWAIT);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed sendmsg: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int receivefromsocket(int sockfd, struct socketdata *skdata)
|
||||
{
|
||||
int ret, buffd;
|
||||
unsigned int len = 0;
|
||||
char cmsg_b[CMSG_SPACE(sizeof(int))];
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msgh;
|
||||
struct iovec iov;
|
||||
fd_set recvFDs;
|
||||
char data[32];
|
||||
|
||||
if (!skdata) {
|
||||
fprintf(stderr, "<%s>: socketdata is NULL\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FD_ZERO(&recvFDs);
|
||||
FD_SET(0, &recvFDs);
|
||||
FD_SET(sockfd, &recvFDs);
|
||||
|
||||
ret = select(sockfd+1, &recvFDs, NULL, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed select: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(sockfd, &recvFDs)) {
|
||||
len = sizeof(buffd);
|
||||
memset(&msgh, 0, sizeof(msgh));
|
||||
msgh.msg_control = &cmsg_b;
|
||||
msgh.msg_controllen = CMSG_LEN(len);
|
||||
iov.iov_base = data;
|
||||
iov.iov_len = sizeof(data)-1;
|
||||
msgh.msg_iov = &iov;
|
||||
msgh.msg_iovlen = 1;
|
||||
cmsg = CMSG_FIRSTHDR(&msgh);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(len);
|
||||
|
||||
ret = recvmsg(sockfd, &msgh, MSG_DONTWAIT);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "<%s>: Failed recvmsg: <%s>\n",
|
||||
__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&buffd, CMSG_DATA(cmsg), len);
|
||||
skdata->data = buffd;
|
||||
skdata->len = len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int closesocket(int sockfd, char *name)
|
||||
{
|
||||
char sockname[MAX_SOCK_NAME_LEN];
|
||||
|
||||
if (sockfd)
|
||||
close(sockfd);
|
||||
sprintf(sockname, "/tmp/%s", name);
|
||||
unlink(sockname);
|
||||
shutdown(sockfd, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
35
tools/testing/selftests/android/ion/ipcsocket.h
Normal file
35
tools/testing/selftests/android/ion/ipcsocket.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
#ifndef _IPCSOCKET_H
|
||||
#define _IPCSOCKET_H
|
||||
|
||||
|
||||
#define MAX_SOCK_NAME_LEN 64
|
||||
|
||||
char sock_name[MAX_SOCK_NAME_LEN];
|
||||
|
||||
/* This structure is responsible for holding the IPC data
|
||||
* data: hold the buffer fd
|
||||
* len: just the length of 32-bit integer fd
|
||||
*/
|
||||
struct socketdata {
|
||||
int data;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
/* This API is used to open the IPC socket connection
|
||||
* name: implies a unique socket name in the system
|
||||
* connecttype: implies server(0) or client(1)
|
||||
*/
|
||||
int opensocket(int *sockfd, const char *name, int connecttype);
|
||||
|
||||
/* This is the API to send socket data over IPC socket */
|
||||
int sendtosocket(int sockfd, struct socketdata *data);
|
||||
|
||||
/* This is the API to receive socket data over IPC socket */
|
||||
int receivefromsocket(int sockfd, struct socketdata *data);
|
||||
|
||||
/* This is the API to close the socket connection */
|
||||
int closesocket(int sockfd, char *name);
|
||||
|
||||
|
||||
#endif
|
||||
3
tools/testing/selftests/android/run.sh
Executable file
3
tools/testing/selftests/android/run.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
(cd ion; ./ion_test.sh)
|
||||
|
|
@ -13,16 +13,17 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i
|
|||
LDLIBS += -lcap -lelf
|
||||
|
||||
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
|
||||
test_align
|
||||
test_align test_verifier_log test_dev_cgroup
|
||||
|
||||
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 sockmap_parse_prog.o sockmap_verdict_prog.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
|
||||
|
||||
TEST_PROGS := test_kmod.sh test_xdp_redirect.sh
|
||||
TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
BPFOBJ := $(OUTPUT)/libbpf.a
|
||||
BPFOBJ := $(OUTPUT)/libbpf.a $(OUTPUT)/cgroup_helpers.c
|
||||
|
||||
$(TEST_GEN_PROGS): $(BPFOBJ)
|
||||
|
||||
|
|
@ -35,8 +36,20 @@ $(BPFOBJ): force
|
|||
$(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/
|
||||
|
||||
CLANG ?= clang
|
||||
LLC ?= llc
|
||||
|
||||
PROBE := $(shell llc -march=bpf -mcpu=probe -filetype=null /dev/null 2>&1)
|
||||
|
||||
# Let newer LLVM versions transparently probe the kernel for availability
|
||||
# of full BPF instruction set.
|
||||
ifeq ($(PROBE),)
|
||||
CPU ?= probe
|
||||
else
|
||||
CPU ?= generic
|
||||
endif
|
||||
|
||||
%.o: %.c
|
||||
$(CLANG) -I. -I./include/uapi -I../../../include/uapi \
|
||||
-Wno-compare-distinct-pointer-types \
|
||||
-O2 -target bpf -c $< -o $@
|
||||
-Wno-compare-distinct-pointer-types \
|
||||
-O2 -target bpf -emit-llvm -c $< -o - | \
|
||||
$(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@
|
||||
|
|
|
|||
|
|
@ -63,14 +63,25 @@ static unsigned long long (*bpf_get_prandom_u32)(void) =
|
|||
(void *) BPF_FUNC_get_prandom_u32;
|
||||
static int (*bpf_xdp_adjust_head)(void *ctx, int offset) =
|
||||
(void *) BPF_FUNC_xdp_adjust_head;
|
||||
static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) =
|
||||
(void *) BPF_FUNC_xdp_adjust_meta;
|
||||
static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,
|
||||
int optlen) =
|
||||
(void *) BPF_FUNC_setsockopt;
|
||||
static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval,
|
||||
int optlen) =
|
||||
(void *) BPF_FUNC_getsockopt;
|
||||
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,
|
||||
unsigned long long flags) =
|
||||
(void *) BPF_FUNC_sock_map_update;
|
||||
static int (*bpf_perf_event_read_value)(void *map, unsigned long long flags,
|
||||
void *buf, unsigned int buf_size) =
|
||||
(void *) BPF_FUNC_perf_event_read_value;
|
||||
static int (*bpf_perf_prog_read_value)(void *ctx, void *buf,
|
||||
unsigned int buf_size) =
|
||||
(void *) BPF_FUNC_perf_prog_read_value;
|
||||
|
||||
|
||||
/* llvm builtin functions that eBPF C program may use to
|
||||
|
|
@ -110,7 +121,47 @@ static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) =
|
|||
static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
||||
(void *) BPF_FUNC_skb_change_head;
|
||||
|
||||
/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
|
||||
#if defined(__TARGET_ARCH_x86)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_s930x)
|
||||
#define bpf_target_s930x
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm64)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_mips)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_powerpc)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_sparc)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
#undef bpf_target_defined
|
||||
#endif
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
#ifndef bpf_target_defined
|
||||
#if defined(__x86_64__)
|
||||
#define bpf_target_x86
|
||||
#elif defined(__s390x__)
|
||||
#define bpf_target_s930x
|
||||
#elif defined(__aarch64__)
|
||||
#define bpf_target_arm64
|
||||
#elif defined(__mips__)
|
||||
#define bpf_target_mips
|
||||
#elif defined(__powerpc__)
|
||||
#define bpf_target_powerpc
|
||||
#elif defined(__sparc__)
|
||||
#define bpf_target_sparc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->di)
|
||||
#define PT_REGS_PARM2(x) ((x)->si)
|
||||
|
|
@ -123,7 +174,7 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->ip)
|
||||
|
||||
#elif defined(__s390x__)
|
||||
#elif defined(bpf_target_s390x)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->gprs[2])
|
||||
#define PT_REGS_PARM2(x) ((x)->gprs[3])
|
||||
|
|
@ -136,7 +187,7 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
#define PT_REGS_SP(x) ((x)->gprs[15])
|
||||
#define PT_REGS_IP(x) ((x)->psw.addr)
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
#elif defined(bpf_target_arm64)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->regs[0])
|
||||
#define PT_REGS_PARM2(x) ((x)->regs[1])
|
||||
|
|
@ -149,7 +200,7 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->pc)
|
||||
|
||||
#elif defined(__mips__)
|
||||
#elif defined(bpf_target_mips)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->regs[4])
|
||||
#define PT_REGS_PARM2(x) ((x)->regs[5])
|
||||
|
|
@ -162,7 +213,7 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
#define PT_REGS_SP(x) ((x)->regs[29])
|
||||
#define PT_REGS_IP(x) ((x)->cp0_epc)
|
||||
|
||||
#elif defined(__powerpc__)
|
||||
#elif defined(bpf_target_powerpc)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->gpr[3])
|
||||
#define PT_REGS_PARM2(x) ((x)->gpr[4])
|
||||
|
|
@ -173,7 +224,7 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->nip)
|
||||
|
||||
#elif defined(__sparc__)
|
||||
#elif defined(bpf_target_sparc)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0])
|
||||
#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1])
|
||||
|
|
@ -183,6 +234,8 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7])
|
||||
#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0])
|
||||
#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP])
|
||||
|
||||
/* Should this also be a bpf_target check for the sparc case? */
|
||||
#if defined(__arch64__)
|
||||
#define PT_REGS_IP(x) ((x)->tpc)
|
||||
#else
|
||||
|
|
@ -191,10 +244,10 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) =
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
#ifdef bpf_target_powerpc
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
#elif defined(__sparc__)
|
||||
#elif bpf_target_sparc
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
#else
|
||||
|
|
|
|||
178
tools/testing/selftests/bpf/cgroup_helpers.c
Normal file
178
tools/testing/selftests/bpf/cgroup_helpers.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdio.h>
|
||||
#include <linux/sched.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <ftw.h>
|
||||
|
||||
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
/*
|
||||
* To avoid relying on the system setup, when setup_cgroup_env is called
|
||||
* we create a new mount namespace, and cgroup namespace. The cgroup2
|
||||
* root is mounted at CGROUP_MOUNT_PATH
|
||||
*
|
||||
* Unfortunately, most people don't have cgroupv2 enabled at this point in time.
|
||||
* It's easier to create our own mount namespace and manage it ourselves.
|
||||
*
|
||||
* We assume /mnt exists.
|
||||
*/
|
||||
|
||||
#define WALK_FD_LIMIT 16
|
||||
#define CGROUP_MOUNT_PATH "/mnt"
|
||||
#define CGROUP_WORK_DIR "/cgroup-test-work-dir"
|
||||
#define format_cgroup_path(buf, path) \
|
||||
snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \
|
||||
CGROUP_WORK_DIR, path)
|
||||
|
||||
/**
|
||||
* setup_cgroup_environment() - Setup the cgroup environment
|
||||
*
|
||||
* After calling this function, cleanup_cgroup_environment should be called
|
||||
* once testing is complete.
|
||||
*
|
||||
* This function will print an error to stderr and return 1 if it is unable
|
||||
* to setup the cgroup environment. If setup is successful, 0 is returned.
|
||||
*/
|
||||
int setup_cgroup_environment(void)
|
||||
{
|
||||
char cgroup_workdir[PATH_MAX + 1];
|
||||
|
||||
format_cgroup_path(cgroup_workdir, "");
|
||||
|
||||
if (unshare(CLONE_NEWNS)) {
|
||||
log_err("unshare");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
|
||||
log_err("mount fakeroot");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL) && errno != EBUSY) {
|
||||
log_err("mount cgroup2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Cleanup existing failed runs, now that the environment is setup */
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) {
|
||||
log_err("mkdir cgroup work dir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nftwfunc(const char *filename, const struct stat *statptr,
|
||||
int fileflags, struct FTW *pfwt)
|
||||
{
|
||||
if ((fileflags & FTW_D) && rmdir(filename))
|
||||
log_err("Removing cgroup: %s", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int join_cgroup_from_top(char *cgroup_path)
|
||||
{
|
||||
char cgroup_procs_path[PATH_MAX + 1];
|
||||
pid_t pid = getpid();
|
||||
int fd, rc = 0;
|
||||
|
||||
snprintf(cgroup_procs_path, sizeof(cgroup_procs_path),
|
||||
"%s/cgroup.procs", cgroup_path);
|
||||
|
||||
fd = open(cgroup_procs_path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
log_err("Opening Cgroup Procs: %s", cgroup_procs_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (dprintf(fd, "%d\n", pid) < 0) {
|
||||
log_err("Joining Cgroup");
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* join_cgroup() - Join a cgroup
|
||||
* @path: The cgroup path, relative to the workdir, to join
|
||||
*
|
||||
* This function expects a cgroup to already be created, relative to the cgroup
|
||||
* work dir, and it joins it. For example, passing "/my-cgroup" as the path
|
||||
* would actually put the calling process into the cgroup
|
||||
* "/cgroup-test-work-dir/my-cgroup"
|
||||
*
|
||||
* On success, it returns 0, otherwise on failure it returns 1.
|
||||
*/
|
||||
int join_cgroup(char *path)
|
||||
{
|
||||
char cgroup_path[PATH_MAX + 1];
|
||||
|
||||
format_cgroup_path(cgroup_path, path);
|
||||
return join_cgroup_from_top(cgroup_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment
|
||||
*
|
||||
* This is an idempotent function to delete all temporary cgroups that
|
||||
* have been created during the test, including the cgroup testing work
|
||||
* directory.
|
||||
*
|
||||
* At call time, it moves the calling process to the root cgroup, and then
|
||||
* runs the deletion process. It is idempotent, and should not fail, unless
|
||||
* a process is lingering.
|
||||
*
|
||||
* On failure, it will print an error to stderr, and try to continue.
|
||||
*/
|
||||
void cleanup_cgroup_environment(void)
|
||||
{
|
||||
char cgroup_workdir[PATH_MAX + 1];
|
||||
|
||||
format_cgroup_path(cgroup_workdir, "");
|
||||
join_cgroup_from_top(CGROUP_MOUNT_PATH);
|
||||
nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD
|
||||
* @path: The cgroup path, relative to the workdir, to join
|
||||
*
|
||||
* This function creates a cgroup under the top level workdir and returns the
|
||||
* file descriptor. It is idempotent.
|
||||
*
|
||||
* On success, it returns the file descriptor. On failure it returns 0.
|
||||
* If there is a failure, it prints the error to stderr.
|
||||
*/
|
||||
int create_and_get_cgroup(char *path)
|
||||
{
|
||||
char cgroup_path[PATH_MAX + 1];
|
||||
int fd;
|
||||
|
||||
format_cgroup_path(cgroup_path, path);
|
||||
if (mkdir(cgroup_path, 0777) && errno != EEXIST) {
|
||||
log_err("mkdiring cgroup %s .. %s", path, cgroup_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(cgroup_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
log_err("Opening Cgroup");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
17
tools/testing/selftests/bpf/cgroup_helpers.h
Normal file
17
tools/testing/selftests/bpf/cgroup_helpers.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __CGROUP_HELPERS_H
|
||||
#define __CGROUP_HELPERS_H
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
|
||||
#define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
|
||||
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
|
||||
|
||||
|
||||
int create_and_get_cgroup(char *path);
|
||||
int join_cgroup(char *path);
|
||||
int setup_cgroup_environment(void);
|
||||
void cleanup_cgroup_environment(void);
|
||||
|
||||
#endif
|
||||
60
tools/testing/selftests/bpf/dev_cgroup.c
Normal file
60
tools/testing/selftests/bpf/dev_cgroup.c
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* Copyright (c) 2017 Facebook
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/version.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
SEC("cgroup/dev")
|
||||
int bpf_prog1(struct bpf_cgroup_dev_ctx *ctx)
|
||||
{
|
||||
short type = ctx->access_type & 0xFFFF;
|
||||
#ifdef DEBUG
|
||||
short access = ctx->access_type >> 16;
|
||||
char fmt[] = " %d:%d \n";
|
||||
|
||||
switch (type) {
|
||||
case BPF_DEVCG_DEV_BLOCK:
|
||||
fmt[0] = 'b';
|
||||
break;
|
||||
case BPF_DEVCG_DEV_CHAR:
|
||||
fmt[0] = 'c';
|
||||
break;
|
||||
default:
|
||||
fmt[0] = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
if (access & BPF_DEVCG_ACC_READ)
|
||||
fmt[8] = 'r';
|
||||
|
||||
if (access & BPF_DEVCG_ACC_WRITE)
|
||||
fmt[9] = 'w';
|
||||
|
||||
if (access & BPF_DEVCG_ACC_MKNOD)
|
||||
fmt[10] = 'm';
|
||||
|
||||
bpf_trace_printk(fmt, sizeof(fmt), ctx->major, ctx->minor);
|
||||
#endif
|
||||
|
||||
/* Allow access to /dev/zero and /dev/random.
|
||||
* Forbid everything else.
|
||||
*/
|
||||
if (ctx->major != 1 || type != BPF_DEVCG_DEV_CHAR)
|
||||
return 0;
|
||||
|
||||
switch (ctx->minor) {
|
||||
case 5: /* 1:5 /dev/zero */
|
||||
case 9: /* 1:9 /dev/urandom */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
__u32 _version SEC("version") = LINUX_VERSION_CODE;
|
||||
|
|
@ -29,9 +29,6 @@ int bpf_prog1(struct __sk_buff *skb)
|
|||
* fields.
|
||||
*/
|
||||
d[7] = 1;
|
||||
|
||||
bpf_printk("parse: data[0] = (%u): local_port %i remote %i\n",
|
||||
d[0], lport, bpf_ntohl(rport));
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,6 @@ int bpf_prog2(struct __sk_buff *skb)
|
|||
d[6] = 0xe;
|
||||
d[7] = 0xf;
|
||||
|
||||
bpf_printk("verdict: data[0] = redir(%u:%u)\n", map, sk);
|
||||
|
||||
if (!map)
|
||||
return bpf_sk_redirect_map(skb, &sock_map_rx, sk, 0);
|
||||
return bpf_sk_redirect_map(skb, &sock_map_tx, sk, 0);
|
||||
|
|
|
|||
93
tools/testing/selftests/bpf/test_dev_cgroup.c
Normal file
93
tools/testing/selftests/bpf/test_dev_cgroup.c
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* Copyright (c) 2017 Facebook
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define DEV_CGROUP_PROG "./dev_cgroup.o"
|
||||
|
||||
#define TEST_CGROUP "test-bpf-based-device-cgroup/"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bpf_object *obj;
|
||||
int error = EXIT_FAILURE;
|
||||
int prog_fd, cgroup_fd;
|
||||
__u32 prog_cnt;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (setup_cgroup_environment()) {
|
||||
printf("Failed to load DEV_CGROUP program\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Create a cgroup, get fd, and join it */
|
||||
cgroup_fd = create_and_get_cgroup(TEST_CGROUP);
|
||||
if (!cgroup_fd) {
|
||||
printf("Failed to create test cgroup\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (join_cgroup(TEST_CGROUP)) {
|
||||
printf("Failed to join cgroup\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Attach bpf program */
|
||||
if (bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE, 0)) {
|
||||
printf("Failed to attach DEV_CGROUP program");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bpf_prog_query(cgroup_fd, BPF_CGROUP_DEVICE, 0, NULL, NULL,
|
||||
&prog_cnt)) {
|
||||
printf("Failed to query attached programs");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* All operations with /dev/zero and and /dev/urandom are allowed,
|
||||
* everything else is forbidden.
|
||||
*/
|
||||
assert(system("rm -f /tmp/test_dev_cgroup_null") == 0);
|
||||
assert(system("mknod /tmp/test_dev_cgroup_null c 1 3"));
|
||||
assert(system("rm -f /tmp/test_dev_cgroup_null") == 0);
|
||||
|
||||
/* /dev/zero is whitelisted */
|
||||
assert(system("rm -f /tmp/test_dev_cgroup_zero") == 0);
|
||||
assert(system("mknod /tmp/test_dev_cgroup_zero c 1 5") == 0);
|
||||
assert(system("rm -f /tmp/test_dev_cgroup_zero") == 0);
|
||||
|
||||
assert(system("dd if=/dev/urandom of=/dev/zero count=64") == 0);
|
||||
|
||||
/* src is allowed, target is forbidden */
|
||||
assert(system("dd if=/dev/urandom of=/dev/full count=64"));
|
||||
|
||||
/* src is forbidden, target is allowed */
|
||||
assert(system("dd if=/dev/random of=/dev/zero count=64"));
|
||||
|
||||
error = 0;
|
||||
printf("test_dev_cgroup:PASS\n");
|
||||
|
||||
err:
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
@ -32,6 +32,10 @@ struct tlpm_node {
|
|||
uint8_t key[];
|
||||
};
|
||||
|
||||
static struct tlpm_node *tlpm_match(struct tlpm_node *list,
|
||||
const uint8_t *key,
|
||||
size_t n_bits);
|
||||
|
||||
static struct tlpm_node *tlpm_add(struct tlpm_node *list,
|
||||
const uint8_t *key,
|
||||
size_t n_bits)
|
||||
|
|
@ -39,9 +43,17 @@ static struct tlpm_node *tlpm_add(struct tlpm_node *list,
|
|||
struct tlpm_node *node;
|
||||
size_t n;
|
||||
|
||||
n = (n_bits + 7) / 8;
|
||||
|
||||
/* 'overwrite' an equivalent entry if one already exists */
|
||||
node = tlpm_match(list, key, n_bits);
|
||||
if (node && node->n_bits == n_bits) {
|
||||
memcpy(node->key, key, n);
|
||||
return list;
|
||||
}
|
||||
|
||||
/* add new entry with @key/@n_bits to @list and return new head */
|
||||
|
||||
n = (n_bits + 7) / 8;
|
||||
node = malloc(sizeof(*node) + n);
|
||||
assert(node);
|
||||
|
||||
|
|
@ -93,6 +105,34 @@ static struct tlpm_node *tlpm_match(struct tlpm_node *list,
|
|||
return best;
|
||||
}
|
||||
|
||||
static struct tlpm_node *tlpm_delete(struct tlpm_node *list,
|
||||
const uint8_t *key,
|
||||
size_t n_bits)
|
||||
{
|
||||
struct tlpm_node *best = tlpm_match(list, key, n_bits);
|
||||
struct tlpm_node *node;
|
||||
|
||||
if (!best || best->n_bits != n_bits)
|
||||
return list;
|
||||
|
||||
if (best == list) {
|
||||
node = best->next;
|
||||
free(best);
|
||||
return node;
|
||||
}
|
||||
|
||||
for (node = list; node; node = node->next) {
|
||||
if (node->next == best) {
|
||||
node->next = best->next;
|
||||
free(best);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
/* should never get here */
|
||||
assert(0);
|
||||
return list;
|
||||
}
|
||||
|
||||
static void test_lpm_basic(void)
|
||||
{
|
||||
struct tlpm_node *list = NULL, *t1, *t2;
|
||||
|
|
@ -115,6 +155,13 @@ static void test_lpm_basic(void)
|
|||
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 15));
|
||||
assert(!tlpm_match(list, (uint8_t[]){ 0x7f, 0xff }, 16));
|
||||
|
||||
list = tlpm_delete(list, (uint8_t[]){ 0xff, 0xff }, 16);
|
||||
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8));
|
||||
assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16));
|
||||
|
||||
list = tlpm_delete(list, (uint8_t[]){ 0xff }, 8);
|
||||
assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 8));
|
||||
|
||||
tlpm_clear(list);
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +206,7 @@ static void test_lpm_order(void)
|
|||
|
||||
static void test_lpm_map(int keysize)
|
||||
{
|
||||
size_t i, j, n_matches, n_nodes, n_lookups;
|
||||
size_t i, j, n_matches, n_matches_after_delete, n_nodes, n_lookups;
|
||||
struct tlpm_node *t, *list = NULL;
|
||||
struct bpf_lpm_trie_key *key;
|
||||
uint8_t *data, *value;
|
||||
|
|
@ -171,6 +218,7 @@ static void test_lpm_map(int keysize)
|
|||
*/
|
||||
|
||||
n_matches = 0;
|
||||
n_matches_after_delete = 0;
|
||||
n_nodes = 1 << 8;
|
||||
n_lookups = 1 << 16;
|
||||
|
||||
|
|
@ -224,15 +272,54 @@ static void test_lpm_map(int keysize)
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove the first half of the elements in the tlpm and the
|
||||
* corresponding nodes from the bpf-lpm. Then run the same
|
||||
* large number of random lookups in both and make sure they match.
|
||||
* Note: we need to count the number of nodes actually inserted
|
||||
* since there may have been duplicates.
|
||||
*/
|
||||
for (i = 0, t = list; t; i++, t = t->next)
|
||||
;
|
||||
for (j = 0; j < i / 2; ++j) {
|
||||
key->prefixlen = list->n_bits;
|
||||
memcpy(key->data, list->key, keysize);
|
||||
r = bpf_map_delete_elem(map, key);
|
||||
assert(!r);
|
||||
list = tlpm_delete(list, list->key, list->n_bits);
|
||||
assert(list);
|
||||
}
|
||||
for (i = 0; i < n_lookups; ++i) {
|
||||
for (j = 0; j < keysize; ++j)
|
||||
data[j] = rand() & 0xff;
|
||||
|
||||
t = tlpm_match(list, data, 8 * keysize);
|
||||
|
||||
key->prefixlen = 8 * keysize;
|
||||
memcpy(key->data, data, keysize);
|
||||
r = bpf_map_lookup_elem(map, key, value);
|
||||
assert(!r || errno == ENOENT);
|
||||
assert(!t == !!r);
|
||||
|
||||
if (t) {
|
||||
++n_matches_after_delete;
|
||||
assert(t->n_bits == value[keysize]);
|
||||
for (j = 0; j < t->n_bits; ++j)
|
||||
assert((t->key[j / 8] & (1 << (7 - j % 8))) ==
|
||||
(value[j / 8] & (1 << (7 - j % 8))));
|
||||
}
|
||||
}
|
||||
|
||||
close(map);
|
||||
tlpm_clear(list);
|
||||
|
||||
/* With 255 random nodes in the map, we are pretty likely to match
|
||||
* something on every lookup. For statistics, use this:
|
||||
*
|
||||
* printf(" nodes: %zu\n"
|
||||
* "lookups: %zu\n"
|
||||
* "matches: %zu\n", n_nodes, n_lookups, n_matches);
|
||||
* printf(" nodes: %zu\n"
|
||||
* " lookups: %zu\n"
|
||||
* " matches: %zu\n"
|
||||
* "matches(delete): %zu\n",
|
||||
* n_nodes, n_lookups, n_matches, n_matches_after_delete);
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
@ -332,6 +419,108 @@ static void test_lpm_ipaddr(void)
|
|||
close(map_fd_ipv6);
|
||||
}
|
||||
|
||||
static void test_lpm_delete(void)
|
||||
{
|
||||
struct bpf_lpm_trie_key *key;
|
||||
size_t key_size;
|
||||
int map_fd;
|
||||
__u64 value;
|
||||
|
||||
key_size = sizeof(*key) + sizeof(__u32);
|
||||
key = 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);
|
||||
|
||||
/* Add nodes:
|
||||
* 192.168.0.0/16 (1)
|
||||
* 192.168.0.0/24 (2)
|
||||
* 192.168.128.0/24 (3)
|
||||
* 192.168.1.0/24 (4)
|
||||
*
|
||||
* (1)
|
||||
* / \
|
||||
* (IM) (3)
|
||||
* / \
|
||||
* (2) (4)
|
||||
*/
|
||||
value = 1;
|
||||
key->prefixlen = 16;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
value = 2;
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
value = 3;
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.128.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
value = 4;
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", key->data);
|
||||
assert(bpf_map_update_elem(map_fd, key, &value, 0) == 0);
|
||||
|
||||
/* remove non-existent node */
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "10.0.0.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
/* assert initial lookup */
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.0.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 2);
|
||||
|
||||
/* remove leaf node */
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.0.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 1);
|
||||
|
||||
/* remove leaf (and intermediary) node */
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.1.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 1);
|
||||
|
||||
/* remove root node */
|
||||
key->prefixlen = 16;
|
||||
inet_pton(AF_INET, "192.168.0.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.128.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == 0);
|
||||
assert(value == 3);
|
||||
|
||||
/* remove last node */
|
||||
key->prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.128.0", key->data);
|
||||
assert(bpf_map_delete_elem(map_fd, key) == 0);
|
||||
|
||||
key->prefixlen = 32;
|
||||
inet_pton(AF_INET, "192.168.128.1", key->data);
|
||||
assert(bpf_map_lookup_elem(map_fd, key, &value) == -1 &&
|
||||
errno == ENOENT);
|
||||
|
||||
close(map_fd);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
|
|
@ -354,6 +543,8 @@ int main(void)
|
|||
|
||||
test_lpm_ipaddr();
|
||||
|
||||
test_lpm_delete();
|
||||
|
||||
printf("test_lpm: OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1043,6 +1043,51 @@ static void test_map_parallel(void)
|
|||
assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT);
|
||||
}
|
||||
|
||||
static void test_map_rdonly(void)
|
||||
{
|
||||
int fd, key = 0, value = 0;
|
||||
|
||||
fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
|
||||
MAP_SIZE, map_flags | BPF_F_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("Failed to create map for read only test '%s'!\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
key = 1;
|
||||
value = 1234;
|
||||
/* Insert key=1 element. */
|
||||
assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == -1 &&
|
||||
errno == EPERM);
|
||||
|
||||
/* Check that key=2 is not found. */
|
||||
assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT);
|
||||
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == ENOENT);
|
||||
}
|
||||
|
||||
static void test_map_wronly(void)
|
||||
{
|
||||
int fd, key = 0, value = 0;
|
||||
|
||||
fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value),
|
||||
MAP_SIZE, map_flags | BPF_F_WRONLY);
|
||||
if (fd < 0) {
|
||||
printf("Failed to create map for read only test '%s'!\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
key = 1;
|
||||
value = 1234;
|
||||
/* Insert key=1 element. */
|
||||
assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
|
||||
|
||||
/* Check that key=2 is not found. */
|
||||
assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == EPERM);
|
||||
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
|
||||
}
|
||||
|
||||
static void run_all_tests(void)
|
||||
{
|
||||
test_hashmap(0, NULL);
|
||||
|
|
@ -1060,6 +1105,9 @@ static void run_all_tests(void)
|
|||
test_map_large();
|
||||
test_map_parallel();
|
||||
test_map_stress();
|
||||
|
||||
test_map_rdonly();
|
||||
test_map_wronly();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
typedef __u16 __sum16;
|
||||
|
|
@ -19,6 +20,8 @@ typedef __u16 __sum16;
|
|||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
|
|
@ -273,16 +276,26 @@ static void test_bpf_obj_id(void)
|
|||
const int nr_iters = 2;
|
||||
const char *file = "./test_obj_id.o";
|
||||
const char *jit_sysctl = "/proc/sys/net/core/bpf_jit_enable";
|
||||
const char *expected_prog_name = "test_obj_id";
|
||||
const char *expected_map_name = "test_map_id";
|
||||
const __u64 nsec_per_sec = 1000000000;
|
||||
|
||||
struct bpf_object *objs[nr_iters];
|
||||
int prog_fds[nr_iters], map_fds[nr_iters];
|
||||
/* +1 to test for the info_len returned by kernel */
|
||||
struct bpf_prog_info prog_infos[nr_iters + 1];
|
||||
struct bpf_map_info map_infos[nr_iters + 1];
|
||||
/* Each prog only uses one map. +1 to test nr_map_ids
|
||||
* returned by kernel.
|
||||
*/
|
||||
__u32 map_ids[nr_iters + 1];
|
||||
char jited_insns[128], xlated_insns[128], zeros[128];
|
||||
__u32 i, next_id, info_len, nr_id_found, duration = 0;
|
||||
struct timespec real_time_ts, boot_time_ts;
|
||||
int sysctl_fd, jit_enabled = 0, err = 0;
|
||||
__u64 array_value;
|
||||
uid_t my_uid = getuid();
|
||||
time_t now, load_time;
|
||||
|
||||
sysctl_fd = open(jit_sysctl, 0, O_RDONLY);
|
||||
if (sysctl_fd != -1) {
|
||||
|
|
@ -307,6 +320,7 @@ static void test_bpf_obj_id(void)
|
|||
/* Check bpf_obj_get_info_by_fd() */
|
||||
bzero(zeros, sizeof(zeros));
|
||||
for (i = 0; i < nr_iters; i++) {
|
||||
now = time(NULL);
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
&objs[i], &prog_fds[i]);
|
||||
/* test_obj_id.o is a dumb prog. It should never fail
|
||||
|
|
@ -316,37 +330,7 @@ static void test_bpf_obj_id(void)
|
|||
error_cnt++;
|
||||
assert(!err);
|
||||
|
||||
/* Check getting prog info */
|
||||
info_len = sizeof(struct bpf_prog_info) * 2;
|
||||
bzero(&prog_infos[i], info_len);
|
||||
bzero(jited_insns, sizeof(jited_insns));
|
||||
bzero(xlated_insns, sizeof(xlated_insns));
|
||||
prog_infos[i].jited_prog_insns = ptr_to_u64(jited_insns);
|
||||
prog_infos[i].jited_prog_len = sizeof(jited_insns);
|
||||
prog_infos[i].xlated_prog_insns = ptr_to_u64(xlated_insns);
|
||||
prog_infos[i].xlated_prog_len = sizeof(xlated_insns);
|
||||
err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i],
|
||||
&info_len);
|
||||
if (CHECK(err ||
|
||||
prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
|
||||
info_len != sizeof(struct bpf_prog_info) ||
|
||||
(jit_enabled && !prog_infos[i].jited_prog_len) ||
|
||||
(jit_enabled &&
|
||||
!memcmp(jited_insns, zeros, sizeof(zeros))) ||
|
||||
!prog_infos[i].xlated_prog_len ||
|
||||
!memcmp(xlated_insns, zeros, sizeof(zeros)),
|
||||
"get-prog-info(fd)",
|
||||
"err %d errno %d i %d type %d(%d) info_len %u(%lu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d\n",
|
||||
err, errno, i,
|
||||
prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
info_len, sizeof(struct bpf_prog_info),
|
||||
jit_enabled,
|
||||
prog_infos[i].jited_prog_len,
|
||||
prog_infos[i].xlated_prog_len,
|
||||
!!memcmp(jited_insns, zeros, sizeof(zeros)),
|
||||
!!memcmp(xlated_insns, zeros, sizeof(zeros))))
|
||||
goto done;
|
||||
|
||||
/* Insert a magic value to the map */
|
||||
map_fds[i] = bpf_find_map(__func__, objs[i], "test_map_id");
|
||||
assert(map_fds[i] >= 0);
|
||||
err = bpf_map_update_elem(map_fds[i], &array_key,
|
||||
|
|
@ -364,16 +348,67 @@ static void test_bpf_obj_id(void)
|
|||
map_infos[i].value_size != sizeof(__u64) ||
|
||||
map_infos[i].max_entries != 1 ||
|
||||
map_infos[i].map_flags != 0 ||
|
||||
info_len != sizeof(struct bpf_map_info),
|
||||
info_len != sizeof(struct bpf_map_info) ||
|
||||
strcmp((char *)map_infos[i].name, expected_map_name),
|
||||
"get-map-info(fd)",
|
||||
"err %d errno %d type %d(%d) info_len %u(%lu) key_size %u value_size %u max_entries %u map_flags %X\n",
|
||||
"err %d errno %d type %d(%d) info_len %u(%lu) key_size %u value_size %u max_entries %u map_flags %X name %s(%s)\n",
|
||||
err, errno,
|
||||
map_infos[i].type, BPF_MAP_TYPE_ARRAY,
|
||||
info_len, sizeof(struct bpf_map_info),
|
||||
map_infos[i].key_size,
|
||||
map_infos[i].value_size,
|
||||
map_infos[i].max_entries,
|
||||
map_infos[i].map_flags))
|
||||
map_infos[i].map_flags,
|
||||
map_infos[i].name, expected_map_name))
|
||||
goto done;
|
||||
|
||||
/* Check getting prog info */
|
||||
info_len = sizeof(struct bpf_prog_info) * 2;
|
||||
bzero(&prog_infos[i], info_len);
|
||||
bzero(jited_insns, sizeof(jited_insns));
|
||||
bzero(xlated_insns, sizeof(xlated_insns));
|
||||
prog_infos[i].jited_prog_insns = ptr_to_u64(jited_insns);
|
||||
prog_infos[i].jited_prog_len = sizeof(jited_insns);
|
||||
prog_infos[i].xlated_prog_insns = ptr_to_u64(xlated_insns);
|
||||
prog_infos[i].xlated_prog_len = sizeof(xlated_insns);
|
||||
prog_infos[i].map_ids = ptr_to_u64(map_ids + i);
|
||||
prog_infos[i].nr_map_ids = 2;
|
||||
err = clock_gettime(CLOCK_REALTIME, &real_time_ts);
|
||||
assert(!err);
|
||||
err = clock_gettime(CLOCK_BOOTTIME, &boot_time_ts);
|
||||
assert(!err);
|
||||
err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i],
|
||||
&info_len);
|
||||
load_time = (real_time_ts.tv_sec - boot_time_ts.tv_sec)
|
||||
+ (prog_infos[i].load_time / nsec_per_sec);
|
||||
if (CHECK(err ||
|
||||
prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
|
||||
info_len != sizeof(struct bpf_prog_info) ||
|
||||
(jit_enabled && !prog_infos[i].jited_prog_len) ||
|
||||
(jit_enabled &&
|
||||
!memcmp(jited_insns, zeros, sizeof(zeros))) ||
|
||||
!prog_infos[i].xlated_prog_len ||
|
||||
!memcmp(xlated_insns, zeros, sizeof(zeros)) ||
|
||||
load_time < now - 60 || load_time > now + 60 ||
|
||||
prog_infos[i].created_by_uid != my_uid ||
|
||||
prog_infos[i].nr_map_ids != 1 ||
|
||||
*(int *)prog_infos[i].map_ids != map_infos[i].id ||
|
||||
strcmp((char *)prog_infos[i].name, expected_prog_name),
|
||||
"get-prog-info(fd)",
|
||||
"err %d errno %d i %d type %d(%d) info_len %u(%lu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d load_time %lu(%lu) uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) name %s(%s)\n",
|
||||
err, errno, i,
|
||||
prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
info_len, sizeof(struct bpf_prog_info),
|
||||
jit_enabled,
|
||||
prog_infos[i].jited_prog_len,
|
||||
prog_infos[i].xlated_prog_len,
|
||||
!!memcmp(jited_insns, zeros, sizeof(zeros)),
|
||||
!!memcmp(xlated_insns, zeros, sizeof(zeros)),
|
||||
load_time, now,
|
||||
prog_infos[i].created_by_uid, my_uid,
|
||||
prog_infos[i].nr_map_ids, 1,
|
||||
*(int *)prog_infos[i].map_ids, map_infos[i].id,
|
||||
prog_infos[i].name, expected_prog_name))
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -382,6 +417,7 @@ static void test_bpf_obj_id(void)
|
|||
next_id = 0;
|
||||
while (!bpf_prog_get_next_id(next_id, &next_id)) {
|
||||
struct bpf_prog_info prog_info = {};
|
||||
__u32 saved_map_id;
|
||||
int prog_fd;
|
||||
|
||||
info_len = sizeof(prog_info);
|
||||
|
|
@ -404,16 +440,33 @@ static void test_bpf_obj_id(void)
|
|||
|
||||
nr_id_found++;
|
||||
|
||||
/* Negative test:
|
||||
* prog_info.nr_map_ids = 1
|
||||
* prog_info.map_ids = NULL
|
||||
*/
|
||||
prog_info.nr_map_ids = 1;
|
||||
err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
|
||||
if (CHECK(!err || errno != EFAULT,
|
||||
"get-prog-fd-bad-nr-map-ids", "err %d errno %d(%d)",
|
||||
err, errno, EFAULT))
|
||||
break;
|
||||
bzero(&prog_info, sizeof(prog_info));
|
||||
info_len = sizeof(prog_info);
|
||||
|
||||
saved_map_id = *(int *)(prog_infos[i].map_ids);
|
||||
prog_info.map_ids = prog_infos[i].map_ids;
|
||||
prog_info.nr_map_ids = 2;
|
||||
err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len);
|
||||
prog_infos[i].jited_prog_insns = 0;
|
||||
prog_infos[i].xlated_prog_insns = 0;
|
||||
CHECK(err || info_len != sizeof(struct bpf_prog_info) ||
|
||||
memcmp(&prog_info, &prog_infos[i], info_len),
|
||||
memcmp(&prog_info, &prog_infos[i], info_len) ||
|
||||
*(int *)prog_info.map_ids != saved_map_id,
|
||||
"get-prog-info(next_id->fd)",
|
||||
"err %d errno %d info_len %u(%lu) memcmp %d\n",
|
||||
"err %d errno %d info_len %u(%lu) memcmp %d map_id %u(%u)\n",
|
||||
err, errno, info_len, sizeof(struct bpf_prog_info),
|
||||
memcmp(&prog_info, &prog_infos[i], info_len));
|
||||
|
||||
memcmp(&prog_info, &prog_infos[i], info_len),
|
||||
*(int *)prog_info.map_ids, saved_map_id);
|
||||
close(prog_fd);
|
||||
}
|
||||
CHECK(nr_id_found != nr_iters,
|
||||
|
|
@ -495,6 +548,75 @@ static void test_pkt_md_access(void)
|
|||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
static void test_obj_name(void)
|
||||
{
|
||||
struct {
|
||||
const char *name;
|
||||
int success;
|
||||
int expected_errno;
|
||||
} tests[] = {
|
||||
{ "", 1, 0 },
|
||||
{ "_123456789ABCDE", 1, 0 },
|
||||
{ "_123456789ABCDEF", 0, EINVAL },
|
||||
{ "_123456789ABCD\n", 0, EINVAL },
|
||||
};
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
__u32 duration = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
||||
size_t name_len = strlen(tests[i].name) + 1;
|
||||
union bpf_attr attr;
|
||||
size_t ncopy;
|
||||
int fd;
|
||||
|
||||
/* test different attr.prog_name during BPF_PROG_LOAD */
|
||||
ncopy = name_len < sizeof(attr.prog_name) ?
|
||||
name_len : sizeof(attr.prog_name);
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_type = BPF_PROG_TYPE_SCHED_CLS;
|
||||
attr.insn_cnt = 2;
|
||||
attr.insns = ptr_to_u64(prog);
|
||||
attr.license = ptr_to_u64("");
|
||||
memcpy(attr.prog_name, tests[i].name, ncopy);
|
||||
|
||||
fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
CHECK((tests[i].success && fd < 0) ||
|
||||
(!tests[i].success && fd != -1) ||
|
||||
(!tests[i].success && errno != tests[i].expected_errno),
|
||||
"check-bpf-prog-name",
|
||||
"fd %d(%d) errno %d(%d)\n",
|
||||
fd, tests[i].success, errno, tests[i].expected_errno);
|
||||
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
||||
/* test different attr.map_name during BPF_MAP_CREATE */
|
||||
ncopy = name_len < sizeof(attr.map_name) ?
|
||||
name_len : sizeof(attr.map_name);
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_type = BPF_MAP_TYPE_ARRAY;
|
||||
attr.key_size = 4;
|
||||
attr.value_size = 4;
|
||||
attr.max_entries = 1;
|
||||
attr.map_flags = 0;
|
||||
memcpy(attr.map_name, tests[i].name, ncopy);
|
||||
fd = syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
CHECK((tests[i].success && fd < 0) ||
|
||||
(!tests[i].success && fd != -1) ||
|
||||
(!tests[i].success && errno != tests[i].expected_errno),
|
||||
"check-bpf-map-name",
|
||||
"fd %d(%d) errno %d(%d)\n",
|
||||
fd, tests[i].success, errno, tests[i].expected_errno);
|
||||
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
|
|
@ -507,6 +629,7 @@ int main(void)
|
|||
test_tcp_estats();
|
||||
test_bpf_obj_id();
|
||||
test_pkt_md_access();
|
||||
test_obj_name();
|
||||
|
||||
printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
|
||||
return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
178
tools/testing/selftests/bpf/test_verifier_log.c
Normal file
178
tools/testing/selftests/bpf/test_verifier_log.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
#define LOG_SIZE (1 << 20)
|
||||
|
||||
#define err(str...) printf("ERROR: " str)
|
||||
|
||||
static const struct bpf_insn code_sample[] = {
|
||||
/* We need a few instructions to pass the min log length */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
|
||||
static inline __u64 ptr_to_u64(const void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
}
|
||||
|
||||
static int load(char *log, size_t log_len, int log_level)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
|
||||
attr.insn_cnt = (__u32)(sizeof(code_sample) / sizeof(struct bpf_insn));
|
||||
attr.insns = ptr_to_u64(code_sample);
|
||||
attr.license = ptr_to_u64("GPL");
|
||||
attr.log_buf = ptr_to_u64(log);
|
||||
attr.log_size = log_len;
|
||||
attr.log_level = log_level;
|
||||
|
||||
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
static void check_ret(int ret, int exp_errno)
|
||||
{
|
||||
if (ret > 0) {
|
||||
close(ret);
|
||||
err("broken sample loaded successfully!?\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!ret || errno != exp_errno) {
|
||||
err("Program load returned: ret:%d/errno:%d, expected ret:%d/errno:%d\n",
|
||||
ret, errno, -1, exp_errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_ones(const char *buf, size_t len, const char *msg)
|
||||
{
|
||||
while (len--)
|
||||
if (buf[len] != 1) {
|
||||
err("%s", msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_log_good(char *log, size_t buf_len, size_t log_len,
|
||||
size_t exp_len, int exp_errno, const char *full_log)
|
||||
{
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
memset(log, 1, buf_len);
|
||||
|
||||
ret = load(log, log_len, 1);
|
||||
check_ret(ret, exp_errno);
|
||||
|
||||
len = strnlen(log, buf_len);
|
||||
if (len == buf_len) {
|
||||
err("verifier did not NULL terminate the log\n");
|
||||
exit(1);
|
||||
}
|
||||
if (exp_len && len != exp_len) {
|
||||
err("incorrect log length expected:%zd have:%zd\n",
|
||||
exp_len, len);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strchr(log, 1)) {
|
||||
err("verifier leaked a byte through\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
check_ones(log + len + 1, buf_len - len - 1,
|
||||
"verifier wrote bytes past NULL termination\n");
|
||||
|
||||
if (memcmp(full_log, log, LOG_SIZE)) {
|
||||
err("log did not match expected output\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_log_bad(char *log, size_t log_len, int log_level)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = load(log, log_len, log_level);
|
||||
check_ret(ret, EINVAL);
|
||||
if (log)
|
||||
check_ones(log, LOG_SIZE,
|
||||
"verifier touched log with bad parameters\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
char full_log[LOG_SIZE];
|
||||
char log[LOG_SIZE];
|
||||
size_t want_len;
|
||||
int i;
|
||||
|
||||
/* allow unlimited locked memory to have more consistent error code */
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &limit) < 0)
|
||||
perror("Unable to lift memlock rlimit");
|
||||
|
||||
memset(log, 1, LOG_SIZE);
|
||||
|
||||
/* Test incorrect attr */
|
||||
printf("Test log_level 0...\n");
|
||||
test_log_bad(log, LOG_SIZE, 0);
|
||||
|
||||
printf("Test log_size < 128...\n");
|
||||
test_log_bad(log, 15, 1);
|
||||
|
||||
printf("Test log_buff = NULL...\n");
|
||||
test_log_bad(NULL, LOG_SIZE, 1);
|
||||
|
||||
/* Test with log big enough */
|
||||
printf("Test oversized buffer...\n");
|
||||
test_log_good(full_log, LOG_SIZE, LOG_SIZE, 0, EACCES, full_log);
|
||||
|
||||
want_len = strlen(full_log);
|
||||
|
||||
printf("Test exact buffer...\n");
|
||||
test_log_good(log, LOG_SIZE, want_len + 2, want_len, EACCES, full_log);
|
||||
|
||||
printf("Test undersized buffers...\n");
|
||||
for (i = 0; i < 64; i++) {
|
||||
full_log[want_len - i + 1] = 1;
|
||||
full_log[want_len - i] = 0;
|
||||
|
||||
test_log_good(log, LOG_SIZE, want_len + 1 - i, want_len - i,
|
||||
ENOSPC, full_log);
|
||||
}
|
||||
|
||||
printf("test_verifier_log: OK\n");
|
||||
return 0;
|
||||
}
|
||||
53
tools/testing/selftests/bpf/test_xdp_meta.c
Normal file
53
tools/testing/selftests/bpf/test_xdp_meta.c
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define __round_mask(x, y) ((__typeof__(x))((y) - 1))
|
||||
#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
|
||||
#define ctx_ptr(ctx, mem) (void *)(unsigned long)ctx->mem
|
||||
|
||||
SEC("t")
|
||||
int ing_cls(struct __sk_buff *ctx)
|
||||
{
|
||||
__u8 *data, *data_meta, *data_end;
|
||||
__u32 diff = 0;
|
||||
|
||||
data_meta = ctx_ptr(ctx, data_meta);
|
||||
data_end = ctx_ptr(ctx, data_end);
|
||||
data = ctx_ptr(ctx, data);
|
||||
|
||||
if (data + ETH_ALEN > data_end ||
|
||||
data_meta + round_up(ETH_ALEN, 4) > data)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
diff |= ((__u32 *)data_meta)[0] ^ ((__u32 *)data)[0];
|
||||
diff |= ((__u16 *)data_meta)[2] ^ ((__u16 *)data)[2];
|
||||
|
||||
return diff ? TC_ACT_SHOT : TC_ACT_OK;
|
||||
}
|
||||
|
||||
SEC("x")
|
||||
int ing_xdp(struct xdp_md *ctx)
|
||||
{
|
||||
__u8 *data, *data_meta, *data_end;
|
||||
int ret;
|
||||
|
||||
ret = bpf_xdp_adjust_meta(ctx, -round_up(ETH_ALEN, 4));
|
||||
if (ret < 0)
|
||||
return XDP_DROP;
|
||||
|
||||
data_meta = ctx_ptr(ctx, data_meta);
|
||||
data_end = ctx_ptr(ctx, data_end);
|
||||
data = ctx_ptr(ctx, data);
|
||||
|
||||
if (data + ETH_ALEN > data_end ||
|
||||
data_meta + round_up(ETH_ALEN, 4) > data)
|
||||
return XDP_DROP;
|
||||
|
||||
__builtin_memcpy(data_meta, data, ETH_ALEN);
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
51
tools/testing/selftests/bpf/test_xdp_meta.sh
Executable file
51
tools/testing/selftests/bpf/test_xdp_meta.sh
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
#!/bin/sh
|
||||
|
||||
cleanup()
|
||||
{
|
||||
if [ "$?" = "0" ]; then
|
||||
echo "selftests: test_xdp_meta [PASS]";
|
||||
else
|
||||
echo "selftests: test_xdp_meta [FAILED]";
|
||||
fi
|
||||
|
||||
set +e
|
||||
ip netns del ns1 2> /dev/null
|
||||
ip netns del ns2 2> /dev/null
|
||||
}
|
||||
|
||||
ip link set dev lo xdp off 2>/dev/null > /dev/null
|
||||
if [ $? -ne 0 ];then
|
||||
echo "selftests: [SKIP] Could not run test without the ip xdp support"
|
||||
exit 0
|
||||
fi
|
||||
set -e
|
||||
|
||||
ip netns add ns1
|
||||
ip netns add ns2
|
||||
|
||||
trap cleanup 0 2 3 6 9
|
||||
|
||||
ip link add veth1 type veth peer name veth2
|
||||
|
||||
ip link set veth1 netns ns1
|
||||
ip link set veth2 netns ns2
|
||||
|
||||
ip netns exec ns1 ip addr add 10.1.1.11/24 dev veth1
|
||||
ip netns exec ns2 ip addr add 10.1.1.22/24 dev veth2
|
||||
|
||||
ip netns exec ns1 tc qdisc add dev veth1 clsact
|
||||
ip netns exec ns2 tc qdisc add dev veth2 clsact
|
||||
|
||||
ip netns exec ns1 tc filter add dev veth1 ingress bpf da obj test_xdp_meta.o sec t
|
||||
ip netns exec ns2 tc filter add dev veth2 ingress bpf da obj test_xdp_meta.o sec t
|
||||
|
||||
ip netns exec ns1 ip link set dev veth1 xdp obj test_xdp_meta.o sec x
|
||||
ip netns exec ns2 ip link set dev veth2 xdp obj test_xdp_meta.o sec x
|
||||
|
||||
ip netns exec ns1 ip link set dev veth1 up
|
||||
ip netns exec ns2 ip link set dev veth2 up
|
||||
|
||||
ip netns exec ns1 ping -c 1 10.1.1.22
|
||||
ip netns exec ns2 ping -c 1 10.1.1.11
|
||||
|
||||
exit 0
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue