[PATCH bpf-next v1 11/11] selftests/bpf: Add tests for prog streams

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add selftests to stress test the various facets of the stream API,
memory allocation pattern, and ensuring dumping support is tested and
functional. Create symlink to bpftool stream.bpf.c and use it to test
the support to dump messages to ringbuf in user space, and verify
output.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>
---
 .../testing/selftests/bpf/prog_tests/stream.c |  95 +++++++++++++
 tools/testing/selftests/bpf/progs/stream.c    | 127 ++++++++++++++++++
 .../selftests/bpf/progs/stream_bpftool.c      |   1 +
 .../testing/selftests/bpf/progs/stream_fail.c |  90 +++++++++++++
 4 files changed, 313 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/stream.c
 create mode 100644 tools/testing/selftests/bpf/progs/stream.c
 create mode 120000 tools/testing/selftests/bpf/progs/stream_bpftool.c
 create mode 100644 tools/testing/selftests/bpf/progs/stream_fail.c

diff --git a/tools/testing/selftests/bpf/prog_tests/stream.c b/tools/testing/selftests/bpf/prog_tests/stream.c
new file mode 100644
index 000000000000..7b97b783ff1f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/stream.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <sys/mman.h>
+
+#include "stream.skel.h"
+#include "stream_fail.skel.h"
+
+#include "stream_bpftool.skel.h"
+
+void test_stream_failure(void)
+{
+	RUN_TESTS(stream_fail);
+}
+
+void test_stream_success(void)
+{
+	RUN_TESTS(stream);
+	RUN_TESTS(stream_bpftool);
+	return;
+}
+
+typedef int (*sample_cb_t)(void *, void *, size_t);
+
+static void stream_ringbuf_output(int prog_id, sample_cb_t sample_cb)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+	struct ring_buffer *ringbuf;
+	struct stream_bpftool *skel;
+	int fd, ret;
+
+	skel = stream_bpftool__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "stream_bpftool_open_and_load"))
+		return;
+
+	fd = bpf_map__fd(skel->maps.ringbuf);
+
+	ringbuf = ring_buffer__new(fd, sample_cb, NULL, NULL);
+	if (!ASSERT_OK_PTR(ringbuf, "ringbuf_new"))
+		goto end;
+
+	skel->bss->prog_id = prog_id;
+	skel->bss->stream_id = 1;
+	do {
+		skel->bss->written_count = skel->bss->written_size = 0;
+		ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.bpftool_dump_prog_stream), &opts);
+		if (ret)
+			break;
+		ret = ring_buffer__consume_n(ringbuf, skel->bss->written_count);
+		if (!ASSERT_EQ(ret, skel->bss->written_count, "consume"))
+			break;
+		ret = 0;
+	} while (opts.retval == EAGAIN);
+
+	ASSERT_OK(ret, "ret");
+	ASSERT_EQ(opts.retval, 0, "retval");
+
+end:
+	stream_bpftool__destroy(skel);
+}
+
+int cnt = 0;
+
+static int process_sample(void *ctx, void *data, size_t len)
+{
+	char buf[64];
+
+	snprintf(buf, sizeof(buf), "num=%d\n", cnt++);
+	ASSERT_TRUE(strcmp(buf, (char *)data) == 0, "sample strcmp");
+	return 0;
+}
+
+void test_stream_output(void)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, opts);
+	struct bpf_prog_info info = {};
+	__u32 info_len = sizeof(info);
+	struct stream *skel;
+	int ret;
+
+	skel = stream__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
+		return;
+
+	ASSERT_OK(bpf_prog_get_info_by_fd(bpf_program__fd(skel->progs.stream_test_output), &info, &info_len), "get info");
+	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.stream_test_output), &opts);
+	ASSERT_OK(ret, "ret");
+	ASSERT_OK(opts.retval, "retval");
+	stream_ringbuf_output(info.id, process_sample);
+
+	ASSERT_EQ(cnt, 1000, "cnt");
+
+	stream__destroy(skel);
+	return;
+}
diff --git a/tools/testing/selftests/bpf/progs/stream.c b/tools/testing/selftests/bpf/progs/stream.c
new file mode 100644
index 000000000000..14cb8690824f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/stream.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+#define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+#define STREAM_STR (u64)(_STR _STR _STR _STR)
+
+static __noinline int stream_exercise(int id, int N)
+{
+	struct bpf_stream_elem *elem, *earr[56] = {};
+	struct bpf_stream *stream;
+	int ret;
+	u32 i;
+
+	if (N > 56)
+		return 56;
+
+	stream = bpf_stream_get(id, NULL);
+	if (!stream)
+		return 1;
+	for (i = 0; i < N; i++)
+		if ((ret = bpf_stream_vprintk(stream, "%llu%s", &(u64[]){i, STREAM_STR}, 16)) < 0) {
+			bpf_printk("bpf_stream_vprintk ret=%d", ret);
+			return 2;
+		}
+	ret = 0;
+	for (i = 0; i < N; i++) {
+		elem = bpf_stream_next_elem(stream);
+		if (!elem) {
+			ret = 4;
+			break;
+		}
+		earr[i] = elem;
+	}
+	elem = bpf_stream_next_elem(stream);
+	if (elem) {
+		bpf_stream_free_elem(elem);
+		ret = 5;
+	}
+	for (i = 0; i < N; i++)
+		if (earr[i])
+			bpf_stream_free_elem(earr[i]);
+	return ret;
+}
+
+static __noinline int stream_exercise_nums(int id)
+{
+	int ret = 0;
+
+	ret = ret ?: stream_exercise(id, 56);
+	ret = ret ?: stream_exercise(id, 42);
+	ret = ret ?: stream_exercise(id, 28);
+	ret = ret ?: stream_exercise(id, 10);
+	ret = ret ?: stream_exercise(id, 1);
+
+	return ret;
+}
+
+SEC("syscall")
+__success __retval(0)
+int stream_test(void *ctx)
+{
+	unsigned long flags;
+	int ret;
+
+	bpf_local_irq_save(&flags);
+	bpf_repeat(50) {
+		ret = stream_exercise_nums(BPF_STDOUT);
+		if (ret)
+			break;
+	}
+	if (ret) {
+		bpf_local_irq_restore(&flags);
+		return ret;
+	}
+	bpf_repeat(100) {
+		ret = stream_exercise_nums(BPF_STDERR);
+		if (ret)
+			break;
+	}
+	bpf_local_irq_restore(&flags);
+
+	if (ret)
+		return ret;
+
+	ret = stream_exercise_nums(BPF_STDOUT);
+	if (ret)
+		return ret;
+	return stream_exercise_nums(BPF_STDERR);
+}
+
+SEC("syscall")
+__success __retval(0)
+int stream_test_output(void *ctx)
+{
+	for (int i = 0; i < 1000; i++)
+		bpf_stream_printk(BPF_STDOUT, "num=%d\n", i);
+	return 0;
+}
+
+SEC("syscall")
+__success __retval(0)
+int stream_test_limit(void *ctx)
+{
+	struct bpf_stream *stream;
+	bool failed = false;
+
+	stream = bpf_stream_get(BPF_STDOUT, NULL);
+	if (!stream)
+		return 2;
+
+	bpf_repeat(BPF_MAX_LOOPS) {
+		failed = bpf_stream_vprintk(stream, "%s%s%s", &(u64[]){STREAM_STR, STREAM_STR}, 16) != 0;
+		if (failed)
+			break;
+	}
+
+	if (failed)
+		return 0;
+	return 1;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/stream_bpftool.c b/tools/testing/selftests/bpf/progs/stream_bpftool.c
new file mode 120000
index 000000000000..5904c0d92edc
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/stream_bpftool.c
@@ -0,0 +1 @@
+../../../../bpf/bpftool/skeleton/stream.bpf.c
\ No newline at end of file
diff --git a/tools/testing/selftests/bpf/progs/stream_fail.c b/tools/testing/selftests/bpf/progs/stream_fail.c
new file mode 100644
index 000000000000..50f70b9878b8
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/stream_fail.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_misc.h"
+
+SEC("syscall")
+__failure __msg("R1 type=trusted_ptr_or_null_ expected=")
+int stream_get_trusted(void *ctx) {
+	struct bpf_stream *stream;
+
+	stream = bpf_stream_get(BPF_STDOUT, NULL);
+	bpf_this_cpu_ptr(stream);
+	return 0;
+}
+
+SEC("tc")
+__failure __msg("calling kernel function bpf_prog_stream_get is not allowed")
+int stream_get_prog_fail(void *ctx) {
+	struct bpf_stream *stream;
+
+	stream = bpf_prog_stream_get(BPF_STDOUT, 0);
+	if (!stream)
+		return 0;
+	bpf_this_cpu_ptr(stream);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("R1 type=ptr_or_null_ expected=")
+int stream_get_prog_trusted(void *ctx) {
+	struct bpf_stream *stream;
+
+	stream = bpf_prog_stream_get(BPF_STDOUT, 0);
+	bpf_this_cpu_ptr(stream);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("Unreleased reference")
+int stream_get_put_missing(void *ctx) {
+	struct bpf_stream *stream;
+
+	stream = bpf_prog_stream_get(BPF_STDOUT, 0);
+	if (!stream)
+		return 0;
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("R1 must be referenced or trusted")
+int stream_next_untrusted_arg(void *ctx)
+{
+	struct bpf_stream *stream;
+
+	stream = bpf_core_cast((void *)0xdeadbeef, typeof(*stream));
+	bpf_stream_next_elem(stream);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("Possibly NULL pointer passed")
+int stream_next_null_arg(void *ctx)
+{
+	bpf_stream_next_elem(NULL);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("R1 must be referenced or trusted")
+int stream_vprintk_untrusted_arg(void *ctx)
+{
+	struct bpf_stream *stream;
+
+	stream = bpf_core_cast((void *)0xfaceb00c, typeof(*stream));
+	bpf_stream_vprintk(stream, "", NULL, 0);
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("Possibly NULL pointer passed")
+int stream_vprintk_null_arg(void *ctx)
+{
+	bpf_stream_vprintk(NULL, "", NULL, 0);
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.47.1





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux