The operations of relay map create, update_elem, and output are tested. The test is borrowed from ringbuf tests, where 2 samples are written into the relay channel, and we get the samples by reading the files. Overwriting mode is also tested, where the size of relay buffer equals sample size and just the last sample can be seen. Signed-off-by: Philo Lu <lulie@xxxxxxxxxxxxxxxxx> --- tools/include/uapi/linux/bpf.h | 7 + tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/config | 1 + .../selftests/bpf/prog_tests/relay_map.c | 197 ++++++++++++++++++ .../selftests/bpf/progs/test_relay_map.c | 69 ++++++ 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/relay_map.c create mode 100644 tools/testing/selftests/bpf/progs/test_relay_map.c diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 7f24d898efbb..1e545bfe701f 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -951,6 +951,7 @@ enum bpf_map_type { BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_USER_RINGBUF, BPF_MAP_TYPE_CGRP_STORAGE, + BPF_MAP_TYPE_RELAY, }; /* Note that tracing related programs such as @@ -1330,6 +1331,9 @@ enum { /* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */ BPF_F_PATH_FD = (1U << 14), + +/* Enable overwrite for relay map */ + BPF_F_OVERWRITE = (1U << 15), }; /* Flags for BPF_PROG_QUERY. */ @@ -1401,6 +1405,9 @@ union bpf_attr { * BPF_MAP_TYPE_BLOOM_FILTER - the lowest 4 bits indicate the * number of hash functions (if 0, the bloom filter will default * to using 5 hash functions). + * + * BPF_MAP_TYPE_RELAY - the lowest 32 bits indicate the number of + * relay subbufs (if 0, the number will be set to 8 by default). */ __u64 map_extra; }; diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 617ae55c3bb5..8cebb3810d50 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -427,7 +427,7 @@ LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \ LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ trace_printk.c trace_vprintk.c map_ptr_kern.c \ core_kern.c core_kern_overflow.c test_ringbuf.c \ - test_ringbuf_map_key.c + test_ringbuf_map_key.c test_relay_map.c # Generate both light skeleton and libbpf skeleton for these LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index c125c441abc7..8de1adf587f0 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -87,3 +87,4 @@ CONFIG_VSOCKETS=y CONFIG_VXLAN=y CONFIG_XDP_SOCKETS=y CONFIG_XFRM_INTERFACE=y +CONFIG_RELAY=y diff --git a/tools/testing/selftests/bpf/prog_tests/relay_map.c b/tools/testing/selftests/bpf/prog_tests/relay_map.c new file mode 100644 index 000000000000..bd9c1e62ca78 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/relay_map.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <linux/compiler.h> +#include <linux/bpf.h> +#include <sys/sysinfo.h> +#include <test_progs.h> +#include <sched.h> + +#include "test_relay_map.lskel.h" + +static int duration; + +/* file names in debugfs */ +static const char dirname[] = "relay_map_selftest"; +static const char mapname[] = "relay_map"; +static const char mapname_ow[] = "relay_map_ow"; +struct relay_sample { + int pid; + int seq; + long value; + char comm[16]; +}; + +static int sample_cnt; +static int overwrite; + +static void process_sample(struct relay_sample *s) +{ + ++sample_cnt; + + switch (s->seq) { + case 0: + /* sample1 will not appear in overwrite mode */ + CHECK(overwrite != 0, "overwrite_mode", + "sample1 appears in overwrite mode\n"); + CHECK(s->value != 333, "sample1_value", "exp %ld, got %ld\n", + 333L, s->value); + break; + case 1: + CHECK(s->value != 777, "sample2_value", "exp %ld, got %ld\n", + 777L, s->value); + break; + default: + break; + } +} + +static int relaymap_read(const char *mapname) +{ + int cpu = libbpf_num_possible_cpus(); + char name[NAME_MAX]; + struct relay_sample data; + int maxloop; + FILE *fp; + + for (int i = 0; i < cpu; ++i) { + sprintf(name, "/sys/kernel/debug/%s/%s%d", dirname, mapname, i); + fp = fopen(name, "r"); + if (CHECK(!fp, "fopen", "relay file open failed\n")) + return -1; + + maxloop = 0; + while (fread(&data, sizeof(data), 1, fp)) { + process_sample(&data); + + /* just 2 samples output */ + if (++maxloop > 2) + return -1; + } + } + return 0; +} + +static struct test_relay_map_lskel *skel; + +static void trigger_samples(void) +{ + skel->bss->dropped = 0; + skel->bss->total = 0; + skel->bss->seq = 0; + + /* trigger exactly two samples */ + skel->bss->value = 333; + syscall(__NR_getpgid); + skel->bss->value = 777; + syscall(__NR_getpgid); +} + +static void relaymap_subtest(void) +{ + int err, map_fd; + + skel = test_relay_map_lskel__open(); + if (CHECK(!skel, "skel_open", "skeleton open failed\n")) + return; + + /* setup relay param */ + skel->maps.relay_map.max_entries = 1024; + + err = test_relay_map_lskel__load(skel); + if (CHECK(err, "skel_load", "skeleton load failed\n")) + goto cleanup; + + /* only trigger BPF program for current process */ + skel->bss->pid = getpid(); + + /* turn off overwrite */ + skel->bss->overwrite_enable = 0; + overwrite = skel->bss->overwrite_enable; + + err = test_relay_map_lskel__attach(skel); + if (CHECK(err, "skel_attach", "skeleton attachment failed: %d\n", err)) + goto cleanup; + + /* before file setup - output failed */ + trigger_samples(); + CHECK(skel->bss->dropped != 2, "err_dropped", "exp %ld, got %ld\n", + 0L, skel->bss->dropped); + CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", + 2L, skel->bss->total); + + /* after file setup - output succ */ + map_fd = skel->maps.relay_map.map_fd; + err = bpf_map_update_elem(map_fd, NULL, dirname, 0); + if (CHECK(err, "map_update", "map update failed: %d\n", err)) + goto cleanup; + trigger_samples(); + CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", + 0L, skel->bss->dropped); + CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", + 2L, skel->bss->total); + + sample_cnt = 0; + err = relaymap_read(mapname); + CHECK(sample_cnt != 2, "sample_cnt", "exp %d samples, got %d\n", + 2, sample_cnt); + + test_relay_map_lskel__detach(skel); +cleanup: + test_relay_map_lskel__destroy(skel); +} + +static void relaymap_overwrite_subtest(void) +{ + int err, map_fd; + + skel = test_relay_map_lskel__open(); + if (CHECK(!skel, "skel_open", "skeleton open failed\n")) + return; + + /* To test overwrite mode, we create subbuf of one-sample size */ + skel->maps.relay_map_ow.max_entries = sizeof(struct relay_sample); + + err = test_relay_map_lskel__load(skel); + if (CHECK(err, "skel_load", "skeleton load failed\n")) + goto cleanup; + + /* only trigger BPF program for current process */ + skel->bss->pid = getpid(); + + /* turn on overwrite */ + skel->bss->overwrite_enable = 1; + overwrite = skel->bss->overwrite_enable; + + err = test_relay_map_lskel__attach(skel); + if (CHECK(err, "skel_attach", "skeleton attachment failed: %d\n", err)) + goto cleanup; + + map_fd = skel->maps.relay_map_ow.map_fd; + err = bpf_map_update_elem(map_fd, NULL, dirname, 0); + if (CHECK(err, "map_update", "map update failed: %d\n", err)) + goto cleanup; + trigger_samples(); + /* relay_write never fails whether overwriting or not */ + CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n", + 0L, skel->bss->dropped); + CHECK(skel->bss->total != 2, "err_total", "exp %ld, got %ld\n", + 2L, skel->bss->total); + + /* 2 samples are output, but only the last (val=777) could be seen */ + sample_cnt = 0; + err = relaymap_read(mapname_ow); + CHECK(sample_cnt != 1, "sample_cnt", "exp %d samples, got %d\n", + 1, sample_cnt); + + test_relay_map_lskel__detach(skel); +cleanup: + test_relay_map_lskel__destroy(skel); +} + +void test_relaymap(void) +{ + if (test__start_subtest("relaymap")) + relaymap_subtest(); + if (test__start_subtest("relaymap_overwrite")) + relaymap_overwrite_subtest(); +} diff --git a/tools/testing/selftests/bpf/progs/test_relay_map.c b/tools/testing/selftests/bpf/progs/test_relay_map.c new file mode 100644 index 000000000000..1adf1be8e125 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_relay_map.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +extern int bpf_relay_output(struct bpf_map *map, void *data, + __u64 data__sz, __u32 flags) __ksym; + +struct relay_sample { + int pid; + int seq; + long value; + char comm[16]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_RELAY); + __uint(max_entries, 1024); +} relay_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RELAY); + __uint(map_flags, BPF_F_OVERWRITE); + __uint(max_entries, 1024); + __uint(map_extra, 1); +} relay_map_ow SEC(".maps"); + +/* inputs */ +int pid = 0; +long value = 0; +int overwrite_enable = 0; + +/* outputs */ +long total = 0; +long dropped = 0; + +/* inner state */ +long seq = 0; + +SEC("fentry/" SYS_PREFIX "sys_getpgid") +int test_bpf_relaymap(void *ctx) +{ + int cur_pid = bpf_get_current_pid_tgid() >> 32; + struct relay_sample sample; + int ret = 0; + + if (cur_pid != pid) + return 0; + + sample.pid = pid; + bpf_get_current_comm(sample.comm, sizeof(sample.comm)); + sample.value = value; + sample.seq = seq++; + __sync_fetch_and_add(&total, 1); + + if (overwrite_enable) + ret = bpf_relay_output((struct bpf_map *)&relay_map_ow, + &sample, sizeof(sample), 0); + else + ret = bpf_relay_output((struct bpf_map *)&relay_map, + &sample, sizeof(sample), 0); + + if (ret) + __sync_fetch_and_add(&dropped, 1); + + return 0; +} -- 2.32.0.3.g01195cf9f