diff mbox series

[bpf-next,24/24] s390/bpf: Implement bpf_jit_supports_kfunc_call()

Message ID 20230125213817.1424447-25-iii@linux.ibm.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Support bpf trampoline for s390x | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count fail Series longer than 15 patches (and no cover letter)
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 12 maintainers not CCed: sdf@google.com kpsingh@kernel.org jolsa@kernel.org borntraeger@linux.ibm.com martin.lau@linux.dev svens@linux.ibm.com song@kernel.org john.fastabend@gmail.com linux-s390@vger.kernel.org haoluo@google.com agordeev@linux.ibm.com yhs@fb.com
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 44 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-7 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_maps on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-12 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-14 success Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for test_progs on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-17 success Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-19 success Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for test_progs_no_alu32 on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-22 success Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-24 success Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for test_progs_no_alu32_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 success Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for test_progs_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-32 success Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-34 success Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for test_verifier on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-37 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 success Logs for test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-31 success Logs for test_progs_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-36 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for test_progs_no_alu32_parallel on s390x with gcc

Commit Message

Ilya Leoshkevich Jan. 25, 2023, 9:38 p.m. UTC
Implement calling kernel functions from eBPF. In general, the eBPF ABI
is fairly close to that of s390x, with one important difference: on
s390x callers should sign-extend signed arguments. Handle that by using
information returned by bpf_jit_find_kfunc_model().

At the moment bpf_jit_find_kfunc_model() does not seem to play nicely
with XDP metadata functions: add_kfunc_call() adds an "abstract" bpf_*()
version to kfunc_btf_tab, but then fixup_kfunc_call() puts the concrete
version into insn->imm, which bpf_jit_find_kfunc_model() cannot find.
But this seems to be a common code problem.

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
 arch/s390/net/bpf_jit_comp.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

Comments

Alexei Starovoitov Jan. 26, 2023, 1:28 a.m. UTC | #1
On Wed, Jan 25, 2023 at 10:38:17PM +0100, Ilya Leoshkevich wrote:
> +
> +		/* Sign-extend the kfunc arguments. */
> +		if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
> +			m = bpf_jit_find_kfunc_model(fp, insn);
> +			if (!m)
> +				return -1;
> +
> +			for (j = 0; j < m->nr_args; j++) {
> +				if (sign_extend(jit, BPF_REG_1 + j,
> +						m->arg_size[j],
> +						m->arg_flags[j]))
> +					return -1;
> +			}
> +		}

Is this because s390 doesn't have subregisters?
Could you give an example where it's necessary?
I'm guessing a bpf prog compiled with alu32 and operates on signed int
that is passed into a kfunc that expects 'int' in 64-bit reg?

>  
> +bool bpf_jit_supports_kfunc_call(void)
> +{
> +	return true;
> +}

Timely :) Thanks for working it.
Ilya Leoshkevich Jan. 27, 2023, 11:36 a.m. UTC | #2
On Wed, 2023-01-25 at 17:28 -0800, Alexei Starovoitov wrote:
> On Wed, Jan 25, 2023 at 10:38:17PM +0100, Ilya Leoshkevich wrote:
> > +
> > +               /* Sign-extend the kfunc arguments. */
> > +               if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
> > +                       m = bpf_jit_find_kfunc_model(fp, insn);
> > +                       if (!m)
> > +                               return -1;
> > +
> > +                       for (j = 0; j < m->nr_args; j++) {
> > +                               if (sign_extend(jit, BPF_REG_1 + j,
> > +                                               m->arg_size[j],
> > +                                               m->arg_flags[j]))
> > +                                       return -1;
> > +                       }
> > +               }
> 
> Is this because s390 doesn't have subregisters?
> Could you give an example where it's necessary?
> I'm guessing a bpf prog compiled with alu32 and operates on signed
> int
> that is passed into a kfunc that expects 'int' in 64-bit reg?

Precisely. The test added in 13/24 fails without this:

