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 10/12] selftests/bpf: Add inline-asm and subprog tests for R0:R2 returns
Date: Wed, 8 Jul 2026 13:10:30 -0700 [thread overview]
Message-ID: <20260708201030.2161016-1-yonghong.song@linux.dev> (raw)
In-Reply-To: <20260708200939.2153664-1-yonghong.song@linux.dev>
Add inline-asm and subprogram tests for more coverage, including
BPF-to-BPF cases, kfunc cases, backtracking and liveness. Both positive
and negative tests are added.
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
.../selftests/bpf/prog_tests/aggregate_ret.c | 25 ++
.../selftests/bpf/progs/aggregate_ret_func.c | 361 ++++++++++++++++++
.../selftests/bpf/progs/aggregate_ret_kfunc.c | 81 ++++
.../selftests/bpf/progs/aggregate_ret_run.c | 160 ++++++++
.../selftests/bpf/test_kmods/bpf_testmod.c | 34 ++
.../bpf/test_kmods/bpf_testmod_kfunc.h | 27 ++
6 files changed, 688 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_func.c
create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_kfunc.c
create mode 100644 tools/testing/selftests/bpf/progs/aggregate_ret_run.c
diff --git a/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c b/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
index d4ecc19684ce..2f1e5c704959 100644
--- a/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
+++ b/tools/testing/selftests/bpf/prog_tests/aggregate_ret.c
@@ -5,6 +5,9 @@
#include "aggregate_ret_int128_c.skel.h"
#include "aggregate_ret_struct_c.skel.h"
#include "aggregate_ret_union_c.skel.h"
+#include "aggregate_ret_run.skel.h"
+#include "aggregate_ret_func.skel.h"
+#include "aggregate_ret_kfunc.skel.h"
/*
* Run one program and check its result. The program returns 0 on success, or
@@ -39,6 +42,7 @@ 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;
+ struct aggregate_ret_run *skel_run;
skel_c = aggregate_ret_int128_c__open_and_load();
if (!ASSERT_OK_PTR(skel_c, "skel_c_open_load"))
@@ -93,4 +97,25 @@ void test_aggregate_ret(void)
run_prog(skel_union_c->progs.aggregate_ret_union_c_test, true);
aggregate_ret_union_c__destroy(skel_union_c);
+
+ skel_run = aggregate_ret_run__open_and_load();
+ if (!ASSERT_OK_PTR(skel_run, "skel_run_open_load"))
+ return;
+
+ /* Inline-asm variant: compiler-version independent, always runs. */
+ if (test__start_subtest("asm"))
+ run_prog(skel_run->progs.aggregate_ret_asm_test, false);
+
+ /* Struct-by-value kfunc returns, also compiler-version independent. */
+ if (test__start_subtest("struct"))
+ run_prog(skel_run->progs.aggregate_ret_struct_test, false);
+
+ /* Union-by-value kfunc return, compiler-version independent. */
+ if (test__start_subtest("union"))
+ run_prog(skel_run->progs.aggregate_ret_union_test, false);
+
+ aggregate_ret_run__destroy(skel_run);
+
+ RUN_TESTS(aggregate_ret_func);
+ RUN_TESTS(aggregate_ret_kfunc);
}
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_func.c b/tools/testing/selftests/bpf/progs/aggregate_ret_func.c
new file mode 100644
index 000000000000..31e176103374
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_func.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+typedef unsigned __int128 u128;
+
+__naked u128 global_agg_good(void)
+{
+ asm volatile (
+ "r0 = 0x1234;" /* low 64 bits */
+ "r2 = 0x5678;" /* high 64 bits */
+ "exit;"
+ );
+}
+
+__naked u128 global_agg_bad(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "exit;"
+ );
+}
+
+__naked u128 global_agg_bad_ptr(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "r2 = r10;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__success __retval(0)
+int aggregate_ret_global(void *ctx)
+{
+ __u64 lo, hi;
+
+ asm volatile (
+ "call %[global_agg_good];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : __imm(global_agg_good)
+ : "r0", "r1", "r2", "r3", "r4", "r5");
+ if (lo != 0x1234)
+ return 1;
+ if (hi != 0x5678)
+ return 2;
+ return 0;
+}
+
+SEC("tc")
+__failure __msg("R2 !read_ok")
+__naked int aggregate_ret_global_fail(void)
+{
+ asm volatile (
+ "call %[global_agg_bad];"
+ "r0 = r2;"
+ "exit;"
+ :
+ : __imm(global_agg_bad)
+ : __clobber_all);
+}
+
+SEC("tc")
+__failure __msg("At subprogram exit the register R2 is not a scalar value")
+__naked int aggregate_ret_global_ptr_fail(void)
+{
+ asm volatile (
+ "call %[global_agg_bad_ptr];"
+ "r0 = r2;"
+ "exit;"
+ :
+ : __imm(global_agg_bad_ptr)
+ : __clobber_all);
+}
+
+static __naked __attribute__((noinline)) u128 static_agg_bad_ptr(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "r2 = r10;" /* stack pointer placed in the second return register */
+ "exit;"
+ );
+}
+
+SEC("tc")
+__failure __msg("cannot return stack pointer to the caller")
+__naked int aggregate_ret_static_ptr_fail(void)
+{
+ asm volatile (
+ "call %[static_agg_bad_ptr];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(static_agg_bad_ptr)
+ : __clobber_all);
+}
+
+static __naked __attribute__((noinline)) u128 static_agg_no_r2(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__failure __msg("R2 !read_ok")
+__naked int aggregate_ret_static_uninit_fail(void)
+{
+ asm volatile (
+ "call %[static_agg_no_r2];"
+ "r0 = r2;"
+ "exit;"
+ :
+ : __imm(static_agg_no_r2)
+ : __clobber_all);
+}
+
+static __naked __attribute__((noinline)) u128 static_agg_precise(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "r2 = 4;" /* second half; its value is made precise below */
+ "exit;"
+ );
+}
+
+SEC("tc")
+__success __retval(0)
+__log_level(2)
+__msg("mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1")
+__msg("mark_precise: frame0: regs=r6 stack= before 4: (07) r1 += -8")
+__msg("mark_precise: frame0: regs=r6 stack= before 3: (bf) r1 = r10")
+__msg("mark_precise: frame0: regs=r6 stack= before 2: (57) r6 &= 7")
+__msg("mark_precise: frame0: regs=r6 stack= before 1: (bf) r6 = r2")
+__msg("mark_precise: frame0: regs=r2 stack= before 12: (95) exit")
+__msg("mark_precise: frame1: regs=r2 stack= before 11: (b7) r2 = 4")
+__naked int aggregate_ret_static_precise(void)
+{
+ asm volatile (
+ "call %[static_agg_precise];"
+ "r6 = r2;" /* derived from the aggregate's second half */
+ "r6 &= 7;" /* keep it in [0, 7] to index the stack */
+ "r1 = r10;"
+ "r1 += -8;"
+ "r1 += r6;" /* ptr += scalar marks r6 (hence R2) precise */
+ "r0 = 0;"
+ "*(u8 *)(r1 + 0) = r0;"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(static_agg_precise)
+ : __clobber_all);
+}
+
+SEC("tc")
+__success __retval(0)
+__log_level(2)
+__msg("mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1")
+__msg("mark_precise: frame0: regs=r6 stack= before 4: (07) r1 += -8")
+__msg("mark_precise: frame0: regs=r6 stack= before 3: (bf) r1 = r10")
+__msg("mark_precise: frame0: regs=r6 stack= before 2: (57) r6 &= 7")
+__msg("mark_precise: frame0: regs=r6 stack= before 1: (bf) r6 = r2")
+__msg("mark_precise: frame0: regs=r2 stack= before 0: (85) call pc+9")
+__naked int aggregate_ret_global_precise(void)
+{
+ asm volatile (
+ "call %[global_agg_good];"
+ "r6 = r2;" /* derived from the aggregate's second half */
+ "r6 &= 7;" /* keep it in [0, 7] to index the stack */
+ "r1 = r10;"
+ "r1 += -8;"
+ "r1 += r6;" /* ptr += scalar marks r6 (hence R2) precise */
+ "r0 = 0;"
+ "*(u8 *)(r1 + 0) = r0;"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(global_agg_good)
+ : __clobber_all);
+}
+
+SEC("tc")
+__failure __msg("return value larger than 8 bytes is not supported at program exit")
+__naked u128 aggregate_ret_entry_fail(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "r2 = 0;"
+ "exit;"
+ );
+}
+
+#if __clang_major__ >= 23
+
+struct pair {
+ __u64 hi;
+ __u64 lo;
+};
+
+union upair {
+ __u64 halves[2];
+ struct {
+ __u64 lo;
+ __u64 hi;
+ } parts;
+};
+
+/* A by-value struct that smuggles a pointer, which must be rejected. */
+struct with_ptr {
+ void *p;
+ __u64 x;
+};
+
+/* A by-value union that smuggles a pointer, which must be rejected too. */
+union upair_with_ptr {
+ void *p;
+ __u64 halves[2];
+};
+
+/* Global subprogram returning a scalar-only 16-byte struct in R0:R2. */
+__naked struct pair global_ret_struct(void)
+{
+ asm volatile (
+ "r0 = 0x1234;" /* struct's first half */
+ "r2 = 0x5678;" /* struct's second half */
+ "exit;"
+ );
+}
+
+/* Global subprogram returning a scalar-only 16-byte union in R0:R2. */
+__naked union upair global_ret_union(void)
+{
+ asm volatile (
+ "r0 = 0x1234;"
+ "r2 = 0x5678;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__success __retval(0)
+int aggregate_ret_global_struct(void *ctx)
+{
+ __u64 lo, hi;
+
+ asm volatile (
+ "call %[global_ret_struct];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : __imm(global_ret_struct)
+ : "r0", "r1", "r2", "r3", "r4", "r5");
+ if (lo != 0x1234)
+ return 1;
+ if (hi != 0x5678)
+ return 2;
+ return 0;
+}
+
+SEC("tc")
+__success __retval(0)
+int aggregate_ret_global_union(void *ctx)
+{
+ __u64 lo, hi;
+
+ asm volatile (
+ "call %[global_ret_union];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : __imm(global_ret_union)
+ : "r0", "r1", "r2", "r3", "r4", "r5");
+ if (lo != 0x1234)
+ return 1;
+ if (hi != 0x5678)
+ return 2;
+ return 0;
+}
+
+__naked struct with_ptr global_ret_struct_ptr(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "r2 = 0;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__failure __msg("Global function global_ret_struct_ptr() has unsupported return type")
+__naked int aggregate_ret_global_struct_ptr_fail(void)
+{
+ asm volatile (
+ "call %[global_ret_struct_ptr];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(global_ret_struct_ptr)
+ : __clobber_all);
+}
+
+__naked union upair_with_ptr global_ret_union_ptr(void)
+{
+ asm volatile (
+ "r0 = 0;"
+ "r2 = 0;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__failure __msg("Global function global_ret_union_ptr() has unsupported return type")
+__naked int aggregate_ret_global_union_ptr_fail(void)
+{
+ asm volatile (
+ "call %[global_ret_union_ptr];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(global_ret_union_ptr)
+ : __clobber_all);
+}
+
+#endif /* __clang_major__ >= 23 */
+
+static __naked u128 agg_callee(void)
+{
+ asm volatile (
+ "r0 = 1;"
+ "r2 = 2;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__log_level(2)
+__msg("Live regs before insn:")
+__msg("0: .12345.... (85) call pc+2")
+__msg("1: ..2....... (bf) r0 = r2")
+__msg("2: 0......... (95) exit")
+__msg("3: .......... (b7) r0 = 1")
+__msg("4: 0......... (b7) r2 = 2")
+__msg("5: 0.2....... (95) exit")
+__naked int aggregate_ret_live(void)
+{
+ asm volatile (
+ "call %[agg_callee];"
+ "r0 = r2;"
+ "exit;"
+ :
+ : [agg_callee]"i"(agg_callee)
+ : __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_kfunc.c b/tools/testing/selftests/bpf/progs/aggregate_ret_kfunc.c
new file mode 100644
index 000000000000..d73d30abed55
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_kfunc.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+/*
+ * Reference kfunc addresses to force those BTF to be emitted. Taking the address
+ * (rather than calling) avoids any dependence on the compiler lowering an
+ * __int128 or struct return value, which the BPF backend only supports from
+ * LLVM 23 on.
+ */
+void __kfunc_btf_root(void)
+{
+ asm volatile (""
+ :
+ : "r"(&bpf_kfunc_call_test_i128),
+ "r"(&bpf_kfunc_call_test_ret_ptr),
+ "r"(&bpf_kfunc_call_test_ret_ii));
+}
+
+SEC("tc")
+__success __retval(0)
+__log_level(2)
+__msg("mark_precise: frame0: last_idx 7 first_idx 0 subseq_idx -1")
+__msg("mark_precise: frame0: regs=r6 stack= before 6: (07) r1 += -8")
+__msg("mark_precise: frame0: regs=r6 stack= before 5: (bf) r1 = r10")
+__msg("mark_precise: frame0: regs=r6 stack= before 4: (57) r6 &= 7")
+__msg("mark_precise: frame0: regs=r6 stack= before 3: (bf) r6 = r2")
+__msg("mark_precise: frame0: regs=r2 stack= before 2: (85) call bpf_kfunc_call_test_i128")
+__naked int aggregate_ret_kfunc_precise(void)
+{
+ asm volatile (
+ "r1 = 1;"
+ "r2 = 2;"
+ "call %[bpf_kfunc_call_test_i128];"
+ "r6 = r2;" /* second return half */
+ "r6 &= 7;" /* keep it in [0, 7] to index the stack */
+ "r1 = r10;"
+ "r1 += -8;"
+ "r1 += r6;" /* ptr += scalar marks r6 (hence R2) precise */
+ "r0 = 0;"
+ "*(u8 *)(r1 + 0) = r0;"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(bpf_kfunc_call_test_i128)
+ : __clobber_all);
+}
+
+SEC("tc")
+__failure __msg("is not composed of scalars")
+__naked int aggregate_ret_kfunc_ptr_fail(void)
+{
+ asm volatile (
+ "r1 = 0;"
+ "call %[bpf_kfunc_call_test_ret_ptr];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(bpf_kfunc_call_test_ret_ptr)
+ : __clobber_all);
+}
+
+SEC("tc")
+__failure __msg("R2 !read_ok")
+__naked int aggregate_ret_kfunc_small_no_r2(void)
+{
+ asm volatile (
+ "r1 = 0;"
+ "r2 = 0;"
+ "call %[bpf_kfunc_call_test_ret_ii];"
+ "r0 = r2;" /* R2 is not a return register for a <=8 byte struct */
+ "exit;"
+ :
+ : __imm(bpf_kfunc_call_test_ret_ii)
+ : __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/aggregate_ret_run.c b/tools/testing/selftests/bpf/progs/aggregate_ret_run.c
new file mode 100644
index 000000000000..d6300b31733d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/aggregate_ret_run.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+typedef unsigned __int128 u128;
+
+/*
+ * Reference kfunc addresses to force those BTF to be emitted. Taking the address
+ * (rather than calling) avoids any dependence on the compiler lowering an __int128
+ * or struct return value, which the BPF backend only supports from LLVM 23 on.
+ */
+void __kfunc_btf_root(void)
+{
+ asm volatile (""
+ :
+ : "r"(&bpf_kfunc_call_test_i128),
+ "r"(&bpf_kfunc_call_test_ret_ll),
+ "r"(&bpf_kfunc_call_test_ret_li),
+ "r"(&bpf_kfunc_call_test_ret_ii),
+ "r"(&bpf_kfunc_call_test_ret_uu));
+}
+
+#define I128_ASM_LO 0xABCDabcd12345678ULL
+#define I128_ASM_HI 0x1234567890abcdefULL
+
+static __naked __attribute__((noinline)) u128 make_i128_asm(void)
+{
+ asm volatile (
+ "r0 = %[lo] ll;" /* low 64 bits */
+ "r2 = %[hi] ll;" /* high 64 bits */
+ "exit;"
+ :
+ : __imm_const(lo, I128_ASM_LO), __imm_const(hi, I128_ASM_HI)
+ );
+}
+
+SEC("tc")
+int aggregate_ret_asm_test(struct __sk_buff *skb)
+{
+ __u64 a = skb->len;
+ __u64 b = skb->len ^ 0xdeadbeefULL;
+ __u64 lo, hi;
+
+ asm volatile (
+ "call %[callee];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : [callee]"i"(make_i128_asm)
+ : "r0", "r1", "r2", "r3", "r4", "r5"
+ );
+ if (lo != I128_ASM_LO)
+ return 1;
+ if (hi != I128_ASM_HI)
+ return 2;
+
+ asm volatile (
+ "r1 = %[a];"
+ "r2 = %[b];"
+ "call %[kfunc];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : [a]"r"(a), [b]"r"(b), [kfunc]"i"(bpf_kfunc_call_test_i128)
+ : "r0", "r1", "r2", "r3", "r4", "r5"
+ );
+ if (hi != a)
+ return 3;
+ if (lo != b)
+ return 4;
+
+ return 0;
+}
+
+SEC("tc")
+int aggregate_ret_struct_test(struct __sk_buff *skb)
+{
+ __u64 a = skb->len;
+ __u64 b = skb->len ^ 0xdeadbeefULL;
+ __u64 lo, hi;
+
+ /* struct { u64 a; u64 b; }: R0 = a, R2 = b. */
+ asm volatile (
+ "r1 = %[a];"
+ "r2 = %[b];"
+ "call %[kfunc];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : [a]"r"(a), [b]"r"(b), [kfunc]"i"(bpf_kfunc_call_test_ret_ll)
+ : "r0", "r1", "r2", "r3", "r4", "r5"
+ );
+ if (lo != a)
+ return 1;
+ if (hi != b)
+ return 2;
+
+ /* struct { u64 a; int b; }: R0 = a, low 32 bits of R2 = b. */
+ asm volatile (
+ "r1 = %[a];"
+ "r2 = %[b];"
+ "call %[kfunc];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : [a]"r"(a), [b]"r"(b), [kfunc]"i"(bpf_kfunc_call_test_ret_li)
+ : "r0", "r1", "r2", "r3", "r4", "r5"
+ );
+ if (lo != a)
+ return 3;
+ if ((int)hi != (int)b)
+ return 4;
+
+ /* struct { int a; int b; }: 8 bytes, packed into R0; R2 is not used. */
+ asm volatile (
+ "r1 = %[a];"
+ "r2 = %[b];"
+ "call %[kfunc];"
+ "%[lo] = r0;"
+ : [lo]"=r"(lo)
+ : [a]"r"(a), [b]"r"(b), [kfunc]"i"(bpf_kfunc_call_test_ret_ii)
+ : "r0", "r1", "r2", "r3", "r4", "r5"
+ );
+ if ((int)lo != (int)a)
+ return 5;
+ if ((int)(lo >> 32) != (int)b)
+ return 6;
+
+ return 0;
+}
+
+SEC("tc")
+int aggregate_ret_union_test(struct __sk_buff *skb)
+{
+ __u64 a = skb->len;
+ __u64 b = skb->len ^ 0xdeadbeefULL;
+ __u64 lo, hi;
+
+ asm volatile (
+ "r1 = %[a];"
+ "r2 = %[b];"
+ "call %[kfunc];"
+ "%[lo] = r0;"
+ "%[hi] = r2;"
+ : [lo]"=r"(lo), [hi]"=r"(hi)
+ : [a]"r"(a), [b]"r"(b), [kfunc]"i"(bpf_kfunc_call_test_ret_uu)
+ : "r0", "r1", "r2", "r3", "r4", "r5"
+ );
+ if (lo != a)
+ return 1;
+ if (hi != b)
+ return 2;
+
+ return 0;
+}
+
+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 3511a4873d6c..c9e211d8a719 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -837,6 +837,36 @@ __bpf_kfunc struct bpf_kfunc_ret_ll bpf_kfunc_call_test_ret_ll(u64 a, u64 b)
return r;
}
+__bpf_kfunc struct bpf_kfunc_ret_li bpf_kfunc_call_test_ret_li(u64 a, int b)
+{
+ struct bpf_kfunc_ret_li r = { .a = a, .b = b };
+
+ return r;
+}
+
+__bpf_kfunc struct bpf_kfunc_ret_ii bpf_kfunc_call_test_ret_ii(int a, int b)
+{
+ struct bpf_kfunc_ret_ii r = { .a = a, .b = b };
+
+ return r;
+}
+
+__bpf_kfunc union bpf_kfunc_ret_uu bpf_kfunc_call_test_ret_uu(u64 a, u64 b)
+{
+ union bpf_kfunc_ret_uu r;
+
+ r.halves[0] = a;
+ r.halves[1] = b;
+ return r;
+}
+
+__bpf_kfunc struct bpf_kfunc_ret_ptr bpf_kfunc_call_test_ret_ptr(u64 tag)
+{
+ struct bpf_kfunc_ret_ptr r = { .p = NULL, .tag = tag };
+
+ 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)
@@ -1372,6 +1402,10 @@ 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_test_ret_li)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_ii)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_uu)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_ptr)
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 34d0a352e70d..9ac31d68e2c8 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -64,6 +64,29 @@ struct bpf_kfunc_ret_ll { /* 16 bytes: R0:R2 */
__u64 b;
};
+struct bpf_kfunc_ret_li { /* 16 bytes: R0:R2 */
+ __u64 a;
+ int b;
+};
+
+struct bpf_kfunc_ret_ii { /* 8 bytes: R0 only */
+ int a;
+ int b;
+};
+
+union bpf_kfunc_ret_uu { /* 16 bytes: R0:R2 */
+ __u64 halves[2];
+ struct {
+ __u64 lo;
+ __u64 hi;
+ } parts;
+};
+
+struct bpf_kfunc_ret_ptr { /* 16 bytes: contains a pointer */
+ void *p;
+ __u64 tag;
+};
+
struct prog_test_fail1 {
void *p;
int x;
@@ -129,6 +152,10 @@ 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;
+struct bpf_kfunc_ret_li bpf_kfunc_call_test_ret_li(__u64 a, int b) __ksym;
+struct bpf_kfunc_ret_ii bpf_kfunc_call_test_ret_ii(int a, int b) __ksym;
+union bpf_kfunc_ret_uu bpf_kfunc_call_test_ret_uu(__u64 a, __u64 b) __ksym;
+struct bpf_kfunc_ret_ptr bpf_kfunc_call_test_ret_ptr(__u64 tag) __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
next prev 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 ` [PATCH bpf-next 09/12] selftests/bpf: Add C tests for 16-byte returns in R0:R2 Yonghong Song
2026-07-08 20:24 ` 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 ` Yonghong Song [this message]
2026-07-08 20:28 ` [PATCH bpf-next 10/12] selftests/bpf: Add inline-asm and subprog tests for R0:R2 returns 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=20260708201030.2161016-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.