diff mbox series

[bpf-next,v1,3/3] selftests/bpf: add bpf relay map selftests

Message ID 20231227100130.84501-4-lulie@linux.alibaba.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series bpf: introduce BPF_MAP_TYPE_RELAY | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-32 fail Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-7 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 fail Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 fail Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 fail Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-23 fail Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-26 fail Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 fail Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 fail Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 fail Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 fail Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 fail Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-37 fail Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 fail Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 fail Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 fail Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-41 fail Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-16 fail Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-14 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success SINGLE THREAD; Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers warning 1 maintainers not CCed: linux-kselftest@vger.kernel.org
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning CHECK: Alignment should match open parenthesis WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 81 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-13 fail Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc

Commit Message

Philo Lu Dec. 27, 2023, 10:01 a.m. UTC
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@linux.alibaba.com>
---
 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

Comments

Yafang Shao Dec. 27, 2023, 1:42 p.m. UTC | #1
On Wed, Dec 27, 2023 at 6:01 PM Philo Lu <lulie@linux.alibaba.com> wrote:
>
> 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@linux.alibaba.com>
> ---
>  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");

fclose() is missed.

> +               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
>
diff mbox series

Patch

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