All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Eduard Zingerman <eddyz87@gmail.com>,
	kernel-team@fb.com
Subject: [PATCH bpf-next 09/12] selftests/bpf: Add C tests for 16-byte returns in R0:R2
Date: Wed,  8 Jul 2026 13:10:25 -0700	[thread overview]
Message-ID: <20260708201025.2160583-1-yonghong.song@linux.dev> (raw)
In-Reply-To: <20260708200939.2153664-1-yonghong.song@linux.dev>

Add selftests that exercise a 16-byte return value passed in the R0:R2
register pair, expressed in C so they rely on the compiler lowering the
register-pair return. Tests include an __int128 return (from a subprogram
and a kfunc), a 16-byte struct return (from static and global subprograms
and a kfunc) and a 16-byte union return.

The R0:R2 return convention is only emitted by LLVM 23 and newer, so the
programs are gated on __clang_major__ >= 23; with an older compiler they
return a sentinel and the "int128_c", "struct_c", "global_struct_c",
"kfunc_struct_c" and "union_c" subtests are skipped.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 .../selftests/bpf/prog_tests/aggregate_ret.c  | 96 ++++++++++++++++++
 .../bpf/progs/aggregate_ret_int128_c.c        | 48 +++++++++
 .../bpf/progs/aggregate_ret_struct_c.c        | 98 +++++++++++++++++++
 .../bpf/progs/aggregate_ret_union_c.c         | 51 ++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    | 14 +++
 .../bpf/test_kmods/bpf_testmod_kfunc.h        | 11 +++
 6 files changed, 318 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
 create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_int128_c.c
 create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_struct_c.c
 create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_union_c.c

diff --git a/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c b/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
new file mode 100644
index 000000000000..d4ecc19684ce
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <test_progs.h>
+#include <network_helpers.h>
+#include "aggregate_ret_int128_c.skel.h"
+#include "aggregate_ret_struct_c.skel.h"
+#include "aggregate_ret_union_c.skel.h"
+
+/*
+ * Run one program and check its result. The program returns 0 on success, or
+ * 1..4 identifying which half of which aggregate (R0:R2) return value came back
+ * wrong. A return of -1 means the program was built with an LLVM that lacks
+ * R0:R2 16-byte return support, in which case the subtest is skipped.
+ */
+static void run_prog(struct bpf_program *prog, bool may_skip)
+{
+	char buf[64] = {};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buf,
+		.data_size_in = sizeof(buf),
+		.repeat = 1,
+	);
+	int err;
+
+	err = bpf_prog_test_run_opts(bpf_program__fd(prog), &topts);
+	if (!ASSERT_OK(err, "test_run"))
+		return;
+
+	if (may_skip && (int)topts.retval == -1) {
+		test__skip();
+		return;
+	}
+
+	ASSERT_EQ(topts.retval, 0, "aggregate_ret_result");
+}
+
+void test_aggregate_ret(void)
+{
+	struct aggregate_ret_struct_c *skel_struct_c;
+	struct aggregate_ret_union_c *skel_union_c;
+	struct aggregate_ret_int128_c *skel_c;
+
+	skel_c = aggregate_ret_int128_c__open_and_load();
+	if (!ASSERT_OK_PTR(skel_c, "skel_c_open_load"))
+		return;
+
+	/* C variant (__int128): relies on LLVM 23 R0:R2 codegen, skipped otherwise. */
+	if (test__start_subtest("int128_c"))
+		run_prog(skel_c->progs.aggregate_ret_test, true);
+
+	aggregate_ret_int128_c__destroy(skel_c);
+
+	skel_struct_c = aggregate_ret_struct_c__open_and_load();
+	if (!ASSERT_OK_PTR(skel_struct_c, "skel_struct_c_open_load"))
+		return;
+
+	/*
+	 * C variant returning a 16-byte struct by value: exercises the struct
+	 * branch of bpf_ret_reg_pair(). Also LLVM 23 only.
+	 */
+	if (test__start_subtest("struct_c"))
+		run_prog(skel_struct_c->progs.aggregate_ret_struct_c_test, true);
+
+	/*
+	 * Same 16-byte struct return, but from a global (non-static) callee:
+	 * exercises the compiler lowering a global struct-returning function to
+	 * R0:R2 and the verifier's global return path end to end. Also LLVM 23
+	 * only.
+	 */
+	if (test__start_subtest("global_struct_c"))
+		run_prog(skel_struct_c->progs.aggregate_ret_global_struct_c_test, true);
+
+	/*
+	 * Same 16-byte struct, but returned by value from a kfunc: exercises
+	 * the struct branch of check_kfunc_call() and the JIT RDX->R2 move.
+	 * Also LLVM 23 only.
+	 */
+	if (test__start_subtest("kfunc_struct_c"))
+		run_prog(skel_struct_c->progs.aggregate_ret_kfunc_struct_c_test, true);
+
+	aggregate_ret_struct_c__destroy(skel_struct_c);
+
+	skel_union_c = aggregate_ret_union_c__open_and_load();
+	if (!ASSERT_OK_PTR(skel_union_c, "skel_union_c_open_load"))
+		return;
+
+	/*
+	 * C variant returning a 16-byte union by value: exercises the union BTF
+	 * kind through the same struct branch of bpf_ret_reg_pair().
+	 * Also LLVM 23 only.
+	 */
+	if (test__start_subtest("union_c"))
+		run_prog(skel_union_c->progs.aggregate_ret_union_c_test, true);
+
+	aggregate_ret_union_c__destroy(skel_union_c);
+}
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_int128_c.c b/tools/testing/selftests/bpf/progs/aggregate_ret_int128_c.c
new file mode 100644
index 000000000000..0b0c4fa52b62
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_int128_c.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+#if __clang_major__ >= 23
+
+typedef unsigned __int128 u128;
+
+static __attribute__((noinline)) u128 make_i128(__u64 a, __u64 b)
+{
+	return ((u128)a << 64) | b;
+}
+
+SEC("tc")
+int aggregate_ret_test(struct __sk_buff *skb)
+{
+	__u64 a = skb->len;
+	__u64 b = skb->len ^ 0xdeadbeefULL;
+	u128 v;
+
+	v = make_i128(a, b);
+	if ((__u64)(v >> 64) != a)
+		return 1;
+	if ((__u64)v != b)
+		return 2;
+
+	v = bpf_kfunc_call_test_i128(a, b);
+	if ((__u64)(v >> 64) != a)
+		return 3;
+	if ((__u64)v != b)
+		return 4;
+
+	return 0;
+}
+
+#else
+
+SEC("tc")
+int aggregate_ret_test(struct __sk_buff *skb)
+{
+	return -1;
+}
+
+#endif
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_struct_c.c b/tools/testing/selftests/bpf/progs/aggregate_ret_struct_c.c
new file mode 100644
index 000000000000..ad8d44755d24
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_struct_c.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+#if __clang_major__ >= 23
+
+struct pair {
+	__u64 hi;
+	__u64 lo;
+};
+
+static __attribute__((noinline)) struct pair make_pair(__u64 a, __u64 b)
+{
+	struct pair p = { .hi = a, .lo = b };
+
+	return p;
+}
+
+SEC("tc")
+int aggregate_ret_struct_c_test(struct __sk_buff *skb)
+{
+	__u64 a = skb->len;
+	__u64 b = skb->len ^ 0xdeadbeefULL;
+	struct pair p;
+
+	p = make_pair(a, b);
+	if (p.hi != a)
+		return 1;
+	if (p.lo != b)
+		return 2;
+
+	return 0;
+}
+
+__attribute__((noinline)) struct pair make_pair_global(__u64 a, __u64 b)
+{
+	struct pair p = { .hi = a, .lo = b };
+
+	return p;
+}
+
+SEC("tc")
+int aggregate_ret_global_struct_c_test(struct __sk_buff *skb)
+{
+	__u64 a = skb->len;
+	__u64 b = skb->len ^ 0xcafef00dULL;
+	struct pair p;
+
+	p = make_pair_global(a, b);
+	if (p.hi != a)
+		return 1;
+	if (p.lo != b)
+		return 2;
+
+	return 0;
+}
+
+SEC("tc")
+int aggregate_ret_kfunc_struct_c_test(struct __sk_buff *skb)
+{
+	__u64 a = skb->len;
+	__u64 b = skb->len ^ 0xfeedfaceULL;
+	struct bpf_kfunc_ret_ll p;
+
+	p = bpf_kfunc_call_test_ret_ll(a, b);
+	if (p.a != a)
+		return 1;
+	if (p.b != b)
+		return 2;
+
+	return 0;
+}
+
+#else
+
+SEC("tc")
+int aggregate_ret_struct_c_test(struct __sk_buff *skb)
+{
+	return -1;
+}
+
+SEC("tc")
+int aggregate_ret_global_struct_c_test(struct __sk_buff *skb)
+{
+	return -1;
+}
+
+SEC("tc")
+int aggregate_ret_kfunc_struct_c_test(struct __sk_buff *skb)
+{
+	return -1;
+}
+
+#endif
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_union_c.c b/tools/testing/selftests/bpf/progs/aggregate_ret_union_c.c
new file mode 100644
index 000000000000..53e21753291b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_union_c.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+#if __clang_major__ >= 23
+
+union pair {
+	__u64 halves[2];
+	struct {
+		__u64 lo;
+		__u64 hi;
+	} parts;
+};
+
+static __attribute__((noinline)) union pair make_pair(__u64 a, __u64 b)
+{
+	union pair p;
+
+	p.parts.lo = a;
+	p.parts.hi = b;
+	return p;
+}
+
+SEC("tc")
+int aggregate_ret_union_c_test(struct __sk_buff *skb)
+{
+	__u64 a = skb->len;
+	__u64 b = skb->len ^ 0xdeadbeefULL;
+	union pair p;
+
+	p = make_pair(a, b);
+	if (p.parts.lo != a)
+		return 1;
+	if (p.parts.hi != b)
+		return 2;
+
+	return 0;
+}
+
+#else
+
+SEC("tc")
+int aggregate_ret_union_c_test(struct __sk_buff *skb)
+{
+	return -1;
+}
+
+#endif
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 30f1cd23093c..3511a4873d6c 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -825,6 +825,18 @@ __bpf_kfunc int bpf_kfunc_call_test5(u8 a, u16 b, u32 c)
 	return 0;
 }
 
+__bpf_kfunc __int128 bpf_kfunc_call_test_i128(u64 a, u64 b)
+{
+	return ((__int128)a << 64) | b;
+}
+
+__bpf_kfunc struct bpf_kfunc_ret_ll bpf_kfunc_call_test_ret_ll(u64 a, u64 b)
+{
+	struct bpf_kfunc_ret_ll r = { .a = a, .b = b };
+
+	return r;
+}
+
 __bpf_kfunc u64 bpf_kfunc_call_stack_arg(u64 a, u64 b, u64 c, u64 d,
 					 u64 e, u64 f, u64 g, u64 h,
 					 u64 i, u64 j)
@@ -1358,6 +1370,8 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test2)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test3)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test4)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test5)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_i128)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_ll)
 BTF_ID_FLAGS(func, bpf_kfunc_call_stack_arg)
 BTF_ID_FLAGS(func, bpf_kfunc_call_stack_arg_ptr)
 BTF_ID_FLAGS(func, bpf_kfunc_call_stack_arg_mix)
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index c36bb911defa..34d0a352e70d 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -55,6 +55,15 @@ struct prog_test_big_arg {
 	__u64 b;
 };
 
+/*
+ * A 16-byte struct returned by value from a kfunc, returned in the R0:R2
+ * register pair.
+ */
+struct bpf_kfunc_ret_ll {	/* 16 bytes: R0:R2 */
+	__u64 a;
+	__u64 b;
+};
+
 struct prog_test_fail1 {
 	void *p;
 	int x;
@@ -118,6 +127,8 @@ int bpf_kfunc_call_test2(struct sock *sk, __u32 a, __u32 b) __ksym;
 struct sock *bpf_kfunc_call_test3(struct sock *sk) __ksym;
 long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym;
 int bpf_kfunc_call_test5(__u8 a, __u16 b, __u32 c) __ksym;
+__int128 bpf_kfunc_call_test_i128(__u64 a, __u64 b) __ksym;
+struct bpf_kfunc_ret_ll bpf_kfunc_call_test_ret_ll(__u64 a, __u64 b) __ksym;
 __u64 bpf_kfunc_call_stack_arg(__u64 a, __u64 b, __u64 c, __u64 d,
 			       __u64 e, __u64 f, __u64 g, __u64 h,
 			       __u64 i, __u64 j) __ksym;
-- 
2.53.0-Meta


  parent reply	other threads:[~2026-07-08 20:10 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-08 20:09 [PATCH bpf-next 00/12] bpf: Support 16-byte return values in the R0:R2 register pair Yonghong Song
2026-07-08 20:09 ` [PATCH bpf-next 01/12] bpf: Factor check_global_ret_scalar_reg() out of the global return check Yonghong Song
2026-07-08 20:09 ` [PATCH bpf-next 02/12] bpf: Add verifier support for 16-byte returns in R0:R2 Yonghong Song
2026-07-08 21:17   ` bot+bpf-ci
2026-07-09 16:40     ` Yonghong Song
2026-07-09 22:21   ` Eduard Zingerman
2026-07-10  4:57     ` Yonghong Song
2026-07-08 20:09 ` [PATCH bpf-next 03/12] bpf: Wire up JIT support for 16-byte kfunc returns Yonghong Song
2026-07-10  0:06   ` Eduard Zingerman
2026-07-08 20:10 ` [PATCH bpf-next 04/12] bpf: Track R2 of register-pair returns in precision backtracking Yonghong Song
2026-07-08 21:10   ` bot+bpf-ci
2026-07-09 16:41     ` Yonghong Song
2026-07-10  0:31   ` Eduard Zingerman
2026-07-08 20:10 ` [PATCH bpf-next 05/12] bpf: Account R2 of register-pair returns in live register analysis Yonghong Song
2026-07-08 21:10   ` bot+bpf-ci
2026-07-09 21:07   ` Eduard Zingerman
2026-07-10  5:06     ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 06/12] bpf: Force JIT for programs using the R0:R2 register pair Yonghong Song
2026-07-08 21:10   ` bot+bpf-ci
2026-07-09  3:15   ` Leon Hwang
2026-07-09 16:44     ` Yonghong Song
2026-07-09 20:30   ` Eduard Zingerman
2026-07-10  5:07     ` Yonghong Song
2026-07-09 22:27   ` Eduard Zingerman
2026-07-10  5:09     ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 07/12] bpf: Reject >8 byte return values on return-reading trampoline paths Yonghong Song
2026-07-09  3:16   ` Leon Hwang
2026-07-09 16:52     ` Yonghong Song
2026-07-10  2:01       ` Leon Hwang
2026-07-09 23:08   ` Eduard Zingerman
2026-07-10  2:02     ` Leon Hwang
2026-07-10  5:27       ` Yonghong Song
2026-07-10  5:12     ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 08/12] bpf: Enable 16-byte aggregate return types Yonghong Song
2026-07-08 20:28   ` sashiko-bot
2026-07-09 19:40     ` Yonghong Song
2026-07-10  0:45   ` Eduard Zingerman
2026-07-10  5:29     ` Yonghong Song
2026-07-08 20:10 ` Yonghong Song [this message]
2026-07-08 20:24   ` [PATCH bpf-next 09/12] selftests/bpf: Add C tests for 16-byte returns in R0:R2 sashiko-bot
2026-07-09 19:51     ` Yonghong Song
2026-07-08 21:10   ` bot+bpf-ci
2026-07-09 19:54     ` Yonghong Song
2026-07-10  1:19   ` Eduard Zingerman
2026-07-10  5:32     ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 10/12] selftests/bpf: Add inline-asm and subprog tests for R0:R2 returns Yonghong Song
2026-07-08 20:28   ` sashiko-bot
2026-07-09 19:57     ` Yonghong Song
2026-07-10  1:38   ` Eduard Zingerman
2026-07-10  5:35     ` Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 11/12] selftests/bpf: Cover tracing on >8 and <=16 byte return targets Yonghong Song
2026-07-08 20:10 ` [PATCH bpf-next 12/12] Documentation/bpf: Document 16-byte kfunc return values in R0:R2 Yonghong Song
2026-07-10  0:56 ` [PATCH bpf-next 00/12] bpf: Support 16-byte return values in the R0:R2 register pair Eduard Zingerman
2026-07-10  6:00   ` Yonghong Song
2026-07-10  6:13     ` Eduard Zingerman
2026-07-10 15:18       ` Yonghong Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260708201025.2160583-1-yonghong.song@linux.dev \
    --to=yonghong.song@linux.dev \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=kernel-team@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.