-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tools/bpf: add selftest in test_progs for bpf_send_signal() helper
The test covered both nmi and tracepoint perf events. $ ./test_progs ... test_send_signal_tracepoint:PASS:tracepoint 0 nsec ... test_send_signal_common:PASS:tracepoint 0 nsec ... test_send_signal_common:PASS:perf_event 0 nsec ... test_send_signal:OK Acked-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
- Loading branch information
Yonghong Song
authored and
Daniel Borkmann
committed
May 24, 2019
1 parent
edaccf8
commit 16f0efc
Showing
3 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"; |