Merge branch 'linus' into perf/urgent, to synchronize UAPI headers

Signed-off-by: Ingo Molnar <[email protected]>
This commit is contained in:
Ingo Molnar 2017-12-06 22:39:39 +01:00
commit d6eabce257
11139 changed files with 536190 additions and 258122 deletions

View file

@ -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

View file

@ -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

View file

@ -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;

View 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

View 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)

View 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)

View 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)

View 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

View 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
View 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);
}
}

View 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);
}

View 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

View 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
View 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
View 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
View 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
View 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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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 */

View 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__ */

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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);

View file

@ -207,6 +207,7 @@ struct bpf_map_def {
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
unsigned int map_flags;
};
/*

View file

@ -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;
}

View file

@ -1,3 +1,3 @@
arch/x86/insn/inat-tables.c
arch/x86/lib/inat-tables.c
objtool
fixdep

View file

@ -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:

View file

@ -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

View file

@ -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"

View file

@ -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);

View file

@ -21,7 +21,7 @@
*/
/* insn_attr_t is defined in inat.h */
#include "inat.h"
#include <asm/inat.h>
struct insn_field {
union {

View file

@ -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"

View file

@ -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) \

View file

@ -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
View 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

View file

@ -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

View file

@ -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 */

View 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 */

View file

@ -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

View 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;
}

View file

@ -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];
}

View 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);
}

View file

@ -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" },

View file

@ -39,6 +39,7 @@ TOOL_OBJS = \
utnonansi.o\
utprint.o\
utstring.o\
utstrsuppt.o\
utstrtoul64.o\
utxferror.o\
oslinuxtbl.o\

View file

@ -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);

View file

@ -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",

View file

@ -1,7 +1,6 @@
.libs
libcpupower.so
libcpupower.so.0
libcpupower.so.0.0.0
libcpupower.so.*
build/ccdv
cpufreq-info
cpufreq-set

View file

@ -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/

View file

@ -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;

View file

@ -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));

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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);

View 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

View 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

View 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;
}

View file

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
TARGETS = bpf
TARGETS = android
TARGETS += bpf
TARGETS += breakpoints
TARGETS += capabilities
TARGETS += cpufreq

View 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

View file

@ -0,0 +1,2 @@
ionapp_export
ionapp_import

View 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

View 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

View file

@ -0,0 +1,4 @@
CONFIG_ANDROID=y
CONFIG_STAGING=y
CONFIG_ION=y
CONFIG_ION_SYSTEM_HEAP=y

View 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 */

View 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

View 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;
}

View 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;
}

View 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;
}

View 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

View 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;
}

View 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

View file

@ -0,0 +1,3 @@
#!/bin/sh
(cd ion; ./ion_test.sh)

View file

@ -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 $@

View file

@ -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

View 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;
}

View 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

View 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;

View file

@ -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;
}

View file

@ -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);

View 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;
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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

View 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;
}

View 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";

View 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