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: 44+ 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-12 22:12 ` Eduard Zingerman
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-12 22:24 ` Eduard Zingerman
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-12 22:47 ` Eduard Zingerman
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-12 23:15 ` Eduard Zingerman
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-12 23:29 ` Eduard Zingerman
2026-08-13 0:09 ` Eduard Zingerman
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-13 0:10 ` Eduard Zingerman
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 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.