From: Yonghong Song <yonghong.song@linux.dev>
To: bot+bpf-ci@kernel.org, bpf@vger.kernel.org
Cc: ast@kernel.org, andrii@kernel.org, daniel@iogearbox.net,
eddyz87@gmail.com, kernel-team@fb.com, martin.lau@kernel.org,
clm@meta.com, ihor.solodrai@linux.dev
Subject: Re: [PATCH bpf-next v4 10/13] selftests/bpf: Add C tests for 16-byte returns in R0:R2
Date: Wed, 12 Aug 2026 13:49:46 -0700 [thread overview]
Message-ID: <3bf69928-8460-4dd6-a2fa-5d5ff263dc35@linux.dev> (raw)
In-Reply-To: <5a9b7e041b501172e1990cb6b6588be45f508972dfdcf7427d0c5d23db63e20f@mail.kernel.org>
On 8/10/26 6:24 PM, bot+bpf-ci@kernel.org wrote:
>> A register-pair return from a BPF subprogram needs no JIT support, so
>> the remaining subtests run everywhere.
> Can the remaining subtests run everywhere? Looking at the same series,
> kernel/bpf/verifier.c makes a register-pair subprogram return JIT-only.
>
> In check_func_call() for global subprogs:
>
> nregs = bpf_ret_reg_pair(env, subprog) ? 2 : 1;
> /* The R0:R2 return convention is only implemented in the JIT ... */
> if (nregs > 1)
> env->prog->jit_required = 1;
>
> and in prepare_func_exit() for static subprogs:
>
> nregs = bpf_ret_reg_pair(env, callee->subprogno) ? 2 : 1;
> if (nregs > 1)
> env->prog->jit_required = 1;
>
> Then kernel/bpf/fixups.c rejects such a program outright when the JIT did
> not run:
>
> #ifndef CONFIG_BPF_JIT_ALWAYS_ON
> if (prog->jit_required) {
> verbose(env, "program requires BPF JIT compiler but it is not available\n");
> return -EINVAL;
>
> So on a kernel built with CONFIG_BPF_JIT=y but with
> net.core.bpf_jit_enable=0, or with no eBPF JIT at all, would
> aggregate_ret_int128_c__open_and_load() fail with -EINVAL?
I missed this case (net.core.bpf_jit_enable=0). Let me add this matrix
for selftest run.
>
> Both kfuncs are restricted to x86_64 and arm64, and the two subtests
> report a skip elsewhere. pahole only BTF-encodes a function whose
> declared arguments sit in the ABI's argument registers, and an
> architecture that returns a value larger than 8 bytes through a hidden
> pointer (sret) shifts every one of them by a register. s390x is such an
> architecture: there pahole drops the function, resolve_btfids leaves the
> kfunc ID at 0, and register_btf_kfunc_id_set() then fails at module init,
> so bpf_testmod does not load at all and every test that needs it fails.
> This is not a property of the compiler -- __SIZEOF_INT128__ is defined by
> gcc on s390x, and a by-value struct return has nothing to do with
> __int128 in the first place -- so the guard is on the architecture.
> prog_tests/tracing_failure.c already restricts a __int128 return the same
> way.
>
> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
>
>> 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 0000000000000..bd18bad17472a
> [ ... ]
>
>> +static void test_int128_c(void)
>> +{
>> + struct aggregate_ret_int128_c *skel;
>> +
>> + skel = aggregate_ret_int128_c__open_and_load();
>> + if (!ASSERT_OK_PTR(skel, "skel_int128_c_open_load"))
>> + return;
>> +
>> + run_subtest("int128_c", skel->progs.aggregate_ret_int128_c_test,
>> + skel->rodata->has_reg_pair_ret);
>> +
>> + aggregate_ret_int128_c__destroy(skel);
>> +}
> Following up on the commit-message question: the four non-kfunc subtests
> (test_int128_c, test_struct_c, test_union_c with five run_subtest calls
> total) gate only on skel->rodata->has_reg_pair_ret, the compiler flag.
>
> Where the kernel has CONFIG_BPF_JIT=y but net.core.bpf_jit_enable=0, or
> no JIT at all, wouldn't aggregate_ret_int128_c__open_and_load() fail with
> -EINVAL because fixups.c rejects prog->jit_required, causing the
> !ASSERT_OK_PTR check to fire? That would be a hard test failure rather
> than a skip, and because it happens before test__start_subtest() the
> subtest wouldn't even register.
>
> The test already has is_jit_enabled() available (declared in
> testing_helpers.h and cached in env.jit_enabled), so folding that into
> the 'supported' argument of run_subtest() would turn these into skips.
>
> test_struct_c() and test_union_c() have the same shape.
Okay, I will check net.core.bpf_jit_enable=0 and env.jit_enabled.
I missed net.core.bpf_jit_enable case.
>
> [ ... ]
>
>> 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 0000000000000..5547fa6cbd495
> A subsystem pattern flags this as potentially concerning: a new selftest
> file added for a narrow variant (union vs struct return) of behavior an
> existing test already covers. The union and struct objects are both added
> in this commit and follow identical scaffolding (same compiler-version
> split, same has_reg_pair_ret flag, same MIX_A/MIX_B constants, a pair
> type, a make_pair() helper, one SEC("tc") program plus stub).
>
> On the kernel side bpf_compute_subprog_ret_regs() keys off
> btf_type_is_struct(), which covers BTF_KIND_UNION and BTF_KIND_STRUCT
> alike, so the verifier path is shared. aggregate_ret_struct_c.c already
> hosts two programs behind one has_reg_pair_ret flag.
>
> Would the union case fit as a third SEC("tc") program in the struct
> object, or does it benefit from being separate? The kfunc object has a
> stated reason to be separate (its load can fail with -EOPNOTSUPP), but
> the union vs struct distinction is less clear.
Okay, I will move union case to struct case. So we just do struct case
collectively (where union cases will be in).
>
>> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> index a6133f7521f34..242d3e2913dc5 100644
>> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> @@ -939,6 +939,34 @@ __bpf_kfunc int bpf_kfunc_call_test5(u8 a, u16 b, u32 c)
>> return 0;
>> }
>>
>> +/*
>> + * A kfunc is only usable where the ABI hands its return value back in
>> + * registers. s390x, for example, returns a by-value struct or union through a
>> + * hidden pointer argument (sret) whatever its size. That pointer shifts every
>> + * declared argument by one register, and pahole, which maps parameters to
>> + * registers positionally, then skips the function with "unexpected register
>> + * usage for parameter". resolve_btfids reports "no BTF func for kfunc" and
>> + * leaves the ID at 0, which makes register_btf_kfunc_id_set() fail at module
>> + * init, so the module does not load at all.
>> + *
>> + * Restrict these kfuncs to the architectures where the return value comes back
>> + * in registers. A kfunc taking no argument has nothing for the sret pointer to
>> + * displace and needs no guard, whatever it returns.
>> + */
>> +#if defined(__x86_64__) || defined(__aarch64__)
>> +__bpf_kfunc __int128 bpf_kfunc_call_test_i128(u64 a, u64 b)
>> +{
>> + return (__int128)(((unsigned __int128)(a + b) << 64) | (a - b));
>> +}
>> +
>> +__bpf_kfunc struct prog_test_ret_pair bpf_kfunc_call_test_ret_pair(u64 a, u64 b)
>> +{
>> + struct prog_test_ret_pair r = { .hi = a + b, .lo = a - b };
>> +
>> + return r;
>> +}
>> +#endif /* __x86_64__ || __aarch64__ */
> The comment states the criterion as "Restrict these kfuncs to the
> architectures where the return value comes back in registers", but the
> guard implements a two-entry allowlist. Does riscv64 also meet this
> criterion?
>
> The LP64 RISC-V ABI returns a 16-byte two-integer aggregate (and an
> __int128) in a0:a1, so nothing displaces the declared u64 arguments and
> pahole would encode the function. In this same series,
> arch/riscv/net/bpf_jit_comp64.c:2114 already opts in:
>
> bool bpf_jit_supports_kfunc_ret_reg_pair(void)
> {
> return true;
> }
>
> On riscv64 the module does not define these two kfuncs, so
> has_ret_pair_kfuncs() returns false and both kfunc subtests skip. That
> means the one architecture whose R0:R2 kfunc path the series describes as
> untested ("The riscv path is expected to work by the same
> register-mapping reasoning as arm64 but has not been tested") is also the
> one the new coverage silently never reaches.
>
> Either the comment should explain why the allowlist is narrower than the
> stated criterion (e.g. only the arches the pair return has been tested
> on), or __riscv (with a 64-bit pointer-width check if needed) should be
> added so the new subtests actually exercise the riscv JIT path.
Okay, I will skip riscv64 for now.
>
> [ ... ]
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31446101762
next prev parent reply other threads:[~2026-08-12 20:49 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 0:09 [PATCH bpf-next v4 00/13] bpf: Support aggregate return values up to 16 bytes Yonghong Song
2026-08-11 0:09 ` [PATCH bpf-next v4 01/13] bpf: Factor check_global_ret_scalar_reg() out of the global return check Yonghong Song
2026-08-11 0:09 ` [PATCH bpf-next v4 02/13] bpf: Add helpers to describe the R0:R2 return register pair Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 19:31 ` Yonghong Song
2026-08-12 20:07 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 03/13] bpf: Wire up JIT support for 16-byte kfunc returns Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 19:48 ` Yonghong Song
2026-08-12 20:42 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 04/13] bpf: Track R2 of register-pair returns in precision backtracking Yonghong Song
2026-08-12 21:16 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 05/13] bpf: Account R2 of register-pair returns in live register analysis Yonghong Song
2026-08-11 1:09 ` bot+bpf-ci
2026-08-12 19:55 ` Yonghong Song
2026-08-12 21:21 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 06/13] bpf: Reject callbacks returning more than 8 bytes Yonghong Song
2026-08-12 21:41 ` Eduard Zingerman
2026-08-11 0:09 ` [PATCH bpf-next v4 07/13] bpf: Add verifier support for 16-byte returns in R0:R2 Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:12 ` Yonghong Song
2026-08-11 0:09 ` [PATCH bpf-next v4 08/13] bpf: Reject register-pair returns when the subprog BTF is unreliable Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:26 ` Yonghong Song
2026-08-11 0:09 ` [PATCH bpf-next v4 09/13] bpf: Enable aggregate return types up to 16 bytes Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:29 ` Yonghong Song
2026-08-11 0:10 ` [PATCH bpf-next v4 10/13] selftests/bpf: Add C tests for 16-byte returns in R0:R2 Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 20:49 ` Yonghong Song [this message]
2026-08-11 0:10 ` [PATCH bpf-next v4 11/13] selftests/bpf: Add inline-asm and subprog tests for R0:R2 returns Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 21:08 ` Yonghong Song
2026-08-11 0:10 ` [PATCH bpf-next v4 12/13] selftests/bpf: Add tests for callbacks returning more than 8 bytes Yonghong Song
2026-08-11 1:24 ` bot+bpf-ci
2026-08-12 21:22 ` Yonghong Song
2026-08-11 0:10 ` [PATCH bpf-next v4 13/13] Documentation/bpf: Document up to 16-byte kfunc return values in R0:R2 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=3bf69928-8460-4dd6-a2fa-5d5ff263dc35@linux.dev \
--to=yonghong.song@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bot+bpf-ci@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=clm@meta.com \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=kernel-team@fb.com \
--cc=martin.lau@kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox