Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Daniel Borkmann says: ==================== pull-request: bpf-next 2020-02-21 The following pull-request contains BPF updates for your *net-next* tree. We've added 25 non-merge commits during the last 4 day(s) which contain a total of 33 files changed, 2433 insertions(+), 161 deletions(-). The main changes are: 1) Allow for adding TCP listen sockets into sock_map/hash so they can be used with reuseport BPF programs, from Jakub Sitnicki. 2) Add a new bpf_program__set_attach_target() helper for adding libbpf support to specify the tracepoint/function dynamically, from Eelco Chaudron. 3) Add bpf_read_branch_records() BPF helper which helps use cases like profile guided optimizations, from Daniel Xu. 4) Enable bpf_perf_event_read_value() in all tracing programs, from Song Liu. 5) Relax BTF mandatory check if only used for libbpf itself e.g. to process BTF defined maps, from Andrii Nakryiko. 6) Move BPF selftests -mcpu compilation attribute from 'probe' to 'v3' as it has been observed that former fails in envs with low memlock, from Yonghong Song. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
170
tools/testing/selftests/bpf/prog_tests/perf_branches.c
Normal file
170
tools/testing/selftests/bpf/prog_tests/perf_branches.c
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/socket.h>
|
||||
#include <test_progs.h>
|
||||
#include "bpf/libbpf_internal.h"
|
||||
#include "test_perf_branches.skel.h"
|
||||
|
||||
static void check_good_sample(struct test_perf_branches *skel)
|
||||
{
|
||||
int written_global = skel->bss->written_global_out;
|
||||
int required_size = skel->bss->required_size_out;
|
||||
int written_stack = skel->bss->written_stack_out;
|
||||
int pbe_size = sizeof(struct perf_branch_entry);
|
||||
int duration = 0;
|
||||
|
||||
if (CHECK(!skel->bss->valid, "output not valid",
|
||||
"no valid sample from prog"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* It's hard to validate the contents of the branch entries b/c it
|
||||
* would require some kind of disassembler and also encoding the
|
||||
* valid jump instructions for supported architectures. So just check
|
||||
* the easy stuff for now.
|
||||
*/
|
||||
CHECK(required_size <= 0, "read_branches_size", "err %d\n", required_size);
|
||||
CHECK(written_stack < 0, "read_branches_stack", "err %d\n", written_stack);
|
||||
CHECK(written_stack % pbe_size != 0, "read_branches_stack",
|
||||
"stack bytes written=%d not multiple of struct size=%d\n",
|
||||
written_stack, pbe_size);
|
||||
CHECK(written_global < 0, "read_branches_global", "err %d\n", written_global);
|
||||
CHECK(written_global % pbe_size != 0, "read_branches_global",
|
||||
"global bytes written=%d not multiple of struct size=%d\n",
|
||||
written_global, pbe_size);
|
||||
CHECK(written_global < written_stack, "read_branches_size",
|
||||
"written_global=%d < written_stack=%d\n", written_global, written_stack);
|
||||
}
|
||||
|
||||
static void check_bad_sample(struct test_perf_branches *skel)
|
||||
{
|
||||
int written_global = skel->bss->written_global_out;
|
||||
int required_size = skel->bss->required_size_out;
|
||||
int written_stack = skel->bss->written_stack_out;
|
||||
int duration = 0;
|
||||
|
||||
if (CHECK(!skel->bss->valid, "output not valid",
|
||||
"no valid sample from prog"))
|
||||
return;
|
||||
|
||||
CHECK((required_size != -EINVAL && required_size != -ENOENT),
|
||||
"read_branches_size", "err %d\n", required_size);
|
||||
CHECK((written_stack != -EINVAL && written_stack != -ENOENT),
|
||||
"read_branches_stack", "written %d\n", written_stack);
|
||||
CHECK((written_global != -EINVAL && written_global != -ENOENT),
|
||||
"read_branches_global", "written %d\n", written_global);
|
||||
}
|
||||
|
||||
static void test_perf_branches_common(int perf_fd,
|
||||
void (*cb)(struct test_perf_branches *))
|
||||
{
|
||||
struct test_perf_branches *skel;
|
||||
int err, i, duration = 0;
|
||||
bool detached = false;
|
||||
struct bpf_link *link;
|
||||
volatile int j = 0;
|
||||
cpu_set_t cpu_set;
|
||||
|
||||
skel = test_perf_branches__open_and_load();
|
||||
if (CHECK(!skel, "test_perf_branches_load",
|
||||
"perf_branches skeleton failed\n"))
|
||||
return;
|
||||
|
||||
/* attach perf_event */
|
||||
link = bpf_program__attach_perf_event(skel->progs.perf_branches, perf_fd);
|
||||
if (CHECK(IS_ERR(link), "attach_perf_event", "err %ld\n", PTR_ERR(link)))
|
||||
goto out_destroy_skel;
|
||||
|
||||
/* generate some branches on cpu 0 */
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(0, &cpu_set);
|
||||
err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
|
||||
if (CHECK(err, "set_affinity", "cpu #0, err %d\n", err))
|
||||
goto out_destroy;
|
||||
/* spin the loop for a while (random high number) */
|
||||
for (i = 0; i < 1000000; ++i)
|
||||
++j;
|
||||
|
||||
test_perf_branches__detach(skel);
|
||||
detached = true;
|
||||
|
||||
cb(skel);
|
||||
out_destroy:
|
||||
bpf_link__destroy(link);
|
||||
out_destroy_skel:
|
||||
if (!detached)
|
||||
test_perf_branches__detach(skel);
|
||||
test_perf_branches__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_perf_branches_hw(void)
|
||||
{
|
||||
struct perf_event_attr attr = {0};
|
||||
int duration = 0;
|
||||
int pfd;
|
||||
|
||||
/* create perf event */
|
||||
attr.size = sizeof(attr);
|
||||
attr.type = PERF_TYPE_HARDWARE;
|
||||
attr.config = PERF_COUNT_HW_CPU_CYCLES;
|
||||
attr.freq = 1;
|
||||
attr.sample_freq = 4000;
|
||||
attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
|
||||
attr.branch_sample_type = PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY;
|
||||
pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
|
||||
|
||||
/*
|
||||
* Some setups don't support branch records (virtual machines, !x86),
|
||||
* so skip test in this case.
|
||||
*/
|
||||
if (pfd == -1) {
|
||||
if (errno == ENOENT || errno == EOPNOTSUPP) {
|
||||
printf("%s:SKIP:no PERF_SAMPLE_BRANCH_STACK\n",
|
||||
__func__);
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
if (CHECK(pfd < 0, "perf_event_open", "err %d errno %d\n",
|
||||
pfd, errno))
|
||||
return;
|
||||
}
|
||||
|
||||
test_perf_branches_common(pfd, check_good_sample);
|
||||
|
||||
close(pfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests negative case -- run bpf_read_branch_records() on improperly configured
|
||||
* perf event.
|
||||
*/
|
||||
static void test_perf_branches_no_hw(void)
|
||||
{
|
||||
struct perf_event_attr attr = {0};
|
||||
int duration = 0;
|
||||
int pfd;
|
||||
|
||||
/* create perf event */
|
||||
attr.size = sizeof(attr);
|
||||
attr.type = PERF_TYPE_SOFTWARE;
|
||||
attr.config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
attr.freq = 1;
|
||||
attr.sample_freq = 4000;
|
||||
pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
|
||||
if (CHECK(pfd < 0, "perf_event_open", "err %d\n", pfd))
|
||||
return;
|
||||
|
||||
test_perf_branches_common(pfd, check_bad_sample);
|
||||
|
||||
close(pfd);
|
||||
}
|
||||
|
||||
void test_perf_branches(void)
|
||||
{
|
||||
if (test__start_subtest("perf_branches_hw"))
|
||||
test_perf_branches_hw();
|
||||
if (test__start_subtest("perf_branches_no_hw"))
|
||||
test_perf_branches_no_hw();
|
||||
}
|
@@ -36,6 +36,7 @@ static int result_map, tmp_index_ovr_map, linum_map, data_check_map;
|
||||
static __u32 expected_results[NR_RESULTS];
|
||||
static int sk_fds[REUSEPORT_ARRAY_SIZE];
|
||||
static int reuseport_array = -1, outer_map = -1;
|
||||
static enum bpf_map_type inner_map_type;
|
||||
static int select_by_skb_data_prog;
|
||||
static int saved_tcp_syncookie = -1;
|
||||
static struct bpf_object *obj;
|
||||
@@ -63,13 +64,15 @@ static union sa46 {
|
||||
} \
|
||||
})
|
||||
|
||||
static int create_maps(void)
|
||||
static int create_maps(enum bpf_map_type inner_type)
|
||||
{
|
||||
struct bpf_create_map_attr attr = {};
|
||||
|
||||
inner_map_type = inner_type;
|
||||
|
||||
/* Creating reuseport_array */
|
||||
attr.name = "reuseport_array";
|
||||
attr.map_type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY;
|
||||
attr.map_type = inner_type;
|
||||
attr.key_size = sizeof(__u32);
|
||||
attr.value_size = sizeof(__u32);
|
||||
attr.max_entries = REUSEPORT_ARRAY_SIZE;
|
||||
@@ -728,12 +731,36 @@ static void cleanup_per_test(bool no_inner_map)
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
if (outer_map != -1)
|
||||
if (outer_map != -1) {
|
||||
close(outer_map);
|
||||
if (reuseport_array != -1)
|
||||
outer_map = -1;
|
||||
}
|
||||
|
||||
if (reuseport_array != -1) {
|
||||
close(reuseport_array);
|
||||
if (obj)
|
||||
reuseport_array = -1;
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
bpf_object__close(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
|
||||
memset(expected_results, 0, sizeof(expected_results));
|
||||
}
|
||||
|
||||
static const char *maptype_str(enum bpf_map_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
|
||||
return "reuseport_sockarray";
|
||||
case BPF_MAP_TYPE_SOCKMAP:
|
||||
return "sockmap";
|
||||
case BPF_MAP_TYPE_SOCKHASH:
|
||||
return "sockhash";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *family_str(sa_family_t family)
|
||||
@@ -781,13 +808,21 @@ static void test_config(int sotype, sa_family_t family, bool inany)
|
||||
const struct test *t;
|
||||
|
||||
for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
|
||||
snprintf(s, sizeof(s), "%s/%s %s %s",
|
||||
snprintf(s, sizeof(s), "%s %s/%s %s %s",
|
||||
maptype_str(inner_map_type),
|
||||
family_str(family), sotype_str(sotype),
|
||||
inany ? "INANY" : "LOOPBACK", t->name);
|
||||
|
||||
if (!test__start_subtest(s))
|
||||
continue;
|
||||
|
||||
if (sotype == SOCK_DGRAM &&
|
||||
inner_map_type != BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {
|
||||
/* SOCKMAP/SOCKHASH don't support UDP yet */
|
||||
test__skip();
|
||||
continue;
|
||||
}
|
||||
|
||||
setup_per_test(sotype, family, inany, t->no_inner_map);
|
||||
t->fn(sotype, family);
|
||||
cleanup_per_test(t->no_inner_map);
|
||||
@@ -816,13 +851,20 @@ static void test_all(void)
|
||||
test_config(c->sotype, c->family, c->inany);
|
||||
}
|
||||
|
||||
void test_select_reuseport(void)
|
||||
void test_map_type(enum bpf_map_type mt)
|
||||
{
|
||||
if (create_maps())
|
||||
if (create_maps(mt))
|
||||
goto out;
|
||||
if (prepare_bpf_obj())
|
||||
goto out;
|
||||
|
||||
test_all();
|
||||
out:
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void test_select_reuseport(void)
|
||||
{
|
||||
saved_tcp_fo = read_int_sysctl(TCP_FO_SYSCTL);
|
||||
if (saved_tcp_fo < 0)
|
||||
goto out;
|
||||
@@ -835,8 +877,9 @@ void test_select_reuseport(void)
|
||||
if (disable_syncookie())
|
||||
goto out;
|
||||
|
||||
test_all();
|
||||
test_map_type(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY);
|
||||
test_map_type(BPF_MAP_TYPE_SOCKMAP);
|
||||
test_map_type(BPF_MAP_TYPE_SOCKHASH);
|
||||
out:
|
||||
cleanup();
|
||||
restore_sysctls();
|
||||
}
|
||||
|
124
tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
Normal file
124
tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2020 Cloudflare
|
||||
/*
|
||||
* Tests for sockmap/sockhash holding kTLS sockets.
|
||||
*/
|
||||
|
||||
#include "test_progs.h"
|
||||
|
||||
#define MAX_TEST_NAME 80
|
||||
#define TCP_ULP 31
|
||||
|
||||
static int tcp_server(int family)
|
||||
{
|
||||
int err, s;
|
||||
|
||||
s = socket(family, SOCK_STREAM, 0);
|
||||
if (CHECK_FAIL(s == -1)) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = listen(s, SOMAXCONN);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("listen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int disconnect(int fd)
|
||||
{
|
||||
struct sockaddr unspec = { AF_UNSPEC };
|
||||
|
||||
return connect(fd, &unspec, sizeof(unspec));
|
||||
}
|
||||
|
||||
/* Disconnect (unhash) a kTLS socket after removing it from sockmap. */
|
||||
static void test_sockmap_ktls_disconnect_after_delete(int family, int map)
|
||||
{
|
||||
struct sockaddr_storage addr = {0};
|
||||
socklen_t len = sizeof(addr);
|
||||
int err, cli, srv, zero = 0;
|
||||
|
||||
srv = tcp_server(family);
|
||||
if (srv == -1)
|
||||
return;
|
||||
|
||||
err = getsockname(srv, (struct sockaddr *)&addr, &len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("getsockopt");
|
||||
goto close_srv;
|
||||
}
|
||||
|
||||
cli = socket(family, SOCK_STREAM, 0);
|
||||
if (CHECK_FAIL(cli == -1)) {
|
||||
perror("socket");
|
||||
goto close_srv;
|
||||
}
|
||||
|
||||
err = connect(cli, (struct sockaddr *)&addr, len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("connect");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = bpf_map_update_elem(map, &zero, &cli, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_map_update_elem");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = setsockopt(cli, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls"));
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("setsockopt(TCP_ULP)");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = bpf_map_delete_elem(map, &zero);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_map_delete_elem");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = disconnect(cli);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("disconnect");
|
||||
|
||||
close_cli:
|
||||
close(cli);
|
||||
close_srv:
|
||||
close(srv);
|
||||
}
|
||||
|
||||
static void run_tests(int family, enum bpf_map_type map_type)
|
||||
{
|
||||
char test_name[MAX_TEST_NAME];
|
||||
int map;
|
||||
|
||||
map = bpf_create_map(map_type, sizeof(int), sizeof(int), 1, 0);
|
||||
if (CHECK_FAIL(map == -1)) {
|
||||
perror("bpf_map_create");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(test_name, MAX_TEST_NAME,
|
||||
"sockmap_ktls disconnect_after_delete %s %s",
|
||||
family == AF_INET ? "IPv4" : "IPv6",
|
||||
map_type == BPF_MAP_TYPE_SOCKMAP ? "SOCKMAP" : "SOCKHASH");
|
||||
if (!test__start_subtest(test_name))
|
||||
return;
|
||||
|
||||
test_sockmap_ktls_disconnect_after_delete(family, map);
|
||||
|
||||
close(map);
|
||||
}
|
||||
|
||||
void test_sockmap_ktls(void)
|
||||
{
|
||||
run_tests(AF_INET, BPF_MAP_TYPE_SOCKMAP);
|
||||
run_tests(AF_INET, BPF_MAP_TYPE_SOCKHASH);
|
||||
run_tests(AF_INET6, BPF_MAP_TYPE_SOCKMAP);
|
||||
run_tests(AF_INET6, BPF_MAP_TYPE_SOCKHASH);
|
||||
}
|
1496
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
Normal file
1496
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -55,31 +55,40 @@ void test_trampoline_count(void)
|
||||
/* attach 'allowed' 40 trampoline programs */
|
||||
for (i = 0; i < MAX_TRAMP_PROGS; i++) {
|
||||
obj = bpf_object__open_file(object, NULL);
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj))) {
|
||||
obj = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK(err, "obj_load", "err %d\n", err))
|
||||
goto cleanup;
|
||||
inst[i].obj = obj;
|
||||
obj = NULL;
|
||||
|
||||
if (rand() % 2) {
|
||||
link = load(obj, fentry_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link)))
|
||||
link = load(inst[i].obj, fentry_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link))) {
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
inst[i].link_fentry = link;
|
||||
} else {
|
||||
link = load(obj, fexit_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link)))
|
||||
link = load(inst[i].obj, fexit_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link))) {
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
inst[i].link_fexit = link;
|
||||
}
|
||||
}
|
||||
|
||||
/* and try 1 extra.. */
|
||||
obj = bpf_object__open_file(object, NULL);
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj))) {
|
||||
obj = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK(err, "obj_load", "err %d\n", err))
|
||||
@@ -104,7 +113,9 @@ void test_trampoline_count(void)
|
||||
cleanup_extra:
|
||||
bpf_object__close(obj);
|
||||
cleanup:
|
||||
while (--i) {
|
||||
if (i >= MAX_TRAMP_PROGS)
|
||||
i = MAX_TRAMP_PROGS - 1;
|
||||
for (; i >= 0; i--) {
|
||||
bpf_link__destroy(inst[i].link_fentry);
|
||||
bpf_link__destroy(inst[i].link_fexit);
|
||||
bpf_object__close(inst[i].obj);
|
||||
|
@@ -14,7 +14,7 @@ void test_xdp_bpf2bpf(void)
|
||||
struct test_xdp *pkt_skel = NULL;
|
||||
struct test_xdp_bpf2bpf *ftrace_skel = NULL;
|
||||
struct vip key4 = {.protocol = 6, .family = AF_INET};
|
||||
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
|
||||
struct bpf_program *prog;
|
||||
|
||||
/* Load XDP program to introspect */
|
||||
pkt_skel = test_xdp__open_and_load();
|
||||
@@ -27,11 +27,21 @@ void test_xdp_bpf2bpf(void)
|
||||
bpf_map_update_elem(map_fd, &key4, &value4, 0);
|
||||
|
||||
/* Load trace program */
|
||||
opts.attach_prog_fd = pkt_fd,
|
||||
ftrace_skel = test_xdp_bpf2bpf__open_opts(&opts);
|
||||
ftrace_skel = test_xdp_bpf2bpf__open();
|
||||
if (CHECK(!ftrace_skel, "__open", "ftrace skeleton failed\n"))
|
||||
goto out;
|
||||
|
||||
/* Demonstrate the bpf_program__set_attach_target() API rather than
|
||||
* the load with options, i.e. opts.attach_prog_fd.
|
||||
*/
|
||||
prog = ftrace_skel->progs.trace_on_entry;
|
||||
bpf_program__set_expected_attach_type(prog, BPF_TRACE_FENTRY);
|
||||
bpf_program__set_attach_target(prog, pkt_fd, "_xdp_tx_iptunnel");
|
||||
|
||||
prog = ftrace_skel->progs.trace_on_exit;
|
||||
bpf_program__set_expected_attach_type(prog, BPF_TRACE_FEXIT);
|
||||
bpf_program__set_attach_target(prog, pkt_fd, "_xdp_tx_iptunnel");
|
||||
|
||||
err = test_xdp_bpf2bpf__load(ftrace_skel);
|
||||
if (CHECK(err, "__load", "ftrace skeleton failed\n"))
|
||||
goto out;
|
||||
|
Reference in New Issue
Block a user