verify_success:PASS:skel 0 nsec                                       
verify_success:PASS:bpf_object__find_program_by_name 0 nsec           
verify_success:PASS:kfunc_call_test4 0 nsec                           
verify_success:FAIL:retval unexpected retval: actual 4294966065 !=
expected -1234                                                        
#94/10   kfunc_call/kfunc_call_test4:FAIL                    

Looking at the assembly:

; long noinline bpf_kfunc_call_test4(signed char a, short b, int c,
long d)
0000000000936a78 <bpf_kfunc_call_test4>:
  936a78:       c0 04 00 00 00 00       jgnop   936a78
<bpf_kfunc_call_test4>
; 	return (long)a + (long)b + (long)c + d;
  936a7e:       b9 08 00 45             agr     %r4,%r5
  936a82:       b9 08 00 43             agr     %r4,%r3
  936a86:       b9 08 00 24             agr     %r2,%r4
  936a8a:       c0 f4 00 1e 3b 27       jg      cfe0d8
<__s390_indirect_jump_r14>

As per the s390x ABI, bpf_kfunc_call_test4() has the right to assume
that a, b and c are sign-extended by the caller, which results in using
64-bit additions (agr) without any additional conversions.

On the JITed code side (without this hunk) we have:

; tmp = bpf_kfunc_call_test4(-3, -30, -200, -1000);
;        5:       b4 10 00 00 ff ff ff fd w1 = -3
   0x3ff7fdcdad4:       llilf   %r2,0xfffffffd
;        6:       b4 20 00 00 ff ff ff e2 w2 = -30
   0x3ff7fdcdada:       llilf   %r3,0xffffffe2
;        7:       b4 30 00 00 ff ff ff 38 w3 = -200
   0x3ff7fdcdae0:       llilf   %r4,0xffffff38
;       8:       b7 40 00 00 ff ff fc 18 r4 = -1000
   0x3ff7fdcdae6:       lgfi    %r5,-1000
   0x3ff7fdcdaec:       mvc     64(4,%r15),160(%r15)
   0x3ff7fdcdaf2:       lgrl    %r1,bpf_kfunc_call_test4@GOT
   0x3ff7fdcdaf8:       brasl   %r14,__s390_indirect_jump_r1

This first 3 llilfs are 32-bit loads, that need to be sign-extended
to 64 bits.

> > +bool bpf_jit_supports_kfunc_call(void)
> > +{
> > +       return true;
> > +}
> 
> Timely :) Thanks for working it.
Alexei Starovoitov Jan. 27, 2023, 4:04 p.m. UTC | #3
On Fri, Jan 27, 2023 at 3:36 AM Ilya Leoshkevich <iii@linux.ibm.com> wrote:
>
> On Wed, 2023-01-25 at 17:28 -0800, Alexei Starovoitov wrote:
> > On Wed, Jan 25, 2023 at 10:38:17PM +0100, Ilya Leoshkevich wrote:
> > > +
> > > +               /* Sign-extend the kfunc arguments. */
> > > +               if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
> > > +                       m = bpf_jit_find_kfunc_model(fp, insn);
> > > +                       if (!m)
> > > +                               return -1;
> > > +
> > > +                       for (j = 0; j < m->nr_args; j++) {
> > > +                               if (sign_extend(jit, BPF_REG_1 + j,
> > > +                                               m->arg_size[j],
> > > +                                               m->arg_flags[j]))
> > > +                                       return -1;
> > > +                       }
> > > +               }
> >
> > Is this because s390 doesn't have subregisters?
> > Could you give an example where it's necessary?
> > I'm guessing a bpf prog compiled with alu32 and operates on signed
> > int
> > that is passed into a kfunc that expects 'int' in 64-bit reg?
>
> Precisely. The test added in 13/24 fails without this:
>
> verify_success:PASS:skel 0 nsec
> verify_success:PASS:bpf_object__find_program_by_name 0 nsec
> verify_success:PASS:kfunc_call_test4 0 nsec
> verify_success:FAIL:retval unexpected retval: actual 4294966065 !=
> expected -1234
> #94/10   kfunc_call/kfunc_call_test4:FAIL
>
> Looking at the assembly:
>
> ; long noinline bpf_kfunc_call_test4(signed char a, short b, int c,
> long d)
> 0000000000936a78 <bpf_kfunc_call_test4>:
>   936a78:       c0 04 00 00 00 00       jgnop   936a78
> <bpf_kfunc_call_test4>
> ;       return (long)a + (long)b + (long)c + d;
>   936a7e:       b9 08 00 45             agr     %r4,%r5
>   936a82:       b9 08 00 43             agr     %r4,%r3
>   936a86:       b9 08 00 24             agr     %r2,%r4
>   936a8a:       c0 f4 00 1e 3b 27       jg      cfe0d8
> <__s390_indirect_jump_r14>
>
> As per the s390x ABI, bpf_kfunc_call_test4() has the right to assume
> that a, b and c are sign-extended by the caller, which results in using
> 64-bit additions (agr) without any additional conversions.
>
> On the JITed code side (without this hunk) we have:
>
> ; tmp = bpf_kfunc_call_test4(-3, -30, -200, -1000);
> ;        5:       b4 10 00 00 ff ff ff fd w1 = -3
>    0x3ff7fdcdad4:       llilf   %r2,0xfffffffd
> ;        6:       b4 20 00 00 ff ff ff e2 w2 = -30
>    0x3ff7fdcdada:       llilf   %r3,0xffffffe2
> ;        7:       b4 30 00 00 ff ff ff 38 w3 = -200
>    0x3ff7fdcdae0:       llilf   %r4,0xffffff38
> ;       8:       b7 40 00 00 ff ff fc 18 r4 = -1000
>    0x3ff7fdcdae6:       lgfi    %r5,-1000
>    0x3ff7fdcdaec:       mvc     64(4,%r15),160(%r15)
>    0x3ff7fdcdaf2:       lgrl    %r1,bpf_kfunc_call_test4@GOT
>    0x3ff7fdcdaf8:       brasl   %r14,__s390_indirect_jump_r1
>
> This first 3 llilfs are 32-bit loads, that need to be sign-extended
> to 64 bits.

All makes sense. Please add this explanation to the commit log.
When 2nd arch appears that needs similar sign extension in
the caller we can add this logic to the verifier.

In parallel we're working on new sign extension instructions.
Doing sign extension with shifts in the verifier won't be efficient,
so we need to wait for them.
diff mbox series

Patch

diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index f4cdecb32629..74b38e817bd8 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -1401,9 +1401,10 @@  static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
 	 */
 	case BPF_JMP | BPF_CALL:
 	{
-		u64 func;
+		const struct btf_func_model *m;
 		bool func_addr_fixed;
-		int ret;
+		int j, ret;
+		u64 func;
 
 		ret = bpf_jit_get_func_addr(fp, insn, extra_pass,
 					    &func, &func_addr_fixed);
@@ -1425,6 +1426,21 @@  static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
 		/* mvc STK_OFF_TCCNT(4,%r15),N(%r15) */
 		_EMIT6(0xd203f000 | STK_OFF_TCCNT,
 		       0xf000 | (STK_OFF_TCCNT + STK_OFF + stack_depth));
+
+		/* Sign-extend the kfunc arguments. */
+		if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
+			m = bpf_jit_find_kfunc_model(fp, insn);
+			if (!m)
+				return -1;
+
+			for (j = 0; j < m->nr_args; j++) {
+				if (sign_extend(jit, BPF_REG_1 + j,
+						m->arg_size[j],
+						m->arg_flags[j]))
+					return -1;
+			}
+		}
+
 		/* lgrl %w1,func */
 		EMIT6_PCREL_RILB(0xc4080000, REG_W1, _EMIT_CONST_U64(func));
 		/* %r1() */
@@ -1980,6 +1996,11 @@  struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
 	return fp;
 }
 
+bool bpf_jit_supports_kfunc_call(void)
+{
+	return true;
+}
+
 int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
 		       void *old_addr, void *new_addr)
 {