mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-20 05:31:15 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Alexei Starovoitov says: ==================== pull-request: bpf-next 2019-05-31 The following pull-request contains BPF updates for your *net-next* tree. Lots of exciting new features in the first PR of this developement cycle! The main changes are: 1) misc verifier improvements, from Alexei. 2) bpftool can now convert btf to valid C, from Andrii. 3) verifier can insert explicit ZEXT insn when requested by 32-bit JITs. This feature greatly improves BPF speed on 32-bit architectures. From Jiong. 4) cgroups will now auto-detach bpf programs. This fixes issue of thousands bpf programs got stuck in dying cgroups. From Roman. 5) new bpf_send_signal() helper, from Yonghong. 6) cgroup inet skb programs can signal CN to the stack, from Lawrence. 7) miscellaneous cleanups, from many developers. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0462eaacee
122 changed files with 6433 additions and 1016 deletions
4
tools/testing/selftests/bpf/.gitignore
vendored
4
tools/testing/selftests/bpf/.gitignore
vendored
|
@ -22,6 +22,7 @@ test_lirc_mode2_user
|
|||
get_cgroup_id_user
|
||||
test_skb_cgroup_id_user
|
||||
test_socket_cookie
|
||||
test_cgroup_attach
|
||||
test_cgroup_storage
|
||||
test_select_reuseport
|
||||
test_flow_dissector
|
||||
|
@ -35,3 +36,6 @@ test_sysctl
|
|||
alu32
|
||||
libbpf.pc
|
||||
libbpf.so.*
|
||||
test_hashmap
|
||||
test_btf_dump
|
||||
xdping
|
||||
|
|
|
@ -15,7 +15,9 @@ LLC ?= llc
|
|||
LLVM_OBJCOPY ?= llvm-objcopy
|
||||
LLVM_READELF ?= llvm-readelf
|
||||
BTF_PAHOLE ?= pahole
|
||||
CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include
|
||||
CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) -I$(GENDIR) $(GENFLAGS) -I../../../include \
|
||||
-Dbpf_prog_load=bpf_prog_test_load \
|
||||
-Dbpf_load_program=bpf_test_load_program
|
||||
LDLIBS += -lcap -lelf -lrt -lpthread
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
|
@ -23,7 +25,8 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
|
|||
test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
|
||||
test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \
|
||||
test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \
|
||||
test_netcnt test_tcpnotify_user test_sock_fields test_sysctl
|
||||
test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
|
||||
test_btf_dump test_cgroup_attach xdping
|
||||
|
||||
BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c)))
|
||||
TEST_GEN_FILES = $(BPF_OBJ_FILES)
|
||||
|
@ -54,7 +57,8 @@ TEST_PROGS := test_kmod.sh \
|
|||
test_lwt_ip_encap.sh \
|
||||
test_tcp_check_syncookie.sh \
|
||||
test_tc_tunnel.sh \
|
||||
test_tc_edt.sh
|
||||
test_tc_edt.sh \
|
||||
test_xdping.sh
|
||||
|
||||
TEST_PROGS_EXTENDED := with_addr.sh \
|
||||
with_tunnels.sh \
|
||||
|
@ -78,9 +82,9 @@ $(OUTPUT)/test_maps: map_tests/*.c
|
|||
|
||||
BPFOBJ := $(OUTPUT)/libbpf.a
|
||||
|
||||
$(TEST_GEN_PROGS): $(BPFOBJ)
|
||||
$(TEST_GEN_PROGS): test_stub.o $(BPFOBJ)
|
||||
|
||||
$(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/libbpf.a
|
||||
$(TEST_GEN_PROGS_EXTENDED): test_stub.o $(OUTPUT)/libbpf.a
|
||||
|
||||
$(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
|
||||
$(OUTPUT)/test_skb_cgroup_id_user: cgroup_helpers.c
|
||||
|
@ -96,6 +100,7 @@ $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
|
|||
$(OUTPUT)/test_netcnt: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sock_fields: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sysctl: cgroup_helpers.c
|
||||
$(OUTPUT)/test_cgroup_attach: cgroup_helpers.c
|
||||
|
||||
.PHONY: force
|
||||
|
||||
|
@ -176,7 +181,7 @@ $(ALU32_BUILD_DIR)/test_progs_32: test_progs.c $(OUTPUT)/libbpf.a\
|
|||
$(ALU32_BUILD_DIR)/urandom_read
|
||||
$(CC) $(TEST_PROGS_CFLAGS) $(CFLAGS) \
|
||||
-o $(ALU32_BUILD_DIR)/test_progs_32 \
|
||||
test_progs.c trace_helpers.c prog_tests/*.c \
|
||||
test_progs.c test_stub.c trace_helpers.c prog_tests/*.c \
|
||||
$(OUTPUT)/libbpf.a $(LDLIBS)
|
||||
|
||||
$(ALU32_BUILD_DIR)/test_progs_32: $(PROG_TESTS_H)
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
*/
|
||||
#define SEC(NAME) __attribute__((section(NAME), used))
|
||||
|
||||
/* helper macro to print out debug messages */
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/* helper functions called from eBPF programs written in C */
|
||||
static void *(*bpf_map_lookup_elem)(void *map, const void *key) =
|
||||
(void *) BPF_FUNC_map_lookup_elem;
|
||||
|
@ -216,6 +224,7 @@ static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk,
|
|||
(void *) BPF_FUNC_sk_storage_get;
|
||||
static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) =
|
||||
(void *)BPF_FUNC_sk_storage_delete;
|
||||
static int (*bpf_send_signal)(unsigned sig) = (void *)BPF_FUNC_send_signal;
|
||||
|
||||
/* llvm builtin functions that eBPF C program may use to
|
||||
* emit BPF_LD_ABS and BPF_LD_IND instructions
|
||||
|
|
|
@ -33,6 +33,60 @@
|
|||
snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \
|
||||
CGROUP_WORK_DIR, path)
|
||||
|
||||
/**
|
||||
* enable_all_controllers() - Enable all available cgroup v2 controllers
|
||||
*
|
||||
* Enable all available cgroup v2 controllers in order to increase
|
||||
* the code coverage.
|
||||
*
|
||||
* If successful, 0 is returned.
|
||||
*/
|
||||
int enable_all_controllers(char *cgroup_path)
|
||||
{
|
||||
char path[PATH_MAX + 1];
|
||||
char buf[PATH_MAX];
|
||||
char *c, *c2;
|
||||
int fd, cfd;
|
||||
size_t len;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/cgroup.controllers", cgroup_path);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
log_err("Opening cgroup.controllers: %s", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
len = read(fd, buf, sizeof(buf) - 1);
|
||||
if (len < 0) {
|
||||
close(fd);
|
||||
log_err("Reading cgroup.controllers: %s", path);
|
||||
return 1;
|
||||
}
|
||||
buf[len] = 0;
|
||||
close(fd);
|
||||
|
||||
/* No controllers available? We're probably on cgroup v1. */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cgroup_path);
|
||||
cfd = open(path, O_RDWR);
|
||||
if (cfd < 0) {
|
||||
log_err("Opening cgroup.subtree_control: %s", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (c = strtok_r(buf, " ", &c2); c; c = strtok_r(NULL, " ", &c2)) {
|
||||
if (dprintf(cfd, "+%s\n", c) <= 0) {
|
||||
log_err("Enabling controller %s: %s", c, path);
|
||||
close(cfd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
close(cfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_cgroup_environment() - Setup the cgroup environment
|
||||
*
|
||||
|
@ -71,6 +125,9 @@ int setup_cgroup_environment(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (enable_all_controllers(cgroup_workdir))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ static int libbpf_debug_print(enum libbpf_print_level level,
|
|||
return vfprintf(stderr, "%s", args);
|
||||
}
|
||||
|
||||
static int check_load(const char *file)
|
||||
static int check_load(const char *file, enum bpf_prog_type type)
|
||||
{
|
||||
struct bpf_prog_load_attr attr;
|
||||
struct bpf_object *obj = NULL;
|
||||
|
@ -20,8 +20,9 @@ static int check_load(const char *file)
|
|||
|
||||
memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
|
||||
attr.file = file;
|
||||
attr.prog_type = BPF_PROG_TYPE_SCHED_CLS;
|
||||
attr.prog_type = type;
|
||||
attr.log_level = 4;
|
||||
attr.prog_flags = BPF_F_TEST_RND_HI32;
|
||||
err = bpf_prog_load_xattr(&attr, &obj, &prog_fd);
|
||||
bpf_object__close(obj);
|
||||
if (err)
|
||||
|
@ -31,19 +32,24 @@ static int check_load(const char *file)
|
|||
|
||||
void test_bpf_verif_scale(void)
|
||||
{
|
||||
const char *file1 = "./test_verif_scale1.o";
|
||||
const char *file2 = "./test_verif_scale2.o";
|
||||
const char *file3 = "./test_verif_scale3.o";
|
||||
int err;
|
||||
const char *scale[] = {
|
||||
"./test_verif_scale1.o", "./test_verif_scale2.o", "./test_verif_scale3.o"
|
||||
};
|
||||
const char *pyperf[] = {
|
||||
"./pyperf50.o", "./pyperf100.o", "./pyperf180.o"
|
||||
};
|
||||
int err, i;
|
||||
|
||||
if (verifier_stats)
|
||||
libbpf_set_print(libbpf_debug_print);
|
||||
|
||||
err = check_load(file1);
|
||||
err |= check_load(file2);
|
||||
err |= check_load(file3);
|
||||
if (!err)
|
||||
printf("test_verif_scale:OK\n");
|
||||
else
|
||||
printf("test_verif_scale:FAIL\n");
|
||||
for (i = 0; i < ARRAY_SIZE(scale); i++) {
|
||||
err = check_load(scale[i], BPF_PROG_TYPE_SCHED_CLS);
|
||||
printf("test_scale:%s:%s\n", scale[i], err ? "FAIL" : "OK");
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pyperf); i++) {
|
||||
err = check_load(pyperf[i], BPF_PROG_TYPE_RAW_TRACEPOINT);
|
||||
printf("test_scale:%s:%s\n", pyperf[i], err ? "FAIL" : "OK");
|
||||
}
|
||||
}
|
||||
|
|
198
tools/testing/selftests/bpf/prog_tests/send_signal.c
Normal file
198
tools/testing/selftests/bpf/prog_tests/send_signal.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
static volatile int sigusr1_received = 0;
|
||||
|
||||
static void sigusr1_handler(int signum)
|
||||
{
|
||||
sigusr1_received++;
|
||||
}
|
||||
|
||||
static int test_send_signal_common(struct perf_event_attr *attr,
|
||||
int prog_type,
|
||||
const char *test_name)
|
||||
{
|
||||
int err = -1, pmu_fd, prog_fd, info_map_fd, status_map_fd;
|
||||
const char *file = "./test_send_signal_kern.o";
|
||||
struct bpf_object *obj = NULL;
|
||||
int pipe_c2p[2], pipe_p2c[2];
|
||||
__u32 key = 0, duration = 0;
|
||||
char buf[256];
|
||||
pid_t pid;
|
||||
__u64 val;
|
||||
|
||||
if (CHECK(pipe(pipe_c2p), test_name,
|
||||
"pipe pipe_c2p error: %s\n", strerror(errno)))
|
||||
goto no_fork_done;
|
||||
|
||||
if (CHECK(pipe(pipe_p2c), test_name,
|
||||
"pipe pipe_p2c error: %s\n", strerror(errno))) {
|
||||
close(pipe_c2p[0]);
|
||||
close(pipe_c2p[1]);
|
||||
goto no_fork_done;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (CHECK(pid < 0, test_name, "fork error: %s\n", strerror(errno))) {
|
||||
close(pipe_c2p[0]);
|
||||
close(pipe_c2p[1]);
|
||||
close(pipe_p2c[0]);
|
||||
close(pipe_p2c[1]);
|
||||
goto no_fork_done;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* install signal handler and notify parent */
|
||||
signal(SIGUSR1, sigusr1_handler);
|
||||
|
||||
close(pipe_c2p[0]); /* close read */
|
||||
close(pipe_p2c[1]); /* close write */
|
||||
|
||||
/* notify parent signal handler is installed */
|
||||
write(pipe_c2p[1], buf, 1);
|
||||
|
||||
/* make sure parent enabled bpf program to send_signal */
|
||||
read(pipe_p2c[0], buf, 1);
|
||||
|
||||
/* wait a little for signal handler */
|
||||
sleep(1);
|
||||
|
||||
if (sigusr1_received)
|
||||
write(pipe_c2p[1], "2", 1);
|
||||
else
|
||||
write(pipe_c2p[1], "0", 1);
|
||||
|
||||
/* wait for parent notification and exit */
|
||||
read(pipe_p2c[0], buf, 1);
|
||||
|
||||
close(pipe_c2p[1]);
|
||||
close(pipe_p2c[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
close(pipe_c2p[1]); /* close write */
|
||||
close(pipe_p2c[0]); /* close read */
|
||||
|
||||
err = bpf_prog_load(file, prog_type, &obj, &prog_fd);
|
||||
if (CHECK(err < 0, test_name, "bpf_prog_load error: %s\n",
|
||||
strerror(errno)))
|
||||
goto prog_load_failure;
|
||||
|
||||
pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1,
|
||||
-1 /* group id */, 0 /* flags */);
|
||||
if (CHECK(pmu_fd < 0, test_name, "perf_event_open error: %s\n",
|
||||
strerror(errno))) {
|
||||
err = -1;
|
||||
goto close_prog;
|
||||
}
|
||||
|
||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (CHECK(err < 0, test_name, "ioctl perf_event_ioc_enable error: %s\n",
|
||||
strerror(errno)))
|
||||
goto disable_pmu;
|
||||
|
||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
|
||||
if (CHECK(err < 0, test_name, "ioctl perf_event_ioc_set_bpf error: %s\n",
|
||||
strerror(errno)))
|
||||
goto disable_pmu;
|
||||
|
||||
err = -1;
|
||||
info_map_fd = bpf_object__find_map_fd_by_name(obj, "info_map");
|
||||
if (CHECK(info_map_fd < 0, test_name, "find map %s error\n", "info_map"))
|
||||
goto disable_pmu;
|
||||
|
||||
status_map_fd = bpf_object__find_map_fd_by_name(obj, "status_map");
|
||||
if (CHECK(status_map_fd < 0, test_name, "find map %s error\n", "status_map"))
|
||||
goto disable_pmu;
|
||||
|
||||
/* wait until child signal handler installed */
|
||||
read(pipe_c2p[0], buf, 1);
|
||||
|
||||
/* trigger the bpf send_signal */
|
||||
key = 0;
|
||||
val = (((__u64)(SIGUSR1)) << 32) | pid;
|
||||
bpf_map_update_elem(info_map_fd, &key, &val, 0);
|
||||
|
||||
/* notify child that bpf program can send_signal now */
|
||||
write(pipe_p2c[1], buf, 1);
|
||||
|
||||
/* wait for result */
|
||||
err = read(pipe_c2p[0], buf, 1);
|
||||
if (CHECK(err < 0, test_name, "reading pipe error: %s\n", strerror(errno)))
|
||||
goto disable_pmu;
|
||||
if (CHECK(err == 0, test_name, "reading pipe error: size 0\n")) {
|
||||
err = -1;
|
||||
goto disable_pmu;
|
||||
}
|
||||
|
||||
err = CHECK(buf[0] != '2', test_name, "incorrect result\n");
|
||||
|
||||
/* notify child safe to exit */
|
||||
write(pipe_p2c[1], buf, 1);
|
||||
|
||||
disable_pmu:
|
||||
close(pmu_fd);
|
||||
close_prog:
|
||||
bpf_object__close(obj);
|
||||
prog_load_failure:
|
||||
close(pipe_c2p[0]);
|
||||
close(pipe_p2c[1]);
|
||||
wait(NULL);
|
||||
no_fork_done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int test_send_signal_tracepoint(void)
|
||||
{
|
||||
const char *id_path = "/sys/kernel/debug/tracing/events/syscalls/sys_enter_nanosleep/id";
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_TRACEPOINT,
|
||||
.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN,
|
||||
.sample_period = 1,
|
||||
.wakeup_events = 1,
|
||||
};
|
||||
__u32 duration = 0;
|
||||
int bytes, efd;
|
||||
char buf[256];
|
||||
|
||||
efd = open(id_path, O_RDONLY, 0);
|
||||
if (CHECK(efd < 0, "tracepoint",
|
||||
"open syscalls/sys_enter_nanosleep/id failure: %s\n",
|
||||
strerror(errno)))
|
||||
return -1;
|
||||
|
||||
bytes = read(efd, buf, sizeof(buf));
|
||||
close(efd);
|
||||
if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "tracepoint",
|
||||
"read syscalls/sys_enter_nanosleep/id failure: %s\n",
|
||||
strerror(errno)))
|
||||
return -1;
|
||||
|
||||
attr.config = strtol(buf, NULL, 0);
|
||||
|
||||
return test_send_signal_common(&attr, BPF_PROG_TYPE_TRACEPOINT, "tracepoint");
|
||||
}
|
||||
|
||||
static int test_send_signal_nmi(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.sample_freq = 50,
|
||||
.freq = 1,
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
};
|
||||
|
||||
return test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT, "perf_event");
|
||||
}
|
||||
|
||||
void test_send_signal(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret |= test_send_signal_tracepoint();
|
||||
ret |= test_send_signal_nmi();
|
||||
if (!ret)
|
||||
printf("test_send_signal:OK\n");
|
||||
else
|
||||
printf("test_send_signal:FAIL\n");
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper tests for bitfield.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct bitfields_only_mixed_types {
|
||||
* int a: 3;
|
||||
* long int b: 2;
|
||||
* _Bool c: 1;
|
||||
* enum {
|
||||
* A = 0,
|
||||
* B = 1,
|
||||
* } d: 1;
|
||||
* short e: 5;
|
||||
* int: 20;
|
||||
* unsigned int f: 30;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
struct bitfields_only_mixed_types {
|
||||
int a: 3;
|
||||
long int b: 2;
|
||||
bool c: 1; /* it's really a _Bool type */
|
||||
enum {
|
||||
A, /* A = 0, dumper is very explicit */
|
||||
B, /* B = 1, same */
|
||||
} d: 1;
|
||||
short e: 5;
|
||||
/* 20-bit padding here */
|
||||
unsigned f: 30; /* this gets aligned on 4-byte boundary */
|
||||
};
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct bitfield_mixed_with_others {
|
||||
* char: 4;
|
||||
* int a: 4;
|
||||
* short b;
|
||||
* long int c;
|
||||
* long int d: 8;
|
||||
* int e;
|
||||
* int f;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
struct bitfield_mixed_with_others {
|
||||
long: 4; /* char is enough as a backing field */
|
||||
int a: 4;
|
||||
/* 8-bit implicit padding */
|
||||
short b; /* combined with previous bitfield */
|
||||
/* 4 more bytes of implicit padding */
|
||||
long c;
|
||||
long d: 8;
|
||||
/* 24 bits implicit padding */
|
||||
int e; /* combined with previous bitfield */
|
||||
int f;
|
||||
/* 4 bytes of padding */
|
||||
};
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct bitfield_flushed {
|
||||
* int a: 4;
|
||||
* long: 60;
|
||||
* long int b: 16;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
struct bitfield_flushed {
|
||||
int a: 4;
|
||||
long: 0; /* flush until next natural alignment boundary */
|
||||
long b: 16;
|
||||
};
|
||||
|
||||
int f(struct {
|
||||
struct bitfields_only_mixed_types _1;
|
||||
struct bitfield_mixed_with_others _2;
|
||||
struct bitfield_flushed _3;
|
||||
} *_)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper test for multi-dimensional array output.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
typedef int arr_t[2];
|
||||
|
||||
typedef int multiarr_t[3][4][5];
|
||||
|
||||
typedef int *ptr_arr_t[6];
|
||||
|
||||
typedef int *ptr_multiarr_t[7][8][9][10];
|
||||
|
||||
typedef int * (*fn_ptr_arr_t[11])();
|
||||
|
||||
typedef int * (*fn_ptr_multiarr_t[12][13])();
|
||||
|
||||
struct root_struct {
|
||||
arr_t _1;
|
||||
multiarr_t _2;
|
||||
ptr_arr_t _3;
|
||||
ptr_multiarr_t _4;
|
||||
fn_ptr_arr_t _5;
|
||||
fn_ptr_multiarr_t _6;
|
||||
};
|
||||
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
int f(struct root_struct *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper test validating no name versioning happens between
|
||||
* independent C namespaces (struct/union/enum vs typedef/enum values).
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
struct S {
|
||||
int S;
|
||||
int U;
|
||||
};
|
||||
|
||||
typedef struct S S;
|
||||
|
||||
union U {
|
||||
int S;
|
||||
int U;
|
||||
};
|
||||
|
||||
typedef union U U;
|
||||
|
||||
enum E {
|
||||
V = 0,
|
||||
};
|
||||
|
||||
typedef enum E E;
|
||||
|
||||
struct A {};
|
||||
|
||||
union B {};
|
||||
|
||||
enum C {
|
||||
A = 1,
|
||||
B = 2,
|
||||
C = 3,
|
||||
};
|
||||
|
||||
struct X {};
|
||||
|
||||
union Y {};
|
||||
|
||||
enum Z;
|
||||
|
||||
typedef int X;
|
||||
|
||||
typedef int Y;
|
||||
|
||||
typedef int Z;
|
||||
|
||||
/*------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
int f(struct {
|
||||
struct S _1;
|
||||
S _2;
|
||||
union U _3;
|
||||
U _4;
|
||||
enum E _5;
|
||||
E _6;
|
||||
struct A a;
|
||||
union B b;
|
||||
enum C c;
|
||||
struct X x;
|
||||
union Y y;
|
||||
enum Z *z;
|
||||
X xx;
|
||||
Y yy;
|
||||
Z zz;
|
||||
} *_)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper test for topological sorting of dependent structs.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
struct s1 {};
|
||||
|
||||
struct s3;
|
||||
|
||||
struct s4;
|
||||
|
||||
struct s2 {
|
||||
struct s2 *s2;
|
||||
struct s3 *s3;
|
||||
struct s4 *s4;
|
||||
};
|
||||
|
||||
struct s3 {
|
||||
struct s1 s1;
|
||||
struct s2 s2;
|
||||
};
|
||||
|
||||
struct s4 {
|
||||
struct s1 s1;
|
||||
struct s3 s3;
|
||||
};
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next;
|
||||
struct list_head *prev;
|
||||
};
|
||||
|
||||
struct hlist_node {
|
||||
struct hlist_node *next;
|
||||
struct hlist_node **pprev;
|
||||
};
|
||||
|
||||
struct hlist_head {
|
||||
struct hlist_node *first;
|
||||
};
|
||||
|
||||
struct callback_head {
|
||||
struct callback_head *next;
|
||||
void (*func)(struct callback_head *);
|
||||
};
|
||||
|
||||
struct root_struct {
|
||||
struct s4 s4;
|
||||
struct list_head l;
|
||||
struct hlist_node n;
|
||||
struct hlist_head h;
|
||||
struct callback_head cb;
|
||||
};
|
||||
|
||||
/*------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
int f(struct root_struct *root)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper tests for struct packing determination.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
struct packed_trailing_space {
|
||||
int a;
|
||||
short b;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct non_packed_trailing_space {
|
||||
int a;
|
||||
short b;
|
||||
};
|
||||
|
||||
struct packed_fields {
|
||||
short a;
|
||||
int b;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct non_packed_fields {
|
||||
short a;
|
||||
int b;
|
||||
};
|
||||
|
||||
struct nested_packed {
|
||||
char: 4;
|
||||
int a: 4;
|
||||
long int b;
|
||||
struct {
|
||||
char c;
|
||||
int d;
|
||||
} __attribute__((packed)) e;
|
||||
} __attribute__((packed));
|
||||
|
||||
union union_is_never_packed {
|
||||
int a: 4;
|
||||
char b;
|
||||
char c: 1;
|
||||
};
|
||||
|
||||
union union_does_not_need_packing {
|
||||
struct {
|
||||
long int a;
|
||||
int b;
|
||||
} __attribute__((packed));
|
||||
int c;
|
||||
};
|
||||
|
||||
union jump_code_union {
|
||||
char code[5];
|
||||
struct {
|
||||
char jump;
|
||||
int offset;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
/*------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
int f(struct {
|
||||
struct packed_trailing_space _1;
|
||||
struct non_packed_trailing_space _2;
|
||||
struct packed_fields _3;
|
||||
struct non_packed_fields _4;
|
||||
struct nested_packed _5;
|
||||
union union_is_never_packed _6;
|
||||
union union_does_not_need_packing _7;
|
||||
union jump_code_union _8;
|
||||
} *_)
|
||||
{
|
||||
return 0;
|
||||
}
|
111
tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c
Normal file
111
tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c
Normal file
|
@ -0,0 +1,111 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper tests for implicit and explicit padding between fields and
|
||||
* at the end of a struct.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
struct padded_implicitly {
|
||||
int a;
|
||||
long int b;
|
||||
char c;
|
||||
};
|
||||
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct padded_explicitly {
|
||||
* int a;
|
||||
* int: 32;
|
||||
* int b;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
struct padded_explicitly {
|
||||
int a;
|
||||
int: 1; /* algo will explicitly pad with full 32 bits here */
|
||||
int b;
|
||||
};
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct padded_a_lot {
|
||||
* int a;
|
||||
* long: 32;
|
||||
* long: 64;
|
||||
* long: 64;
|
||||
* int b;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
struct padded_a_lot {
|
||||
int a;
|
||||
/* 32 bit of implicit padding here, which algo will make explicit */
|
||||
long: 64;
|
||||
long: 64;
|
||||
int b;
|
||||
};
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct padded_cache_line {
|
||||
* int a;
|
||||
* long: 32;
|
||||
* long: 64;
|
||||
* long: 64;
|
||||
* long: 64;
|
||||
* int b;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
struct padded_cache_line {
|
||||
int a;
|
||||
int b __attribute__((aligned(32)));
|
||||
};
|
||||
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
/*
|
||||
*struct zone_padding {
|
||||
* char x[0];
|
||||
*};
|
||||
*
|
||||
*struct zone {
|
||||
* int a;
|
||||
* short b;
|
||||
* short: 16;
|
||||
* struct zone_padding __pad__;
|
||||
*};
|
||||
*
|
||||
*/
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
struct zone_padding {
|
||||
char x[0];
|
||||
} __attribute__((__aligned__(8)));
|
||||
|
||||
struct zone {
|
||||
int a;
|
||||
short b;
|
||||
short: 16;
|
||||
struct zone_padding __pad__;
|
||||
};
|
||||
|
||||
int f(struct {
|
||||
struct padded_implicitly _1;
|
||||
struct padded_explicitly _2;
|
||||
struct padded_a_lot _3;
|
||||
struct padded_cache_line _4;
|
||||
struct zone _5;
|
||||
} *_)
|
||||
{
|
||||
return 0;
|
||||
}
|
229
tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
Normal file
229
tools/testing/selftests/bpf/progs/btf_dump_test_case_syntax.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* BTF-to-C dumper test for majority of C syntax quirks.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
enum e1 {
|
||||
A = 0,
|
||||
B = 1,
|
||||
};
|
||||
|
||||
enum e2 {
|
||||
C = 100,
|
||||
D = -100,
|
||||
E = 0,
|
||||
};
|
||||
|
||||
typedef enum e2 e2_t;
|
||||
|
||||
typedef enum {
|
||||
F = 0,
|
||||
G = 1,
|
||||
H = 2,
|
||||
} e3_t;
|
||||
|
||||
typedef int int_t;
|
||||
|
||||
typedef volatile const int * volatile const crazy_ptr_t;
|
||||
|
||||
typedef int *****we_need_to_go_deeper_ptr_t;
|
||||
|
||||
typedef volatile const we_need_to_go_deeper_ptr_t * restrict * volatile * const * restrict volatile * restrict const * volatile const * restrict volatile const how_about_this_ptr_t;
|
||||
|
||||
typedef int *ptr_arr_t[10];
|
||||
|
||||
typedef void (*fn_ptr1_t)(int);
|
||||
|
||||
typedef void (*printf_fn_t)(const char *, ...);
|
||||
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
/*
|
||||
* While previous function pointers are pretty trivial (C-syntax-level
|
||||
* trivial), the following are deciphered here for future generations:
|
||||
*
|
||||
* - `fn_ptr2_t`: function, taking anonymous struct as a first arg and pointer
|
||||
* to a function, that takes int and returns int, as a second arg; returning
|
||||
* a pointer to a const pointer to a char. Equivalent to:
|
||||
* typedef struct { int a; } s_t;
|
||||
* typedef int (*fn_t)(int);
|
||||
* typedef char * const * (*fn_ptr2_t)(s_t, fn_t);
|
||||
*
|
||||
* - `fn_complext_t`: pointer to a function returning struct and accepting
|
||||
* union and struct. All structs and enum are anonymous and defined inline.
|
||||
*
|
||||
* - `signal_t: pointer to a function accepting a pointer to a function as an
|
||||
* argument and returning pointer to a function as a result. Sane equivalent:
|
||||
* typedef void (*signal_handler_t)(int);
|
||||
* typedef signal_handler_t (*signal_ptr_t)(int, signal_handler_t);
|
||||
*
|
||||
* - fn_ptr_arr1_t: array of pointers to a function accepting pointer to
|
||||
* a pointer to an int and returning pointer to a char. Easy.
|
||||
*
|
||||
* - fn_ptr_arr2_t: array of const pointers to a function taking no arguments
|
||||
* and returning a const pointer to a function, that takes pointer to a
|
||||
* `int -> char *` function and returns pointer to a char. Equivalent:
|
||||
* typedef char * (*fn_input_t)(int);
|
||||
* typedef char * (*fn_output_outer_t)(fn_input_t);
|
||||
* typedef const fn_output_outer_t (* fn_output_inner_t)();
|
||||
* typedef const fn_output_inner_t fn_ptr_arr2_t[5];
|
||||
*/
|
||||
/* ----- START-EXPECTED-OUTPUT ----- */
|
||||
typedef char * const * (*fn_ptr2_t)(struct {
|
||||
int a;
|
||||
}, int (*)(int));
|
||||
|
||||
typedef struct {
|
||||
int a;
|
||||
void (*b)(int, struct {
|
||||
int c;
|
||||
}, union {
|
||||
char d;
|
||||
int e[5];
|
||||
});
|
||||
} (*fn_complex_t)(union {
|
||||
void *f;
|
||||
char g[16];
|
||||
}, struct {
|
||||
int h;
|
||||
});
|
||||
|
||||
typedef void (* (*signal_t)(int, void (*)(int)))(int);
|
||||
|
||||
typedef char * (*fn_ptr_arr1_t[10])(int **);
|
||||
|
||||
typedef char * (* const (* const fn_ptr_arr2_t[5])())(char * (*)(int));
|
||||
|
||||
struct struct_w_typedefs {
|
||||
int_t a;
|
||||
crazy_ptr_t b;
|
||||
we_need_to_go_deeper_ptr_t c;
|
||||
how_about_this_ptr_t d;
|
||||
ptr_arr_t e;
|
||||
fn_ptr1_t f;
|
||||
printf_fn_t g;
|
||||
fn_ptr2_t h;
|
||||
fn_complex_t i;
|
||||
signal_t j;
|
||||
fn_ptr_arr1_t k;
|
||||
fn_ptr_arr2_t l;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
} anon_struct_t;
|
||||
|
||||
struct struct_fwd;
|
||||
|
||||
typedef struct struct_fwd struct_fwd_t;
|
||||
|
||||
typedef struct struct_fwd *struct_fwd_ptr_t;
|
||||
|
||||
union union_fwd;
|
||||
|
||||
typedef union union_fwd union_fwd_t;
|
||||
|
||||
typedef union union_fwd *union_fwd_ptr_t;
|
||||
|
||||
struct struct_empty {};
|
||||
|
||||
struct struct_simple {
|
||||
int a;
|
||||
char b;
|
||||
const int_t *p;
|
||||
struct struct_empty s;
|
||||
enum e2 e;
|
||||
enum {
|
||||
ANON_VAL1 = 1,
|
||||
ANON_VAL2 = 2,
|
||||
} f;
|
||||
int arr1[13];
|
||||
enum e2 arr2[5];
|
||||
};
|
||||
|
||||
union union_empty {};
|
||||
|
||||
union union_simple {
|
||||
void *ptr;
|
||||
int num;
|
||||
int_t num2;
|
||||
union union_empty u;
|
||||
};
|
||||
|
||||
struct struct_in_struct {
|
||||
struct struct_simple simple;
|
||||
union union_simple also_simple;
|
||||
struct {
|
||||
int a;
|
||||
} not_so_hard_as_well;
|
||||
union {
|
||||
int b;
|
||||
int c;
|
||||
} anon_union_is_good;
|
||||
struct {
|
||||
int d;
|
||||
int e;
|
||||
};
|
||||
union {
|
||||
int f;
|
||||
int g;
|
||||
};
|
||||
};
|
||||
|
||||
struct struct_with_embedded_stuff {
|
||||
int a;
|
||||
struct {
|
||||
int b;
|
||||
struct {
|
||||
struct struct_with_embedded_stuff *c;
|
||||
const char *d;
|
||||
} e;
|
||||
union {
|
||||
volatile long int f;
|
||||
void * restrict g;
|
||||
};
|
||||
};
|
||||
union {
|
||||
const int_t *h;
|
||||
void (*i)(char, int, void *);
|
||||
} j;
|
||||
enum {
|
||||
K = 100,
|
||||
L = 200,
|
||||
} m;
|
||||
char n[16];
|
||||
struct {
|
||||
char o;
|
||||
int p;
|
||||
void (*q)(int);
|
||||
} r[5];
|
||||
struct struct_in_struct s[10];
|
||||
int t[11];
|
||||
};
|
||||
|
||||
struct root_struct {
|
||||
enum e1 _1;
|
||||
enum e2 _2;
|
||||
e2_t _2_1;
|
||||
e3_t _2_2;
|
||||
struct struct_w_typedefs _3;
|
||||
anon_struct_t _7;
|
||||
struct struct_fwd *_8;
|
||||
struct_fwd_t *_9;
|
||||
struct_fwd_ptr_t _10;
|
||||
union union_fwd *_11;
|
||||
union_fwd_t *_12;
|
||||
union_fwd_ptr_t _13;
|
||||
struct struct_with_embedded_stuff _14;
|
||||
};
|
||||
|
||||
/* ------ END-EXPECTED-OUTPUT ------ */
|
||||
|
||||
int f(struct root_struct *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
268
tools/testing/selftests/bpf/progs/pyperf.h
Normal file
268
tools/testing/selftests/bpf/progs/pyperf.h
Normal file
|
@ -0,0 +1,268 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define FUNCTION_NAME_LEN 64
|
||||
#define FILE_NAME_LEN 128
|
||||
#define TASK_COMM_LEN 16
|
||||
|
||||
typedef struct {
|
||||
int PyThreadState_frame;
|
||||
int PyThreadState_thread;
|
||||
int PyFrameObject_back;
|
||||
int PyFrameObject_code;
|
||||
int PyFrameObject_lineno;
|
||||
int PyCodeObject_filename;
|
||||
int PyCodeObject_name;
|
||||
int String_data;
|
||||
int String_size;
|
||||
} OffsetConfig;
|
||||
|
||||
typedef struct {
|
||||
uintptr_t current_state_addr;
|
||||
uintptr_t tls_key_addr;
|
||||
OffsetConfig offsets;
|
||||
bool use_tls;
|
||||
} PidData;
|
||||
|
||||
typedef struct {
|
||||
uint32_t success;
|
||||
} Stats;
|
||||
|
||||
typedef struct {
|
||||
char name[FUNCTION_NAME_LEN];
|
||||
char file[FILE_NAME_LEN];
|
||||
} Symbol;
|
||||
|
||||
typedef struct {
|
||||
uint32_t pid;
|
||||
uint32_t tid;
|
||||
char comm[TASK_COMM_LEN];
|
||||
int32_t kernel_stack_id;
|
||||
int32_t user_stack_id;
|
||||
bool thread_current;
|
||||
bool pthread_match;
|
||||
bool stack_complete;
|
||||
int16_t stack_len;
|
||||
int32_t stack[STACK_MAX_LEN];
|
||||
|
||||
int has_meta;
|
||||
int metadata;
|
||||
char dummy_safeguard;
|
||||
} Event;
|
||||
|
||||
|
||||
struct bpf_elf_map {
|
||||
__u32 type;
|
||||
__u32 size_key;
|
||||
__u32 size_value;
|
||||
__u32 max_elem;
|
||||
__u32 flags;
|
||||
};
|
||||
|
||||
typedef int pid_t;
|
||||
|
||||
typedef struct {
|
||||
void* f_back; // PyFrameObject.f_back, previous frame
|
||||
void* f_code; // PyFrameObject.f_code, pointer to PyCodeObject
|
||||
void* co_filename; // PyCodeObject.co_filename
|
||||
void* co_name; // PyCodeObject.co_name
|
||||
} FrameData;
|
||||
|
||||
static inline __attribute__((__always_inline__)) void*
|
||||
get_thread_state(void* tls_base, PidData* pidData)
|
||||
{
|
||||
void* thread_state;
|
||||
int key;
|
||||
|
||||
bpf_probe_read(&key, sizeof(key), (void*)(long)pidData->tls_key_addr);
|
||||
bpf_probe_read(&thread_state, sizeof(thread_state),
|
||||
tls_base + 0x310 + key * 0x10 + 0x08);
|
||||
return thread_state;
|
||||
}
|
||||
|
||||
static inline __attribute__((__always_inline__)) bool
|
||||
get_frame_data(void* frame_ptr, PidData* pidData, FrameData* frame, Symbol* symbol)
|
||||
{
|
||||
// read data from PyFrameObject
|
||||
bpf_probe_read(&frame->f_back,
|
||||
sizeof(frame->f_back),
|
||||
frame_ptr + pidData->offsets.PyFrameObject_back);
|
||||
bpf_probe_read(&frame->f_code,
|
||||
sizeof(frame->f_code),
|
||||
frame_ptr + pidData->offsets.PyFrameObject_code);
|
||||
|
||||
// read data from PyCodeObject
|
||||
if (!frame->f_code)
|
||||
return false;
|
||||
bpf_probe_read(&frame->co_filename,
|
||||
sizeof(frame->co_filename),
|
||||
frame->f_code + pidData->offsets.PyCodeObject_filename);
|
||||
bpf_probe_read(&frame->co_name,
|
||||
sizeof(frame->co_name),
|
||||
frame->f_code + pidData->offsets.PyCodeObject_name);
|
||||
// read actual names into symbol
|
||||
if (frame->co_filename)
|
||||
bpf_probe_read_str(&symbol->file,
|
||||
sizeof(symbol->file),
|
||||
frame->co_filename + pidData->offsets.String_data);
|
||||
if (frame->co_name)
|
||||
bpf_probe_read_str(&symbol->name,
|
||||
sizeof(symbol->name),
|
||||
frame->co_name + pidData->offsets.String_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct bpf_elf_map SEC("maps") pidmap = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.size_key = sizeof(int),
|
||||
.size_value = sizeof(PidData),
|
||||
.max_elem = 1,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") eventmap = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.size_key = sizeof(int),
|
||||
.size_value = sizeof(Event),
|
||||
.max_elem = 1,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") symbolmap = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.size_key = sizeof(Symbol),
|
||||
.size_value = sizeof(int),
|
||||
.max_elem = 1,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") statsmap = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.size_key = sizeof(Stats),
|
||||
.size_value = sizeof(int),
|
||||
.max_elem = 1,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") perfmap = {
|
||||
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
||||
.size_key = sizeof(int),
|
||||
.size_value = sizeof(int),
|
||||
.max_elem = 32,
|
||||
};
|
||||
|
||||
struct bpf_elf_map SEC("maps") stackmap = {
|
||||
.type = BPF_MAP_TYPE_STACK_TRACE,
|
||||
.size_key = sizeof(int),
|
||||
.size_value = sizeof(long long) * 127,
|
||||
.max_elem = 1000,
|
||||
};
|
||||
|
||||
static inline __attribute__((__always_inline__)) int __on_event(struct pt_regs *ctx)
|
||||
{
|
||||
uint64_t pid_tgid = bpf_get_current_pid_tgid();
|
||||
pid_t pid = (pid_t)(pid_tgid >> 32);
|
||||
PidData* pidData = bpf_map_lookup_elem(&pidmap, &pid);
|
||||
if (!pidData)
|
||||
return 0;
|
||||
|
||||
int zero = 0;
|
||||
Event* event = bpf_map_lookup_elem(&eventmap, &zero);
|
||||
if (!event)
|
||||
return 0;
|
||||
|
||||
event->pid = pid;
|
||||
|
||||
event->tid = (pid_t)pid_tgid;
|
||||
bpf_get_current_comm(&event->comm, sizeof(event->comm));
|
||||
|
||||
event->user_stack_id = bpf_get_stackid(ctx, &stackmap, BPF_F_USER_STACK);
|
||||
event->kernel_stack_id = bpf_get_stackid(ctx, &stackmap, 0);
|
||||
|
||||
void* thread_state_current = (void*)0;
|
||||
bpf_probe_read(&thread_state_current,
|
||||
sizeof(thread_state_current),
|
||||
(void*)(long)pidData->current_state_addr);
|
||||
|
||||
struct task_struct* task = (struct task_struct*)bpf_get_current_task();
|
||||
void* tls_base = (void*)task;
|
||||
|
||||
void* thread_state = pidData->use_tls ? get_thread_state(tls_base, pidData)
|
||||
: thread_state_current;
|
||||
event->thread_current = thread_state == thread_state_current;
|
||||
|
||||
if (pidData->use_tls) {
|
||||
uint64_t pthread_created;
|
||||
uint64_t pthread_self;
|
||||
bpf_probe_read(&pthread_self, sizeof(pthread_self), tls_base + 0x10);
|
||||
|
||||
bpf_probe_read(&pthread_created,
|
||||
sizeof(pthread_created),
|
||||
thread_state + pidData->offsets.PyThreadState_thread);
|
||||
event->pthread_match = pthread_created == pthread_self;
|
||||
} else {
|
||||
event->pthread_match = 1;
|
||||
}
|
||||
|
||||
if (event->pthread_match || !pidData->use_tls) {
|
||||
void* frame_ptr;
|
||||
FrameData frame;
|
||||
Symbol sym = {};
|
||||
int cur_cpu = bpf_get_smp_processor_id();
|
||||
|
||||
bpf_probe_read(&frame_ptr,
|
||||
sizeof(frame_ptr),
|
||||
thread_state + pidData->offsets.PyThreadState_frame);
|
||||
|
||||
int32_t* symbol_counter = bpf_map_lookup_elem(&symbolmap, &sym);
|
||||
if (symbol_counter == NULL)
|
||||
return 0;
|
||||
#pragma unroll
|
||||
/* Unwind python stack */
|
||||
for (int i = 0; i < STACK_MAX_LEN; ++i) {
|
||||
if (frame_ptr && get_frame_data(frame_ptr, pidData, &frame, &sym)) {
|
||||
int32_t new_symbol_id = *symbol_counter * 64 + cur_cpu;
|
||||
int32_t *symbol_id = bpf_map_lookup_elem(&symbolmap, &sym);
|
||||
if (!symbol_id) {
|
||||
bpf_map_update_elem(&symbolmap, &sym, &zero, 0);
|
||||
symbol_id = bpf_map_lookup_elem(&symbolmap, &sym);
|
||||
if (!symbol_id)
|
||||
return 0;
|
||||
}
|
||||
if (*symbol_id == new_symbol_id)
|
||||
(*symbol_counter)++;
|
||||
event->stack[i] = *symbol_id;
|
||||
event->stack_len = i + 1;
|
||||
frame_ptr = frame.f_back;
|
||||
}
|
||||
}
|
||||
event->stack_complete = frame_ptr == NULL;
|
||||
} else {
|
||||
event->stack_complete = 1;
|
||||
}
|
||||
|
||||
Stats* stats = bpf_map_lookup_elem(&statsmap, &zero);
|
||||
if (stats)
|
||||
stats->success++;
|
||||
|
||||
event->has_meta = 0;
|
||||
bpf_perf_event_output(ctx, &perfmap, 0, event, offsetof(Event, metadata));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("raw_tracepoint/kfree_skb")
|
||||
int on_event(struct pt_regs* ctx)
|
||||
{
|
||||
int i, ret = 0;
|
||||
ret |= __on_event(ctx);
|
||||
ret |= __on_event(ctx);
|
||||
ret |= __on_event(ctx);
|
||||
ret |= __on_event(ctx);
|
||||
ret |= __on_event(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
4
tools/testing/selftests/bpf/progs/pyperf100.c
Normal file
4
tools/testing/selftests/bpf/progs/pyperf100.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#define STACK_MAX_LEN 100
|
||||
#include "pyperf.h"
|
4
tools/testing/selftests/bpf/progs/pyperf180.c
Normal file
4
tools/testing/selftests/bpf/progs/pyperf180.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#define STACK_MAX_LEN 180
|
||||
#include "pyperf.h"
|
4
tools/testing/selftests/bpf/progs/pyperf50.c
Normal file
4
tools/testing/selftests/bpf/progs/pyperf50.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#define STACK_MAX_LEN 50
|
||||
#include "pyperf.h"
|
|
@ -5,13 +5,6 @@
|
|||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
SEC("sk_skb1")
|
||||
int bpf_prog1(struct __sk_buff *skb)
|
||||
{
|
||||
|
|
|
@ -5,13 +5,6 @@
|
|||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
SEC("sk_msg1")
|
||||
int bpf_prog1(struct sk_msg_md *msg)
|
||||
{
|
||||
|
|
|
@ -5,13 +5,6 @@
|
|||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
struct bpf_map_def SEC("maps") sock_map_rx = {
|
||||
.type = BPF_MAP_TYPE_SOCKMAP,
|
||||
.key_size = sizeof(int),
|
||||
|
|
|
@ -6,13 +6,6 @@
|
|||
#include "bpf_helpers.h"
|
||||
#include "bpf_endian.h"
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/* Packet parsing state machine helpers. */
|
||||
#define cursor_advance(_cursor, _len) \
|
||||
({ void *_tmp = _cursor; _cursor += _len; _tmp; })
|
||||
|
|
51
tools/testing/selftests/bpf/progs/test_send_signal_kern.c
Normal file
51
tools/testing/selftests/bpf/progs/test_send_signal_kern.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/version.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
struct bpf_map_def SEC("maps") info_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
BPF_ANNOTATE_KV_PAIR(info_map, __u32, __u64);
|
||||
|
||||
struct bpf_map_def SEC("maps") status_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(__u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
BPF_ANNOTATE_KV_PAIR(status_map, __u32, __u64);
|
||||
|
||||
SEC("send_signal_demo")
|
||||
int bpf_send_signal_test(void *ctx)
|
||||
{
|
||||
__u64 *info_val, *status_val;
|
||||
__u32 key = 0, pid, sig;
|
||||
int ret;
|
||||
|
||||
status_val = bpf_map_lookup_elem(&status_map, &key);
|
||||
if (!status_val || *status_val != 0)
|
||||
return 0;
|
||||
|
||||
info_val = bpf_map_lookup_elem(&info_map, &key);
|
||||
if (!info_val || *info_val == 0)
|
||||
return 0;
|
||||
|
||||
sig = *info_val >> 32;
|
||||
pid = *info_val & 0xffffFFFF;
|
||||
|
||||
if ((bpf_get_current_pid_tgid() >> 32) == pid) {
|
||||
ret = bpf_send_signal(sig);
|
||||
if (ret == 0)
|
||||
*status_val = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
char __license[] SEC("license") = "GPL";
|
|
@ -15,13 +15,6 @@
|
|||
#include <linux/udp.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
static __u32 rol32(__u32 word, unsigned int shift)
|
||||
{
|
||||
return (word << shift) | (word >> ((-shift) & 31));
|
||||
|
|
184
tools/testing/selftests/bpf/progs/xdping_kern.c
Normal file
184
tools/testing/selftests/bpf/progs/xdping_kern.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
|
||||
|
||||
#define KBUILD_MODNAME "foo"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include "bpf_helpers.h"
|
||||
#include "bpf_endian.h"
|
||||
|
||||
#include "xdping.h"
|
||||
|
||||
struct bpf_map_def SEC("maps") ping_map = {
|
||||
.type = BPF_MAP_TYPE_HASH,
|
||||
.key_size = sizeof(__u32),
|
||||
.value_size = sizeof(struct pinginfo),
|
||||
.max_entries = 256,
|
||||
};
|
||||
|
||||
static __always_inline void swap_src_dst_mac(void *data)
|
||||
{
|
||||
unsigned short *p = data;
|
||||
unsigned short dst[3];
|
||||
|
||||
dst[0] = p[0];
|
||||
dst[1] = p[1];
|
||||
dst[2] = p[2];
|
||||
p[0] = p[3];
|
||||
p[1] = p[4];
|
||||
p[2] = p[5];
|
||||
p[3] = dst[0];
|
||||
p[4] = dst[1];
|
||||
p[5] = dst[2];
|
||||
}
|
||||
|
||||
static __always_inline __u16 csum_fold_helper(__wsum sum)
|
||||
{
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
return ~((sum & 0xffff) + (sum >> 16));
|
||||
}
|
||||
|
||||
static __always_inline __u16 ipv4_csum(void *data_start, int data_size)
|
||||
{
|
||||
__wsum sum;
|
||||
|
||||
sum = bpf_csum_diff(0, 0, data_start, data_size, 0);
|
||||
return csum_fold_helper(sum);
|
||||
}
|
||||
|
||||
#define ICMP_ECHO_LEN 64
|
||||
|
||||
static __always_inline int icmp_check(struct xdp_md *ctx, int type)
|
||||
{
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
struct ethhdr *eth = data;
|
||||
struct icmphdr *icmph;
|
||||
struct iphdr *iph;
|
||||
|
||||
if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end)
|
||||
return XDP_PASS;
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP))
|
||||
return XDP_PASS;
|
||||
|
||||
iph = data + sizeof(*eth);
|
||||
|
||||
if (iph->protocol != IPPROTO_ICMP)
|
||||
return XDP_PASS;
|
||||
|
||||
if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN)
|
||||
return XDP_PASS;
|
||||
|
||||
icmph = data + sizeof(*eth) + sizeof(*iph);
|
||||
|
||||
if (icmph->type != type)
|
||||
return XDP_PASS;
|
||||
|
||||
return XDP_TX;
|
||||
}
|
||||
|
||||
SEC("xdpclient")
|
||||
int xdping_client(struct xdp_md *ctx)
|
||||
{
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
struct pinginfo *pinginfo = NULL;
|
||||
struct ethhdr *eth = data;
|
||||
struct icmphdr *icmph;
|
||||
struct iphdr *iph;
|
||||
__u64 recvtime;
|
||||
__be32 raddr;
|
||||
__be16 seq;
|
||||
int ret;
|
||||
__u8 i;
|
||||
|
||||
ret = icmp_check(ctx, ICMP_ECHOREPLY);
|
||||
|
||||
if (ret != XDP_TX)
|
||||
return ret;
|
||||
|
||||
iph = data + sizeof(*eth);
|
||||
icmph = data + sizeof(*eth) + sizeof(*iph);
|
||||
raddr = iph->saddr;
|
||||
|
||||
/* Record time reply received. */
|
||||
recvtime = bpf_ktime_get_ns();
|
||||
pinginfo = bpf_map_lookup_elem(&ping_map, &raddr);
|
||||
if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence)
|
||||
return XDP_PASS;
|
||||
|
||||
if (pinginfo->start) {
|
||||
#pragma clang loop unroll(full)
|
||||
for (i = 0; i < XDPING_MAX_COUNT; i++) {
|
||||
if (pinginfo->times[i] == 0)
|
||||
break;
|
||||
}
|
||||
/* verifier is fussy here... */
|
||||
if (i < XDPING_MAX_COUNT) {
|
||||
pinginfo->times[i] = recvtime -
|
||||
pinginfo->start;
|
||||
pinginfo->start = 0;
|
||||
i++;
|
||||
}
|
||||
/* No more space for values? */
|
||||
if (i == pinginfo->count || i == XDPING_MAX_COUNT)
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
/* Now convert reply back into echo request. */
|
||||
swap_src_dst_mac(data);
|
||||
iph->saddr = iph->daddr;
|
||||
iph->daddr = raddr;
|
||||
icmph->type = ICMP_ECHO;
|
||||
seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1);
|
||||
icmph->un.echo.sequence = seq;
|
||||
icmph->checksum = 0;
|
||||
icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
|
||||
|
||||
pinginfo->seq = seq;
|
||||
pinginfo->start = bpf_ktime_get_ns();
|
||||
|
||||
return XDP_TX;
|
||||
}
|
||||
|
||||
SEC("xdpserver")
|
||||
int xdping_server(struct xdp_md *ctx)
|
||||
{
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
struct ethhdr *eth = data;
|
||||
struct icmphdr *icmph;
|
||||
struct iphdr *iph;
|
||||
__be32 raddr;
|
||||
int ret;
|
||||
|
||||
ret = icmp_check(ctx, ICMP_ECHO);
|
||||
|
||||
if (ret != XDP_TX)
|
||||
return ret;
|
||||
|
||||
iph = data + sizeof(*eth);
|
||||
icmph = data + sizeof(*eth) + sizeof(*iph);
|
||||
raddr = iph->saddr;
|
||||
|
||||
/* Now convert request into echo reply. */
|
||||
swap_src_dst_mac(data);
|
||||
iph->saddr = iph->daddr;
|
||||
iph->daddr = raddr;
|
||||
icmph->type = ICMP_ECHOREPLY;
|
||||
icmph->checksum = 0;
|
||||
icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
|
||||
|
||||
return XDP_TX;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
|
@ -4025,62 +4025,13 @@ static struct btf_file_test file_tests[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int file_has_btf_elf(const char *fn, bool *has_btf_ext)
|
||||
{
|
||||
Elf_Scn *scn = NULL;
|
||||
GElf_Ehdr ehdr;
|
||||
int ret = 0;
|
||||
int elf_fd;
|
||||
Elf *elf;
|
||||
|
||||
if (CHECK(elf_version(EV_CURRENT) == EV_NONE,
|
||||
"elf_version(EV_CURRENT) == EV_NONE"))
|
||||
return -1;
|
||||
|
||||
elf_fd = open(fn, O_RDONLY);
|
||||
if (CHECK(elf_fd == -1, "open(%s): errno:%d", fn, errno))
|
||||
return -1;
|
||||
|
||||
elf = elf_begin(elf_fd, ELF_C_READ, NULL);
|
||||
if (CHECK(!elf, "elf_begin(%s): %s", fn, elf_errmsg(elf_errno()))) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (CHECK(!gelf_getehdr(elf, &ehdr), "!gelf_getehdr(%s)", fn)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
while ((scn = elf_nextscn(elf, scn))) {
|
||||
const char *sh_name;
|
||||
GElf_Shdr sh;
|
||||
|
||||
if (CHECK(gelf_getshdr(scn, &sh) != &sh,
|
||||
"file:%s gelf_getshdr != &sh", fn)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
sh_name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
|
||||
if (!strcmp(sh_name, BTF_ELF_SEC))
|
||||
ret = 1;
|
||||
if (!strcmp(sh_name, BTF_EXT_ELF_SEC))
|
||||
*has_btf_ext = true;
|
||||
}
|
||||
|
||||
done:
|
||||
close(elf_fd);
|
||||
elf_end(elf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_test_file(unsigned int test_num)
|
||||
{
|
||||
const struct btf_file_test *test = &file_tests[test_num - 1];
|
||||
const char *expected_fnames[] = {"_dummy_tracepoint",
|
||||
"test_long_fname_1",
|
||||
"test_long_fname_2"};
|
||||
struct btf_ext *btf_ext = NULL;
|
||||
struct bpf_prog_info info = {};
|
||||
struct bpf_object *obj = NULL;
|
||||
struct bpf_func_info *finfo;
|
||||
|
@ -4095,15 +4046,19 @@ static int do_test_file(unsigned int test_num)
|
|||
fprintf(stderr, "BTF libbpf test[%u] (%s): ", test_num,
|
||||
test->file);
|
||||
|
||||
err = file_has_btf_elf(test->file, &has_btf_ext);
|
||||
if (err == -1)
|
||||
return err;
|
||||
|
||||
if (err == 0) {
|
||||
fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
|
||||
skip_cnt++;
|
||||
return 0;
|
||||
btf = btf__parse_elf(test->file, &btf_ext);
|
||||
if (IS_ERR(btf)) {
|
||||
if (PTR_ERR(btf) == -ENOENT) {
|
||||
fprintf(stderr, "SKIP. No ELF %s found", BTF_ELF_SEC);
|
||||
skip_cnt++;
|
||||
return 0;
|
||||
}
|
||||
return PTR_ERR(btf);
|
||||
}
|
||||
btf__free(btf);
|
||||
|
||||
has_btf_ext = btf_ext != NULL;
|
||||
btf_ext__free(btf_ext);
|
||||
|
||||
obj = bpf_object__open(test->file);
|
||||
if (CHECK(IS_ERR(obj), "obj: %ld", PTR_ERR(obj)))
|
||||
|
|
143
tools/testing/selftests/bpf/test_btf_dump.c
Normal file
143
tools/testing/selftests/bpf/test_btf_dump.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <btf.h>
|
||||
|
||||
#define CHECK(condition, format...) ({ \
|
||||
int __ret = !!(condition); \
|
||||
if (__ret) { \
|
||||
fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__); \
|
||||
fprintf(stderr, format); \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
void btf_dump_printf(void *ctx, const char *fmt, va_list args)
|
||||
{
|
||||
vfprintf(ctx, fmt, args);
|
||||
}
|
||||
|
||||
struct btf_dump_test_case {
|
||||
const char *name;
|
||||
struct btf_dump_opts opts;
|
||||
} btf_dump_test_cases[] = {
|
||||
{.name = "btf_dump_test_case_syntax", .opts = {}},
|
||||
{.name = "btf_dump_test_case_ordering", .opts = {}},
|
||||
{.name = "btf_dump_test_case_padding", .opts = {}},
|
||||
{.name = "btf_dump_test_case_packing", .opts = {}},
|
||||
{.name = "btf_dump_test_case_bitfields", .opts = {}},
|
||||
{.name = "btf_dump_test_case_multidim", .opts = {}},
|
||||
{.name = "btf_dump_test_case_namespacing", .opts = {}},
|
||||
};
|
||||
|
||||
static int btf_dump_all_types(const struct btf *btf,
|
||||
const struct btf_dump_opts *opts)
|
||||
{
|
||||
size_t type_cnt = btf__get_nr_types(btf);
|
||||
struct btf_dump *d;
|
||||
int err = 0, id;
|
||||
|
||||
d = btf_dump__new(btf, NULL, opts, btf_dump_printf);
|
||||
if (IS_ERR(d))
|
||||
return PTR_ERR(d);
|
||||
|
||||
for (id = 1; id <= type_cnt; id++) {
|
||||
err = btf_dump__dump_type(d, id);
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
btf_dump__free(d);
|
||||
return err;
|
||||
}
|
||||
|
||||
int test_btf_dump_case(int n, struct btf_dump_test_case *test_case)
|
||||
{
|
||||
char test_file[256], out_file[256], diff_cmd[1024];
|
||||
struct btf *btf = NULL;
|
||||
int err = 0, fd = -1;
|
||||
FILE *f = NULL;
|
||||
|
||||
fprintf(stderr, "Test case #%d (%s): ", n, test_case->name);
|
||||
|
||||
snprintf(test_file, sizeof(test_file), "%s.o", test_case->name);
|
||||
|
||||
btf = btf__parse_elf(test_file, NULL);
|
||||
if (CHECK(IS_ERR(btf),
|
||||
"failed to load test BTF: %ld\n", PTR_ERR(btf))) {
|
||||
err = -PTR_ERR(btf);
|
||||
btf = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
snprintf(out_file, sizeof(out_file),
|
||||
"/tmp/%s.output.XXXXXX", test_case->name);
|
||||
fd = mkstemp(out_file);
|
||||
if (CHECK(fd < 0, "failed to create temp output file: %d\n", fd)) {
|
||||
err = fd;
|
||||
goto done;
|
||||
}
|
||||
f = fdopen(fd, "w");
|
||||
if (CHECK(f == NULL, "failed to open temp output file: %s(%d)\n",
|
||||
strerror(errno), errno)) {
|
||||
close(fd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
test_case->opts.ctx = f;
|
||||
err = btf_dump_all_types(btf, &test_case->opts);
|
||||
fclose(f);
|
||||
close(fd);
|
||||
if (CHECK(err, "failure during C dumping: %d\n", err)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
snprintf(test_file, sizeof(test_file), "progs/%s.c", test_case->name);
|
||||
/*
|
||||
* Diff test output and expected test output, contained between
|
||||
* START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT lines in test case.
|
||||
* For expected output lines, everything before '*' is stripped out.
|
||||
* Also lines containing comment start and comment end markers are
|
||||
* ignored.
|
||||
*/
|
||||
snprintf(diff_cmd, sizeof(diff_cmd),
|
||||
"awk '/START-EXPECTED-OUTPUT/{out=1;next} "
|
||||
"/END-EXPECTED-OUTPUT/{out=0} "
|
||||
"/\\/\\*|\\*\\//{next} " /* ignore comment start/end lines */
|
||||
"out {sub(/^[ \\t]*\\*/, \"\"); print}' '%s' | diff -u - '%s'",
|
||||
test_file, out_file);
|
||||
err = system(diff_cmd);
|
||||
if (CHECK(err,
|
||||
"differing test output, output=%s, err=%d, diff cmd:\n%s\n",
|
||||
out_file, err, diff_cmd))
|
||||
goto done;
|
||||
|
||||
remove(out_file);
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
done:
|
||||
btf__free(btf);
|
||||
return err;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int test_case_cnt, i, err, failed = 0;
|
||||
|
||||
test_case_cnt = sizeof(btf_dump_test_cases) /
|
||||
sizeof(btf_dump_test_cases[0]);
|
||||
|
||||
for (i = 0; i < test_case_cnt; i++) {
|
||||
err = test_btf_dump_case(i, &btf_dump_test_cases[i]);
|
||||
if (err)
|
||||
failed++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%d tests succeeded, %d tests failed.\n",
|
||||
test_case_cnt - failed, failed);
|
||||
|
||||
return failed;
|
||||
}
|
571
tools/testing/selftests/bpf/test_cgroup_attach.c
Normal file
571
tools/testing/selftests/bpf/test_cgroup_attach.c
Normal file
|
@ -0,0 +1,571 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/* eBPF example program:
|
||||
*
|
||||
* - Creates arraymap in kernel with 4 bytes keys and 8 byte values
|
||||
*
|
||||
* - Loads eBPF program
|
||||
*
|
||||
* The eBPF program accesses the map passed in to store two pieces of
|
||||
* information. The number of invocations of the program, which maps
|
||||
* to the number of packets received, is stored to key 0. Key 1 is
|
||||
* incremented on each iteration by the number of bytes stored in
|
||||
* the skb. The program also stores the number of received bytes
|
||||
* in the cgroup storage.
|
||||
*
|
||||
* - Attaches the new program to a cgroup using BPF_PROG_ATTACH
|
||||
*
|
||||
* - Every second, reads map[0] and map[1] to see how many bytes and
|
||||
* packets were seen on any socket of tasks in the given cgroup.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/filter.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
#include "bpf_util.h"
|
||||
#include "bpf_rlimit.h"
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define FOO "/foo"
|
||||
#define BAR "/foo/bar/"
|
||||
#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
|
||||
|
||||
char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug(args...) printf(args)
|
||||
#else
|
||||
#define debug(args...)
|
||||
#endif
|
||||
|
||||
static int prog_load(int verdict)
|
||||
{
|
||||
int ret;
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
|
||||
|
||||
ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
|
||||
prog, insns_cnt, "GPL", 0,
|
||||
bpf_log_buf, BPF_LOG_BUF_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
log_err("Loading program");
|
||||
printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_foo_bar(void)
|
||||
{
|
||||
int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0;
|
||||
|
||||
allow_prog = prog_load(1);
|
||||
if (!allow_prog)
|
||||
goto err;
|
||||
|
||||
drop_prog = prog_load(0);
|
||||
if (!drop_prog)
|
||||
goto err;
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto err;
|
||||
|
||||
/* Create cgroup /foo, get fd, and join it */
|
||||
foo = create_and_get_cgroup(FOO);
|
||||
if (foo < 0)
|
||||
goto err;
|
||||
|
||||
if (join_cgroup(FOO))
|
||||
goto err;
|
||||
|
||||
if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
log_err("Attaching prog to /foo");
|
||||
goto err;
|
||||
}
|
||||
|
||||
debug("Attached DROP prog. This ping in cgroup /foo should fail...\n");
|
||||
assert(system(PING_CMD) != 0);
|
||||
|
||||
/* Create cgroup /foo/bar, get fd, and join it */
|
||||
bar = create_and_get_cgroup(BAR);
|
||||
if (bar < 0)
|
||||
goto err;
|
||||
|
||||
if (join_cgroup(BAR))
|
||||
goto err;
|
||||
|
||||
debug("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
|
||||
assert(system(PING_CMD) != 0);
|
||||
|
||||
if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
log_err("Attaching prog to /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
debug("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
|
||||
assert(system(PING_CMD) == 0);
|
||||
|
||||
if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Detaching program from /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
debug("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
|
||||
"This ping in cgroup /foo/bar should fail...\n");
|
||||
assert(system(PING_CMD) != 0);
|
||||
|
||||
if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
log_err("Attaching prog to /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Detaching program from /foo");
|
||||
goto err;
|
||||
}
|
||||
|
||||
debug("Attached PASS from /foo/bar and detached DROP from /foo.\n"
|
||||
"This ping in cgroup /foo/bar should pass...\n");
|
||||
assert(system(PING_CMD) == 0);
|
||||
|
||||
if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
log_err("Attaching prog to /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
|
||||
errno = 0;
|
||||
log_err("Unexpected success attaching prog to /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Detaching program from /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
|
||||
errno = 0;
|
||||
log_err("Unexpected success in double detach from /foo");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
|
||||
log_err("Attaching non-overridable prog to /foo");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
|
||||
errno = 0;
|
||||
log_err("Unexpected success attaching non-overridable prog to /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
errno = 0;
|
||||
log_err("Unexpected success attaching overridable prog to /foo/bar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
errno = 0;
|
||||
log_err("Unexpected success attaching overridable prog to /foo");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
|
||||
log_err("Attaching different non-overridable prog to /foo");
|
||||
goto err;
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
err:
|
||||
rc = 1;
|
||||
|
||||
out:
|
||||
close(foo);
|
||||
close(bar);
|
||||
cleanup_cgroup_environment();
|
||||
if (!rc)
|
||||
printf("#override:PASS\n");
|
||||
else
|
||||
printf("#override:FAIL\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int map_fd = -1;
|
||||
|
||||
static int prog_load_cnt(int verdict, int val)
|
||||
{
|
||||
int cgroup_storage_fd, percpu_cgroup_storage_fd;
|
||||
|
||||
if (map_fd < 0)
|
||||
map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
|
||||
if (map_fd < 0) {
|
||||
printf("failed to create map '%s'\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cgroup_storage_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE,
|
||||
sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
|
||||
if (cgroup_storage_fd < 0) {
|
||||
printf("failed to create map '%s'\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
percpu_cgroup_storage_fd = bpf_create_map(
|
||||
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
|
||||
sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
|
||||
if (percpu_cgroup_storage_fd < 0) {
|
||||
printf("failed to create map '%s'\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_MOV32_IMM(BPF_REG_0, 0),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
|
||||
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
|
||||
BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
|
||||
|
||||
BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
|
||||
BPF_MOV64_IMM(BPF_REG_1, val),
|
||||
BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0),
|
||||
|
||||
BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd),
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0),
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
|
||||
int ret;
|
||||
|
||||
ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
|
||||
prog, insns_cnt, "GPL", 0,
|
||||
bpf_log_buf, BPF_LOG_BUF_SIZE);
|
||||
|
||||
if (ret < 0) {
|
||||
log_err("Loading program");
|
||||
printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
|
||||
return 0;
|
||||
}
|
||||
close(cgroup_storage_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int test_multiprog(void)
|
||||
{
|
||||
__u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id;
|
||||
int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0;
|
||||
int drop_prog, allow_prog[6] = {}, rc = 0;
|
||||
unsigned long long value;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
allow_prog[i] = prog_load_cnt(1, 1 << i);
|
||||
if (!allow_prog[i])
|
||||
goto err;
|
||||
}
|
||||
drop_prog = prog_load_cnt(0, 1);
|
||||
if (!drop_prog)
|
||||
goto err;
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto err;
|
||||
|
||||
cg1 = create_and_get_cgroup("/cg1");
|
||||
if (cg1 < 0)
|
||||
goto err;
|
||||
cg2 = create_and_get_cgroup("/cg1/cg2");
|
||||
if (cg2 < 0)
|
||||
goto err;
|
||||
cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
|
||||
if (cg3 < 0)
|
||||
goto err;
|
||||
cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
|
||||
if (cg4 < 0)
|
||||
goto err;
|
||||
cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
|
||||
if (cg5 < 0)
|
||||
goto err;
|
||||
|
||||
if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
|
||||
goto err;
|
||||
|
||||
if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_MULTI)) {
|
||||
log_err("Attaching prog to cg1");
|
||||
goto err;
|
||||
}
|
||||
if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_MULTI)) {
|
||||
log_err("Unexpected success attaching the same prog to cg1");
|
||||
goto err;
|
||||
}
|
||||
if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_MULTI)) {
|
||||
log_err("Attaching prog2 to cg1");
|
||||
goto err;
|
||||
}
|
||||
if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
log_err("Attaching prog to cg2");
|
||||
goto err;
|
||||
}
|
||||
if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_MULTI)) {
|
||||
log_err("Attaching prog to cg3");
|
||||
goto err;
|
||||
}
|
||||
if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_OVERRIDE)) {
|
||||
log_err("Attaching prog to cg4");
|
||||
goto err;
|
||||
}
|
||||
if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
|
||||
log_err("Attaching prog to cg5");
|
||||
goto err;
|
||||
}
|
||||
assert(system(PING_CMD) == 0);
|
||||
assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
|
||||
assert(value == 1 + 2 + 8 + 32);
|
||||
|
||||
/* query the number of effective progs in cg5 */
|
||||
assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
|
||||
NULL, NULL, &prog_cnt) == 0);
|
||||
assert(prog_cnt == 4);
|
||||
/* retrieve prog_ids of effective progs in cg5 */
|
||||
assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
|
||||
&attach_flags, prog_ids, &prog_cnt) == 0);
|
||||
assert(prog_cnt == 4);
|
||||
assert(attach_flags == 0);
|
||||
saved_prog_id = prog_ids[0];
|
||||
/* check enospc handling */
|
||||
prog_ids[0] = 0;
|
||||
prog_cnt = 2;
|
||||
assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
|
||||
&attach_flags, prog_ids, &prog_cnt) == -1 &&
|
||||
errno == ENOSPC);
|
||||
assert(prog_cnt == 4);
|
||||
/* check that prog_ids are returned even when buffer is too small */
|
||||
assert(prog_ids[0] == saved_prog_id);
|
||||
/* retrieve prog_id of single attached prog in cg5 */
|
||||
prog_ids[0] = 0;
|
||||
assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
|
||||
NULL, prog_ids, &prog_cnt) == 0);
|
||||
assert(prog_cnt == 1);
|
||||
assert(prog_ids[0] == saved_prog_id);
|
||||
|
||||
/* detach bottom program and ping again */
|
||||
if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Detaching prog from cg5");
|
||||
goto err;
|
||||
}
|
||||
value = 0;
|
||||
assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
|
||||
assert(system(PING_CMD) == 0);
|
||||
assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
|
||||
assert(value == 1 + 2 + 8 + 16);
|
||||
|
||||
/* detach 3rd from bottom program and ping again */
|
||||
errno = 0;
|
||||
if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Unexpected success on detach from cg3");
|
||||
goto err;
|
||||
}
|
||||
if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Detaching from cg3");
|
||||
goto err;
|
||||
}
|
||||
value = 0;
|
||||
assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
|
||||
assert(system(PING_CMD) == 0);
|
||||
assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
|
||||
assert(value == 1 + 2 + 16);
|
||||
|
||||
/* detach 2nd from bottom program and ping again */
|
||||
if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) {
|
||||
log_err("Detaching prog from cg4");
|
||||
goto err;
|
||||
}
|
||||
value = 0;
|
||||
assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
|
||||
assert(system(PING_CMD) == 0);
|
||||
assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
|
||||
assert(value == 1 + 2 + 4);
|
||||
|
||||
prog_cnt = 4;
|
||||
assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
|
||||
&attach_flags, prog_ids, &prog_cnt) == 0);
|
||||
assert(prog_cnt == 3);
|
||||
assert(attach_flags == 0);
|
||||
assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
|
||||
NULL, prog_ids, &prog_cnt) == 0);
|
||||
assert(prog_cnt == 0);
|
||||
goto out;
|
||||
err:
|
||||
rc = 1;
|
||||
|
||||
out:
|
||||
for (i = 0; i < 6; i++)
|
||||
if (allow_prog[i] > 0)
|
||||
close(allow_prog[i]);
|
||||
close(cg1);
|
||||
close(cg2);
|
||||
close(cg3);
|
||||
close(cg4);
|
||||
close(cg5);
|
||||
cleanup_cgroup_environment();
|
||||
if (!rc)
|
||||
printf("#multi:PASS\n");
|
||||
else
|
||||
printf("#multi:FAIL\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int test_autodetach(void)
|
||||
{
|
||||
__u32 prog_cnt = 4, attach_flags;
|
||||
int allow_prog[2] = {0};
|
||||
__u32 prog_ids[2] = {0};
|
||||
int cg = 0, i, rc = -1;
|
||||
void *ptr = NULL;
|
||||
int attempts;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
|
||||
allow_prog[i] = prog_load_cnt(1, 1 << i);
|
||||
if (!allow_prog[i])
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto err;
|
||||
|
||||
/* create a cgroup, attach two programs and remember their ids */
|
||||
cg = create_and_get_cgroup("/cg_autodetach");
|
||||
if (cg < 0)
|
||||
goto err;
|
||||
|
||||
if (join_cgroup("/cg_autodetach"))
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
|
||||
if (bpf_prog_attach(allow_prog[i], cg, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_MULTI)) {
|
||||
log_err("Attaching prog[%d] to cg:egress", i);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure that programs are attached and run some traffic */
|
||||
assert(bpf_prog_query(cg, BPF_CGROUP_INET_EGRESS, 0, &attach_flags,
|
||||
prog_ids, &prog_cnt) == 0);
|
||||
assert(system(PING_CMD) == 0);
|
||||
|
||||
/* allocate some memory (4Mb) to pin the original cgroup */
|
||||
ptr = malloc(4 * (1 << 20));
|
||||
if (!ptr)
|
||||
goto err;
|
||||
|
||||
/* close programs and cgroup fd */
|
||||
for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
|
||||
close(allow_prog[i]);
|
||||
allow_prog[i] = 0;
|
||||
}
|
||||
|
||||
close(cg);
|
||||
cg = 0;
|
||||
|
||||
/* leave the cgroup and remove it. don't detach programs */
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
/* wait for the asynchronous auto-detachment.
|
||||
* wait for no more than 5 sec and give up.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(prog_ids); i++) {
|
||||
for (attempts = 5; attempts >= 0; attempts--) {
|
||||
int fd = bpf_prog_get_fd_by_id(prog_ids[i]);
|
||||
|
||||
if (fd < 0)
|
||||
break;
|
||||
|
||||
/* don't leave the fd open */
|
||||
close(fd);
|
||||
|
||||
if (!attempts)
|
||||
goto err;
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
err:
|
||||
for (i = 0; i < ARRAY_SIZE(allow_prog); i++)
|
||||
if (allow_prog[i] > 0)
|
||||
close(allow_prog[i]);
|
||||
if (cg)
|
||||
close(cg);
|
||||
free(ptr);
|
||||
cleanup_cgroup_environment();
|
||||
if (!rc)
|
||||
printf("#autodetach:PASS\n");
|
||||
else
|
||||
printf("#autodetach:FAIL\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int (*tests[])(void) = {
|
||||
test_foo_bar,
|
||||
test_multiprog,
|
||||
test_autodetach,
|
||||
};
|
||||
int errors = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++)
|
||||
if (tests[i]())
|
||||
errors++;
|
||||
|
||||
if (errors)
|
||||
printf("test_cgroup_attach:FAIL\n");
|
||||
else
|
||||
printf("test_cgroup_attach:PASS\n");
|
||||
|
||||
return errors ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
382
tools/testing/selftests/bpf/test_hashmap.c
Normal file
382
tools/testing/selftests/bpf/test_hashmap.c
Normal file
|
@ -0,0 +1,382 @@
|
|||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* Tests for libbpf's hashmap.
|
||||
*
|
||||
* Copyright (c) 2019 Facebook
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <linux/err.h>
|
||||
#include "hashmap.h"
|
||||
|
||||
#define CHECK(condition, format...) ({ \
|
||||
int __ret = !!(condition); \
|
||||
if (__ret) { \
|
||||
fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__); \
|
||||
fprintf(stderr, format); \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
size_t hash_fn(const void *k, void *ctx)
|
||||
{
|
||||
return (long)k;
|
||||
}
|
||||
|
||||
bool equal_fn(const void *a, const void *b, void *ctx)
|
||||
{
|
||||
return (long)a == (long)b;
|
||||
}
|
||||
|
||||
static inline size_t next_pow_2(size_t n)
|
||||
{
|
||||
size_t r = 1;
|
||||
|
||||
while (r < n)
|
||||
r <<= 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline size_t exp_cap(size_t sz)
|
||||
{
|
||||
size_t r = next_pow_2(sz);
|
||||
|
||||
if (sz * 4 / 3 > r)
|
||||
r <<= 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
#define ELEM_CNT 62
|
||||
|
||||
int test_hashmap_generic(void)
|
||||
{
|
||||
struct hashmap_entry *entry, *tmp;
|
||||
int err, bkt, found_cnt, i;
|
||||
long long found_msk;
|
||||
struct hashmap *map;
|
||||
|
||||
fprintf(stderr, "%s: ", __func__);
|
||||
|
||||
map = hashmap__new(hash_fn, equal_fn, NULL);
|
||||
if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < ELEM_CNT; i++) {
|
||||
const void *oldk, *k = (const void *)(long)i;
|
||||
void *oldv, *v = (void *)(long)(1024 + i);
|
||||
|
||||
err = hashmap__update(map, k, v, &oldk, &oldv);
|
||||
if (CHECK(err != -ENOENT, "unexpected result: %d\n", err))
|
||||
return 1;
|
||||
|
||||
if (i % 2) {
|
||||
err = hashmap__add(map, k, v);
|
||||
} else {
|
||||
err = hashmap__set(map, k, v, &oldk, &oldv);
|
||||
if (CHECK(oldk != NULL || oldv != NULL,
|
||||
"unexpected k/v: %p=%p\n", oldk, oldv))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CHECK(err, "failed to add k/v %ld = %ld: %d\n",
|
||||
(long)k, (long)v, err))
|
||||
return 1;
|
||||
|
||||
if (CHECK(!hashmap__find(map, k, &oldv),
|
||||
"failed to find key %ld\n", (long)k))
|
||||
return 1;
|
||||
if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CHECK(hashmap__size(map) != ELEM_CNT,
|
||||
"invalid map size: %zu\n", hashmap__size(map)))
|
||||
return 1;
|
||||
if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
|
||||
"unexpected map capacity: %zu\n", hashmap__capacity(map)))
|
||||
return 1;
|
||||
|
||||
found_msk = 0;
|
||||
hashmap__for_each_entry(map, entry, bkt) {
|
||||
long k = (long)entry->key;
|
||||
long v = (long)entry->value;
|
||||
|
||||
found_msk |= 1ULL << k;
|
||||
if (CHECK(v - k != 1024, "invalid k/v pair: %ld = %ld\n", k, v))
|
||||
return 1;
|
||||
}
|
||||
if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
|
||||
"not all keys iterated: %llx\n", found_msk))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < ELEM_CNT; i++) {
|
||||
const void *oldk, *k = (const void *)(long)i;
|
||||
void *oldv, *v = (void *)(long)(256 + i);
|
||||
|
||||
err = hashmap__add(map, k, v);
|
||||
if (CHECK(err != -EEXIST, "unexpected add result: %d\n", err))
|
||||
return 1;
|
||||
|
||||
if (i % 2)
|
||||
err = hashmap__update(map, k, v, &oldk, &oldv);
|
||||
else
|
||||
err = hashmap__set(map, k, v, &oldk, &oldv);
|
||||
|
||||
if (CHECK(err, "failed to update k/v %ld = %ld: %d\n",
|
||||
(long)k, (long)v, err))
|
||||
return 1;
|
||||
if (CHECK(!hashmap__find(map, k, &oldv),
|
||||
"failed to find key %ld\n", (long)k))
|
||||
return 1;
|
||||
if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CHECK(hashmap__size(map) != ELEM_CNT,
|
||||
"invalid updated map size: %zu\n", hashmap__size(map)))
|
||||
return 1;
|
||||
if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
|
||||
"unexpected map capacity: %zu\n", hashmap__capacity(map)))
|
||||
return 1;
|
||||
|
||||
found_msk = 0;
|
||||
hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
|
||||
long k = (long)entry->key;
|
||||
long v = (long)entry->value;
|
||||
|
||||
found_msk |= 1ULL << k;
|
||||
if (CHECK(v - k != 256,
|
||||
"invalid updated k/v pair: %ld = %ld\n", k, v))
|
||||
return 1;
|
||||
}
|
||||
if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
|
||||
"not all keys iterated after update: %llx\n", found_msk))
|
||||
return 1;
|
||||
|
||||
found_cnt = 0;
|
||||
hashmap__for_each_key_entry(map, entry, (void *)0) {
|
||||
found_cnt++;
|
||||
}
|
||||
if (CHECK(!found_cnt, "didn't find any entries for key 0\n"))
|
||||
return 1;
|
||||
|
||||
found_msk = 0;
|
||||
found_cnt = 0;
|
||||
hashmap__for_each_key_entry_safe(map, entry, tmp, (void *)0) {
|
||||
const void *oldk, *k;
|
||||
void *oldv, *v;
|
||||
|
||||
k = entry->key;
|
||||
v = entry->value;
|
||||
|
||||
found_cnt++;
|
||||
found_msk |= 1ULL << (long)k;
|
||||
|
||||
if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
|
||||
"failed to delete k/v %ld = %ld\n",
|
||||
(long)k, (long)v))
|
||||
return 1;
|
||||
if (CHECK(oldk != k || oldv != v,
|
||||
"invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
|
||||
(long)k, (long)v, (long)oldk, (long)oldv))
|
||||
return 1;
|
||||
if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
|
||||
"unexpectedly deleted k/v %ld = %ld\n",
|
||||
(long)oldk, (long)oldv))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CHECK(!found_cnt || !found_msk,
|
||||
"didn't delete any key entries\n"))
|
||||
return 1;
|
||||
if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt,
|
||||
"invalid updated map size (already deleted: %d): %zu\n",
|
||||
found_cnt, hashmap__size(map)))
|
||||
return 1;
|
||||
if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
|
||||
"unexpected map capacity: %zu\n", hashmap__capacity(map)))
|
||||
return 1;
|
||||
|
||||
hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
|
||||
const void *oldk, *k;
|
||||
void *oldv, *v;
|
||||
|
||||
k = entry->key;
|
||||
v = entry->value;
|
||||
|
||||
found_cnt++;
|
||||
found_msk |= 1ULL << (long)k;
|
||||
|
||||
if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
|
||||
"failed to delete k/v %ld = %ld\n",
|
||||
(long)k, (long)v))
|
||||
return 1;
|
||||
if (CHECK(oldk != k || oldv != v,
|
||||
"invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
|
||||
(long)k, (long)v, (long)oldk, (long)oldv))
|
||||
return 1;
|
||||
if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
|
||||
"unexpectedly deleted k/v %ld = %ld\n",
|
||||
(long)k, (long)v))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
|
||||
"not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
|
||||
found_cnt, found_msk))
|
||||
return 1;
|
||||
if (CHECK(hashmap__size(map) != 0,
|
||||
"invalid updated map size (already deleted: %d): %zu\n",
|
||||
found_cnt, hashmap__size(map)))
|
||||
return 1;
|
||||
|
||||
found_cnt = 0;
|
||||
hashmap__for_each_entry(map, entry, bkt) {
|
||||
CHECK(false, "unexpected map entries left: %ld = %ld\n",
|
||||
(long)entry->key, (long)entry->value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hashmap__free(map);
|
||||
hashmap__for_each_entry(map, entry, bkt) {
|
||||
CHECK(false, "unexpected map entries left: %ld = %ld\n",
|
||||
(long)entry->key, (long)entry->value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t collision_hash_fn(const void *k, void *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_hashmap_multimap(void)
|
||||
{
|
||||
void *k1 = (void *)0, *k2 = (void *)1;
|
||||
struct hashmap_entry *entry;
|
||||
struct hashmap *map;
|
||||
long found_msk;
|
||||
int err, bkt;
|
||||
|
||||
fprintf(stderr, "%s: ", __func__);
|
||||
|
||||
/* force collisions */
|
||||
map = hashmap__new(collision_hash_fn, equal_fn, NULL);
|
||||
if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
|
||||
return 1;
|
||||
|
||||
|
||||
/* set up multimap:
|
||||
* [0] -> 1, 2, 4;
|
||||
* [1] -> 8, 16, 32;
|
||||
*/
|
||||
err = hashmap__append(map, k1, (void *)1);
|
||||
if (CHECK(err, "failed to add k/v: %d\n", err))
|
||||
return 1;
|
||||
err = hashmap__append(map, k1, (void *)2);
|
||||
if (CHECK(err, "failed to add k/v: %d\n", err))
|
||||
return 1;
|
||||
err = hashmap__append(map, k1, (void *)4);
|
||||
if (CHECK(err, "failed to add k/v: %d\n", err))
|
||||
return 1;
|
||||
|
||||
err = hashmap__append(map, k2, (void *)8);
|
||||
if (CHECK(err, "failed to add k/v: %d\n", err))
|
||||
return 1;
|
||||
err = hashmap__append(map, k2, (void *)16);
|
||||
if (CHECK(err, "failed to add k/v: %d\n", err))
|
||||
return 1;
|
||||
err = hashmap__append(map, k2, (void *)32);
|
||||
if (CHECK(err, "failed to add k/v: %d\n", err))
|
||||
return 1;
|
||||
|
||||
if (CHECK(hashmap__size(map) != 6,
|
||||
"invalid map size: %zu\n", hashmap__size(map)))
|
||||
return 1;
|
||||
|
||||
/* verify global iteration still works and sees all values */
|
||||
found_msk = 0;
|
||||
hashmap__for_each_entry(map, entry, bkt) {
|
||||
found_msk |= (long)entry->value;
|
||||
}
|
||||
if (CHECK(found_msk != (1 << 6) - 1,
|
||||
"not all keys iterated: %lx\n", found_msk))
|
||||
return 1;
|
||||
|
||||
/* iterate values for key 1 */
|
||||
found_msk = 0;
|
||||
hashmap__for_each_key_entry(map, entry, k1) {
|
||||
found_msk |= (long)entry->value;
|
||||
}
|
||||
if (CHECK(found_msk != (1 | 2 | 4),
|
||||
"invalid k1 values: %lx\n", found_msk))
|
||||
return 1;
|
||||
|
||||
/* iterate values for key 2 */
|
||||
found_msk = 0;
|
||||
hashmap__for_each_key_entry(map, entry, k2) {
|
||||
found_msk |= (long)entry->value;
|
||||
}
|
||||
if (CHECK(found_msk != (8 | 16 | 32),
|
||||
"invalid k2 values: %lx\n", found_msk))
|
||||
return 1;
|
||||
|
||||
fprintf(stderr, "OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_hashmap_empty()
|
||||
{
|
||||
struct hashmap_entry *entry;
|
||||
int bkt;
|
||||
struct hashmap *map;
|
||||
void *k = (void *)0;
|
||||
|
||||
fprintf(stderr, "%s: ", __func__);
|
||||
|
||||
/* force collisions */
|
||||
map = hashmap__new(hash_fn, equal_fn, NULL);
|
||||
if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
|
||||
return 1;
|
||||
|
||||
if (CHECK(hashmap__size(map) != 0,
|
||||
"invalid map size: %zu\n", hashmap__size(map)))
|
||||
return 1;
|
||||
if (CHECK(hashmap__capacity(map) != 0,
|
||||
"invalid map capacity: %zu\n", hashmap__capacity(map)))
|
||||
return 1;
|
||||
if (CHECK(hashmap__find(map, k, NULL), "unexpected find\n"))
|
||||
return 1;
|
||||
if (CHECK(hashmap__delete(map, k, NULL, NULL), "unexpected delete\n"))
|
||||
return 1;
|
||||
|
||||
hashmap__for_each_entry(map, entry, bkt) {
|
||||
CHECK(false, "unexpected iterated entry\n");
|
||||
return 1;
|
||||
}
|
||||
hashmap__for_each_key_entry(map, entry, k) {
|
||||
CHECK(false, "unexpected key entry\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "OK\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool failed = false;
|
||||
|
||||
if (test_hashmap_generic())
|
||||
failed = true;
|
||||
if (test_hashmap_multimap())
|
||||
failed = true;
|
||||
if (test_hashmap_empty())
|
||||
failed = true;
|
||||
|
||||
return failed;
|
||||
}
|
|
@ -745,6 +745,7 @@ static int load_path(const struct sock_addr_test *test, const char *path)
|
|||
attr.file = path;
|
||||
attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR;
|
||||
attr.expected_attach_type = test->expected_attach_type;
|
||||
attr.prog_flags = BPF_F_TEST_RND_HI32;
|
||||
|
||||
if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
|
||||
if (test->expected_result != LOAD_REJECT)
|
||||
|
|
|
@ -414,6 +414,7 @@ int main(int argc, char **argv)
|
|||
struct bpf_prog_load_attr attr = {
|
||||
.file = "test_sock_fields_kern.o",
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||
.prog_flags = BPF_F_TEST_RND_HI32,
|
||||
};
|
||||
int cgroup_fd, egress_fd, ingress_fd, err;
|
||||
struct bpf_program *ingress_prog;
|
||||
|
|
|
@ -148,6 +148,7 @@ static int run_test(int cgfd)
|
|||
memset(&attr, 0, sizeof(attr));
|
||||
attr.file = SOCKET_COOKIE_PROG;
|
||||
attr.prog_type = BPF_PROG_TYPE_UNSPEC;
|
||||
attr.prog_flags = BPF_F_TEST_RND_HI32;
|
||||
|
||||
err = bpf_prog_load_xattr(&attr, &pobj, &prog_fd);
|
||||
if (err) {
|
||||
|
|
|
@ -28,13 +28,6 @@
|
|||
* are established and verdicts are decided.
|
||||
*/
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
struct bpf_map_def SEC("maps") sock_map = {
|
||||
.type = TEST_MAP_TYPE,
|
||||
.key_size = sizeof(int),
|
||||
|
|
40
tools/testing/selftests/bpf/test_stub.c
Normal file
40
tools/testing/selftests/bpf/test_stub.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
/* Copyright (C) 2019 Netronome Systems, Inc. */
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <string.h>
|
||||
|
||||
int bpf_prog_test_load(const char *file, enum bpf_prog_type type,
|
||||
struct bpf_object **pobj, int *prog_fd)
|
||||
{
|
||||
struct bpf_prog_load_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
|
||||
attr.file = file;
|
||||
attr.prog_type = type;
|
||||
attr.expected_attach_type = 0;
|
||||
attr.prog_flags = BPF_F_TEST_RND_HI32;
|
||||
|
||||
return bpf_prog_load_xattr(&attr, pobj, prog_fd);
|
||||
}
|
||||
|
||||
int bpf_test_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)
|
||||
{
|
||||
struct bpf_load_program_attr load_attr;
|
||||
|
||||
memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
|
||||
load_attr.prog_type = type;
|
||||
load_attr.expected_attach_type = 0;
|
||||
load_attr.name = NULL;
|
||||
load_attr.insns = insns;
|
||||
load_attr.insns_cnt = insns_cnt;
|
||||
load_attr.license = license;
|
||||
load_attr.kern_version = kern_version;
|
||||
load_attr.prog_flags = BPF_F_TEST_RND_HI32;
|
||||
|
||||
return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
|
||||
}
|
|
@ -696,30 +696,57 @@ check_err()
|
|||
|
||||
bpf_tunnel_test()
|
||||
{
|
||||
local errors=0
|
||||
|
||||
echo "Testing GRE tunnel..."
|
||||
test_gre
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IP6GRE tunnel..."
|
||||
test_ip6gre
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IP6GRETAP tunnel..."
|
||||
test_ip6gretap
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing ERSPAN tunnel..."
|
||||
test_erspan v2
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IP6ERSPAN tunnel..."
|
||||
test_ip6erspan v2
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing VXLAN tunnel..."
|
||||
test_vxlan
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IP6VXLAN tunnel..."
|
||||
test_ip6vxlan
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing GENEVE tunnel..."
|
||||
test_geneve
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IP6GENEVE tunnel..."
|
||||
test_ip6geneve
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IPIP tunnel..."
|
||||
test_ipip
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IPIP6 tunnel..."
|
||||
test_ipip6
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IPSec tunnel..."
|
||||
test_xfrm_tunnel
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
return $errors
|
||||
}
|
||||
|
||||
trap cleanup 0 3 6
|
||||
|
@ -728,4 +755,9 @@ trap cleanup_exit 2 9
|
|||
cleanup
|
||||
bpf_tunnel_test
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "$(basename $0): ${RED}FAIL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "$(basename $0): ${GREEN}PASS${NC}"
|
||||
exit 0
|
||||
|
|
|
@ -138,32 +138,36 @@ static void bpf_fill_ld_abs_vlan_push_pop(struct bpf_test *self)
|
|||
loop:
|
||||
for (j = 0; j < PUSH_CNT; j++) {
|
||||
insn[i++] = BPF_LD_ABS(BPF_B, 0);
|
||||
insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 2);
|
||||
/* jump to error label */
|
||||
insn[i] = BPF_JMP32_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 3);
|
||||
i++;
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_6);
|
||||
insn[i++] = BPF_MOV64_IMM(BPF_REG_2, 1);
|
||||
insn[i++] = BPF_MOV64_IMM(BPF_REG_3, 2);
|
||||
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_skb_vlan_push),
|
||||
insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 2);
|
||||
insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 3);
|
||||
i++;
|
||||
}
|
||||
|
||||
for (j = 0; j < PUSH_CNT; j++) {
|
||||
insn[i++] = BPF_LD_ABS(BPF_B, 0);
|
||||
insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 2);
|
||||
insn[i] = BPF_JMP32_IMM(BPF_JNE, BPF_REG_0, 0x34, len - i - 3);
|
||||
i++;
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_6);
|
||||
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_skb_vlan_pop),
|
||||
insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 2);
|
||||
insn[i] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, len - i - 3);
|
||||
i++;
|
||||
}
|
||||
if (++k < 5)
|
||||
goto loop;
|
||||
|
||||
for (; i < len - 1; i++)
|
||||
insn[i] = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, 0xbef);
|
||||
for (; i < len - 3; i++)
|
||||
insn[i] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0xbef);
|
||||
insn[len - 3] = BPF_JMP_A(1);
|
||||
/* error label */
|
||||
insn[len - 2] = BPF_MOV32_IMM(BPF_REG_0, 0);
|
||||
insn[len - 1] = BPF_EXIT_INSN();
|
||||
self->prog_len = len;
|
||||
}
|
||||
|
@ -171,8 +175,13 @@ loop:
|
|||
static void bpf_fill_jump_around_ld_abs(struct bpf_test *self)
|
||||
{
|
||||
struct bpf_insn *insn = self->fill_insns;
|
||||
/* jump range is limited to 16 bit. every ld_abs is replaced by 6 insns */
|
||||
unsigned int len = (1 << 15) / 6;
|
||||
/* jump range is limited to 16 bit. every ld_abs is replaced by 6 insns,
|
||||
* but on arches like arm, ppc etc, there will be one BPF_ZEXT inserted
|
||||
* to extend the error value of the inlined ld_abs sequence which then
|
||||
* contains 7 insns. so, set the dividend to 7 so the testcase could
|
||||
* work on all arches.
|
||||
*/
|
||||
unsigned int len = (1 << 15) / 7;
|
||||
int i = 0;
|
||||
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
|
||||
|
@ -210,33 +219,35 @@ static void bpf_fill_rand_ld_dw(struct bpf_test *self)
|
|||
self->retval = (uint32_t)res;
|
||||
}
|
||||
|
||||
/* test the sequence of 1k jumps */
|
||||
#define MAX_JMP_SEQ 8192
|
||||
|
||||
/* test the sequence of 8k jumps */
|
||||
static void bpf_fill_scale1(struct bpf_test *self)
|
||||
{
|
||||
struct bpf_insn *insn = self->fill_insns;
|
||||
int i = 0, k = 0;
|
||||
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
|
||||
/* test to check that the sequence of 1024 jumps is acceptable */
|
||||
while (k++ < 1024) {
|
||||
/* test to check that the long sequence of jumps is acceptable */
|
||||
while (k++ < MAX_JMP_SEQ) {
|
||||
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_prandom_u32);
|
||||
insn[i++] = BPF_JMP_IMM(BPF_JGT, BPF_REG_0, bpf_semi_rand_get(), 2);
|
||||
insn[i++] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, bpf_semi_rand_get(), 2);
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_10);
|
||||
insn[i++] = BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6,
|
||||
-8 * (k % 64 + 1));
|
||||
}
|
||||
/* every jump adds 1024 steps to insn_processed, so to stay exactly
|
||||
* within 1m limit add MAX_TEST_INSNS - 1025 MOVs and 1 EXIT
|
||||
/* every jump adds 1 step to insn_processed, so to stay exactly
|
||||
* within 1m limit add MAX_TEST_INSNS - MAX_JMP_SEQ - 1 MOVs and 1 EXIT
|
||||
*/
|
||||
while (i < MAX_TEST_INSNS - 1025)
|
||||
insn[i++] = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, 42);
|
||||
while (i < MAX_TEST_INSNS - MAX_JMP_SEQ - 1)
|
||||
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 42);
|
||||
insn[i] = BPF_EXIT_INSN();
|
||||
self->prog_len = i + 1;
|
||||
self->retval = 42;
|
||||
}
|
||||
|
||||
/* test the sequence of 1k jumps in inner most function (function depth 8)*/
|
||||
/* test the sequence of 8k jumps in inner most function (function depth 8)*/
|
||||
static void bpf_fill_scale2(struct bpf_test *self)
|
||||
{
|
||||
struct bpf_insn *insn = self->fill_insns;
|
||||
|
@ -248,20 +259,21 @@ static void bpf_fill_scale2(struct bpf_test *self)
|
|||
insn[i++] = BPF_EXIT_INSN();
|
||||
}
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
|
||||
/* test to check that the sequence of 1024 jumps is acceptable */
|
||||
while (k++ < 1024) {
|
||||
/* test to check that the long sequence of jumps is acceptable */
|
||||
k = 0;
|
||||
while (k++ < MAX_JMP_SEQ) {
|
||||
insn[i++] = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_prandom_u32);
|
||||
insn[i++] = BPF_JMP_IMM(BPF_JGT, BPF_REG_0, bpf_semi_rand_get(), 2);
|
||||
insn[i++] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, bpf_semi_rand_get(), 2);
|
||||
insn[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_10);
|
||||
insn[i++] = BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6,
|
||||
-8 * (k % (64 - 4 * FUNC_NEST) + 1));
|
||||
}
|
||||
/* every jump adds 1024 steps to insn_processed, so to stay exactly
|
||||
* within 1m limit add MAX_TEST_INSNS - 1025 MOVs and 1 EXIT
|
||||
/* every jump adds 1 step to insn_processed, so to stay exactly
|
||||
* within 1m limit add MAX_TEST_INSNS - MAX_JMP_SEQ - 1 MOVs and 1 EXIT
|
||||
*/
|
||||
while (i < MAX_TEST_INSNS - 1025)
|
||||
insn[i++] = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, 42);
|
||||
while (i < MAX_TEST_INSNS - MAX_JMP_SEQ - 1)
|
||||
insn[i++] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 42);
|
||||
insn[i] = BPF_EXIT_INSN();
|
||||
self->prog_len = i + 1;
|
||||
self->retval = 42;
|
||||
|
@ -870,7 +882,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
|||
if (fixup_skips != skips)
|
||||
return;
|
||||
|
||||
pflags = 0;
|
||||
pflags = BPF_F_TEST_RND_HI32;
|
||||
if (test->flags & F_LOAD_WITH_STRICT_ALIGNMENT)
|
||||
pflags |= BPF_F_STRICT_ALIGNMENT;
|
||||
if (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)
|
||||
|
|
99
tools/testing/selftests/bpf/test_xdping.sh
Executable file
99
tools/testing/selftests/bpf/test_xdping.sh
Executable file
|
@ -0,0 +1,99 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# xdping tests
|
||||
# Here we setup and teardown configuration required to run
|
||||
# xdping, exercising its options.
|
||||
#
|
||||
# Setup is similar to test_tunnel tests but without the tunnel.
|
||||
#
|
||||
# Topology:
|
||||
# ---------
|
||||
# root namespace | tc_ns0 namespace
|
||||
# |
|
||||
# ---------- | ----------
|
||||
# | veth1 | --------- | veth0 |
|
||||
# ---------- peer ----------
|
||||
#
|
||||
# Device Configuration
|
||||
# --------------------
|
||||
# Root namespace with BPF
|
||||
# Device names and addresses:
|
||||
# veth1 IP: 10.1.1.200
|
||||
# xdp added to veth1, xdpings originate from here.
|
||||
#
|
||||
# Namespace tc_ns0 with BPF
|
||||
# Device names and addresses:
|
||||
# veth0 IPv4: 10.1.1.100
|
||||
# For some tests xdping run in server mode here.
|
||||
#
|
||||
|
||||
readonly TARGET_IP="10.1.1.100"
|
||||
readonly TARGET_NS="xdp_ns0"
|
||||
|
||||
readonly LOCAL_IP="10.1.1.200"
|
||||
|
||||
setup()
|
||||
{
|
||||
ip netns add $TARGET_NS
|
||||
ip link add veth0 type veth peer name veth1
|
||||
ip link set veth0 netns $TARGET_NS
|
||||
ip netns exec $TARGET_NS ip addr add ${TARGET_IP}/24 dev veth0
|
||||
ip addr add ${LOCAL_IP}/24 dev veth1
|
||||
ip netns exec $TARGET_NS ip link set veth0 up
|
||||
ip link set veth1 up
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
set +e
|
||||
ip netns delete $TARGET_NS 2>/dev/null
|
||||
ip link del veth1 2>/dev/null
|
||||
if [[ $server_pid -ne 0 ]]; then
|
||||
kill -TERM $server_pid
|
||||
fi
|
||||
}
|
||||
|
||||
test()
|
||||
{
|
||||
client_args="$1"
|
||||
server_args="$2"
|
||||
|
||||
echo "Test client args '$client_args'; server args '$server_args'"
|
||||
|
||||
server_pid=0
|
||||
if [[ -n "$server_args" ]]; then
|
||||
ip netns exec $TARGET_NS ./xdping $server_args &
|
||||
server_pid=$!
|
||||
sleep 10
|
||||
fi
|
||||
./xdping $client_args $TARGET_IP
|
||||
|
||||
if [[ $server_pid -ne 0 ]]; then
|
||||
kill -TERM $server_pid
|
||||
server_pid=0
|
||||
fi
|
||||
|
||||
echo "Test client args '$client_args'; server args '$server_args': PASS"
|
||||
}
|
||||
|
||||
set -e
|
||||
|
||||
server_pid=0
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup
|
||||
|
||||
for server_args in "" "-I veth0 -s -S" ; do
|
||||
# client in skb mode
|
||||
client_args="-I veth1 -S"
|
||||
test "$client_args" "$server_args"
|
||||
|
||||
# client with count of 10 RTT measurements.
|
||||
client_args="-I veth1 -S -c 10"
|
||||
test "$client_args" "$server_args"
|
||||
done
|
||||
|
||||
echo "OK. All tests passed"
|
||||
exit 0
|
|
@ -30,9 +30,7 @@ int load_kallsyms(void)
|
|||
if (!f)
|
||||
return -ENOENT;
|
||||
|
||||
while (!feof(f)) {
|
||||
if (!fgets(buf, sizeof(buf), f))
|
||||
break;
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
|
||||
break;
|
||||
if (!addr)
|
||||
|
|
258
tools/testing/selftests/bpf/xdping.c
Normal file
258
tools/testing/selftests/bpf/xdping.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/resource.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "bpf/bpf.h"
|
||||
#include "bpf/libbpf.h"
|
||||
|
||||
#include "xdping.h"
|
||||
|
||||
static int ifindex;
|
||||
static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
|
||||
|
||||
static void cleanup(int sig)
|
||||
{
|
||||
bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
|
||||
if (sig)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int get_stats(int fd, __u16 count, __u32 raddr)
|
||||
{
|
||||
struct pinginfo pinginfo = { 0 };
|
||||
char inaddrbuf[INET_ADDRSTRLEN];
|
||||
struct in_addr inaddr;
|
||||
__u16 i;
|
||||
|
||||
inaddr.s_addr = raddr;
|
||||
|
||||
printf("\nXDP RTT data:\n");
|
||||
|
||||
if (bpf_map_lookup_elem(fd, &raddr, &pinginfo)) {
|
||||
perror("bpf_map_lookup elem: ");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (pinginfo.times[i] == 0)
|
||||
break;
|
||||
|
||||
printf("64 bytes from %s: icmp_seq=%d ttl=64 time=%#.5f ms\n",
|
||||
inet_ntop(AF_INET, &inaddr, inaddrbuf,
|
||||
sizeof(inaddrbuf)),
|
||||
count + i + 1,
|
||||
(double)pinginfo.times[i]/1000000);
|
||||
}
|
||||
|
||||
if (i < count) {
|
||||
fprintf(stderr, "Expected %d samples, got %d.\n", count, i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bpf_map_delete_elem(fd, &raddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [OPTS] -I interface destination\n\n"
|
||||
"OPTS:\n"
|
||||
" -c count Stop after sending count requests\n"
|
||||
" (default %d, max %d)\n"
|
||||
" -I interface interface name\n"
|
||||
" -N Run in driver mode\n"
|
||||
" -s Server mode\n"
|
||||
" -S Run in skb mode\n",
|
||||
prog, XDPING_DEFAULT_COUNT, XDPING_MAX_COUNT);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
__u32 mode_flags = XDP_FLAGS_DRV_MODE | XDP_FLAGS_SKB_MODE;
|
||||
struct addrinfo *a, hints = { .ai_family = AF_INET };
|
||||
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
|
||||
__u16 count = XDPING_DEFAULT_COUNT;
|
||||
struct pinginfo pinginfo = { 0 };
|
||||
const char *optstr = "c:I:NsS";
|
||||
struct bpf_program *main_prog;
|
||||
int prog_fd = -1, map_fd = -1;
|
||||
struct sockaddr_in rin;
|
||||
struct bpf_object *obj;
|
||||
struct bpf_map *map;
|
||||
char *ifname = NULL;
|
||||
char filename[256];
|
||||
int opt, ret = 1;
|
||||
__u32 raddr = 0;
|
||||
int server = 0;
|
||||
char cmd[256];
|
||||
|
||||
while ((opt = getopt(argc, argv, optstr)) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
count = atoi(optarg);
|
||||
if (count < 1 || count > XDPING_MAX_COUNT) {
|
||||
fprintf(stderr,
|
||||
"min count is 1, max count is %d\n",
|
||||
XDPING_MAX_COUNT);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
ifname = optarg;
|
||||
ifindex = if_nametoindex(ifname);
|
||||
if (!ifindex) {
|
||||
fprintf(stderr, "Could not get interface %s\n",
|
||||
ifname);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
xdp_flags |= XDP_FLAGS_DRV_MODE;
|
||||
break;
|
||||
case 's':
|
||||
/* use server program */
|
||||
server = 1;
|
||||
break;
|
||||
case 'S':
|
||||
xdp_flags |= XDP_FLAGS_SKB_MODE;
|
||||
break;
|
||||
default:
|
||||
show_usage(basename(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ifname) {
|
||||
show_usage(basename(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
if (!server && optind == argc) {
|
||||
show_usage(basename(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((xdp_flags & mode_flags) == mode_flags) {
|
||||
fprintf(stderr, "-N or -S can be specified, not both.\n");
|
||||
show_usage(basename(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!server) {
|
||||
/* Only supports IPv4; see hints initiailization above. */
|
||||
if (getaddrinfo(argv[optind], NULL, &hints, &a) || !a) {
|
||||
fprintf(stderr, "Could not resolve %s\n", argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
memcpy(&rin, a->ai_addr, sizeof(rin));
|
||||
raddr = rin.sin_addr.s_addr;
|
||||
freeaddrinfo(a);
|
||||
}
|
||||
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
|
||||
perror("setrlimit(RLIMIT_MEMLOCK)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
|
||||
|
||||
if (bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd)) {
|
||||
fprintf(stderr, "load of %s failed\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
main_prog = bpf_object__find_program_by_title(obj,
|
||||
server ? "xdpserver" :
|
||||
"xdpclient");
|
||||
if (main_prog)
|
||||
prog_fd = bpf_program__fd(main_prog);
|
||||
if (!main_prog || prog_fd < 0) {
|
||||
fprintf(stderr, "could not find xdping program");
|
||||
return 1;
|
||||
}
|
||||
|
||||
map = bpf_map__next(NULL, obj);
|
||||
if (map)
|
||||
map_fd = bpf_map__fd(map);
|
||||
if (!map || map_fd < 0) {
|
||||
fprintf(stderr, "Could not find ping map");
|
||||
goto done;
|
||||
}
|
||||
|
||||
signal(SIGINT, cleanup);
|
||||
signal(SIGTERM, cleanup);
|
||||
|
||||
printf("Setting up XDP for %s, please wait...\n", ifname);
|
||||
|
||||
printf("XDP setup disrupts network connectivity, hit Ctrl+C to quit\n");
|
||||
|
||||
if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) {
|
||||
fprintf(stderr, "Link set xdp fd failed for %s\n", ifname);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (server) {
|
||||
close(prog_fd);
|
||||
close(map_fd);
|
||||
printf("Running server on %s; press Ctrl+C to exit...\n",
|
||||
ifname);
|
||||
do { } while (1);
|
||||
}
|
||||
|
||||
/* Start xdping-ing from last regular ping reply, e.g. for a count
|
||||
* of 10 ICMP requests, we start xdping-ing using reply with seq number
|
||||
* 10. The reason the last "real" ping RTT is much higher is that
|
||||
* the ping program sees the ICMP reply associated with the last
|
||||
* XDP-generated packet, so ping doesn't get a reply until XDP is done.
|
||||
*/
|
||||
pinginfo.seq = htons(count);
|
||||
pinginfo.count = count;
|
||||
|
||||
if (bpf_map_update_elem(map_fd, &raddr, &pinginfo, BPF_ANY)) {
|
||||
fprintf(stderr, "could not communicate with BPF map: %s\n",
|
||||
strerror(errno));
|
||||
cleanup(0);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We need to wait for XDP setup to complete. */
|
||||
sleep(10);
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "ping -c %d -I %s %s",
|
||||
count, ifname, argv[optind]);
|
||||
|
||||
printf("\nNormal ping RTT data\n");
|
||||
printf("[Ignore final RTT; it is distorted by XDP using the reply]\n");
|
||||
|
||||
ret = system(cmd);
|
||||
|
||||
if (!ret)
|
||||
ret = get_stats(map_fd, count, raddr);
|
||||
|
||||
cleanup(0);
|
||||
|
||||
done:
|
||||
if (prog_fd > 0)
|
||||
close(prog_fd);
|
||||
if (map_fd > 0)
|
||||
close(map_fd);
|
||||
|
||||
return ret;
|
||||
}
|
13
tools/testing/selftests/bpf/xdping.h
Normal file
13
tools/testing/selftests/bpf/xdping.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
|
||||
|
||||
#define XDPING_MAX_COUNT 10
|
||||
#define XDPING_DEFAULT_COUNT 4
|
||||
|
||||
struct pinginfo {
|
||||
__u64 start;
|
||||
__be16 seq;
|
||||
__u16 count;
|
||||
__u32 pad;
|
||||
__u64 times[XDPING_MAX_COUNT];
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue