* [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi
@ 2024-03-24 10:33 Pu Lehui
2024-03-24 18:26 ` Puranjay Mohan
2024-03-24 18:40 ` Alexei Starovoitov
0 siblings, 2 replies; 6+ messages in thread
From: Pu Lehui @ 2024-03-24 10:33 UTC (permalink / raw)
To: bpf, linux-riscv, netdev
Cc: Björn Töpel, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, Palmer Dabbelt, Luke Nelson, Pu Lehui,
Pu Lehui
From: Pu Lehui <pulehui@huawei.com>
We encountered a failing case when running selftest in no_alu32 mode:
The failure case is `kfunc_call/kfunc_call_test4` and its source code is
like bellow:
```
long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym;
int kfunc_call_test4(struct __sk_buff *skb)
{
...
tmp = bpf_kfunc_call_test4(-3, -30, -200, -1000);
...
}
```
And its corresponding asm code is:
```
0: r1 = -3
1: r2 = -30
2: r3 = 0xffffff38 # opcode: 18 03 00 00 38 ff ff ff 00 00 00 00 00 00 00 00
4: r4 = -1000
5: call bpf_kfunc_call_test4
```
insn 2 is parsed to ld_imm64 insn to emit 0x00000000ffffff38 imm, and
converted to int type and then send to bpf_kfunc_call_test4. But since
it is zero-extended in the bpf calling convention, riscv jit will
directly treat it as an unsigned 32-bit int value, and then fails with
the message "actual 4294966063 != expected -1234".
The reason is the incompatibility between bpf and riscv abi, that is,
bpf will do zero-extension on uint, but riscv64 requires sign-extension
on int or uint. We can solve this problem by sign extending the 32-bit
parameters in kfunc.
The issue is related to [0], and thanks to Yonghong and Alexei.
Link: https://github.com/llvm/llvm-project/pull/84874 [0]
Fixes: d40c3847b485 ("riscv, bpf: Add kfunc support for RV64")
Signed-off-by: Pu Lehui <pulehui@huawei.com>
---
arch/riscv/net/bpf_jit_comp64.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 869e4282a2c4..e3fc39370f7d 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -1454,6 +1454,22 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
if (ret < 0)
return ret;
+ if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
+ const struct btf_func_model *fm;
+ int idx;
+
+ fm = bpf_jit_find_kfunc_model(ctx->prog, insn);
+ if (!fm)
+ return -EINVAL;
+
+ for (idx = 0; idx < fm->nr_args; idx++) {
+ u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx);
+
+ if (fm->arg_size[idx] == sizeof(int))
+ emit_sextw(reg, reg, ctx);
+ }
+ }
+
ret = emit_call(addr, fixed_addr, ctx);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi 2024-03-24 10:33 [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi Pu Lehui @ 2024-03-24 18:26 ` Puranjay Mohan 2024-03-24 18:40 ` Alexei Starovoitov 1 sibling, 0 replies; 6+ messages in thread From: Puranjay Mohan @ 2024-03-24 18:26 UTC (permalink / raw) To: Pu Lehui, bpf, linux-riscv, netdev Cc: Björn Töpel, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Palmer Dabbelt, Luke Nelson, Pu Lehui, Pu Lehui Pu Lehui <pulehui@huaweicloud.com> writes: > From: Pu Lehui <pulehui@huawei.com> > > We encountered a failing case when running selftest in no_alu32 mode: > > The failure case is `kfunc_call/kfunc_call_test4` and its source code is > like bellow: > ``` > long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym; > int kfunc_call_test4(struct __sk_buff *skb) > { > ... > tmp = bpf_kfunc_call_test4(-3, -30, -200, -1000); > ... > } > ``` > > And its corresponding asm code is: > ``` > 0: r1 = -3 > 1: r2 = -30 > 2: r3 = 0xffffff38 # opcode: 18 03 00 00 38 ff ff ff 00 00 00 00 00 00 00 00 > 4: r4 = -1000 > 5: call bpf_kfunc_call_test4 > ``` > > insn 2 is parsed to ld_imm64 insn to emit 0x00000000ffffff38 imm, and > converted to int type and then send to bpf_kfunc_call_test4. But since > it is zero-extended in the bpf calling convention, riscv jit will > directly treat it as an unsigned 32-bit int value, and then fails with > the message "actual 4294966063 != expected -1234". > > The reason is the incompatibility between bpf and riscv abi, that is, > bpf will do zero-extension on uint, but riscv64 requires sign-extension > on int or uint. We can solve this problem by sign extending the 32-bit > parameters in kfunc. > > The issue is related to [0], and thanks to Yonghong and Alexei. > > Link: https://github.com/llvm/llvm-project/pull/84874 [0] > Fixes: d40c3847b485 ("riscv, bpf: Add kfunc support for RV64") > Signed-off-by: Pu Lehui <pulehui@huawei.com> > --- > arch/riscv/net/bpf_jit_comp64.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c > index 869e4282a2c4..e3fc39370f7d 100644 > --- a/arch/riscv/net/bpf_jit_comp64.c > +++ b/arch/riscv/net/bpf_jit_comp64.c > @@ -1454,6 +1454,22 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, > if (ret < 0) > return ret; > > + if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { > + const struct btf_func_model *fm; > + int idx; > + > + fm = bpf_jit_find_kfunc_model(ctx->prog, insn); > + if (!fm) > + return -EINVAL; > + > + for (idx = 0; idx < fm->nr_args; idx++) { > + u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx); > + > + if (fm->arg_size[idx] == sizeof(int)) > + emit_sextw(reg, reg, ctx); > + } > + } > + > ret = emit_call(addr, fixed_addr, ctx); > if (ret) > return ret; > -- > 2.34.1 Thanks for doing this, it fixes the issue I was seeing with arena_htab selftest after enabling arena on RISCV. Tested-by: Puranjay Mohan <puranjay12@gmail.com> Reviewed-by: Puranjay Mohan <puranjay12@gmail.com> ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi 2024-03-24 10:33 [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi Pu Lehui 2024-03-24 18:26 ` Puranjay Mohan @ 2024-03-24 18:40 ` Alexei Starovoitov 2024-03-25 15:27 ` Pu Lehui 1 sibling, 1 reply; 6+ messages in thread From: Alexei Starovoitov @ 2024-03-24 18:40 UTC (permalink / raw) To: Pu Lehui Cc: bpf, linux-riscv, Network Development, Björn Töpel, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Palmer Dabbelt, Luke Nelson, Pu Lehui On Sun, Mar 24, 2024 at 3:32 AM Pu Lehui <pulehui@huaweicloud.com> wrote: > > From: Pu Lehui <pulehui@huawei.com> > > We encountered a failing case when running selftest in no_alu32 mode: > > The failure case is `kfunc_call/kfunc_call_test4` and its source code is > like bellow: > ``` > long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym; > int kfunc_call_test4(struct __sk_buff *skb) > { > ... > tmp = bpf_kfunc_call_test4(-3, -30, -200, -1000); > ... > } > ``` > > And its corresponding asm code is: > ``` > 0: r1 = -3 > 1: r2 = -30 > 2: r3 = 0xffffff38 # opcode: 18 03 00 00 38 ff ff ff 00 00 00 00 00 00 00 00 > 4: r4 = -1000 > 5: call bpf_kfunc_call_test4 > ``` > > insn 2 is parsed to ld_imm64 insn to emit 0x00000000ffffff38 imm, and > converted to int type and then send to bpf_kfunc_call_test4. But since > it is zero-extended in the bpf calling convention, riscv jit will > directly treat it as an unsigned 32-bit int value, and then fails with > the message "actual 4294966063 != expected -1234". > > The reason is the incompatibility between bpf and riscv abi, that is, > bpf will do zero-extension on uint, but riscv64 requires sign-extension > on int or uint. We can solve this problem by sign extending the 32-bit > parameters in kfunc. > > The issue is related to [0], and thanks to Yonghong and Alexei. > > Link: https://github.com/llvm/llvm-project/pull/84874 [0] > Fixes: d40c3847b485 ("riscv, bpf: Add kfunc support for RV64") > Signed-off-by: Pu Lehui <pulehui@huawei.com> > --- > arch/riscv/net/bpf_jit_comp64.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c > index 869e4282a2c4..e3fc39370f7d 100644 > --- a/arch/riscv/net/bpf_jit_comp64.c > +++ b/arch/riscv/net/bpf_jit_comp64.c > @@ -1454,6 +1454,22 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, > if (ret < 0) > return ret; > > + if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { > + const struct btf_func_model *fm; > + int idx; > + > + fm = bpf_jit_find_kfunc_model(ctx->prog, insn); > + if (!fm) > + return -EINVAL; > + > + for (idx = 0; idx < fm->nr_args; idx++) { > + u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx); > + > + if (fm->arg_size[idx] == sizeof(int)) > + emit_sextw(reg, reg, ctx); > + } > + } > + The btf_func_model usage looks good. Glad that no new flags were necessary, since both int and uint need to be sign extend the existing arg_size was enough. Since we're at it. Do we need to do zero extension of return value ? There is __bpf_kfunc int bpf_kfunc_call_test2(struct sock *sk, u32 a, u32 b) but the selftest with it is too simple: return bpf_kfunc_call_test2((struct sock *)sk, 1, 2); Could you extend this selftest with a return of large int/uint with 31th bit set to force sign extension in native kernel risc-v code ? I suspect the bpf side will be confused. Which would mean that risc-v JIT in addition to: if (insn->src_reg != BPF_PSEUDO_CALL) emit_mv(bpf_to_rv_reg(BPF_REG_0, ctx), RV_REG_A0, ctx); need to conditionally do: if (fm->ret_size == sizeof(int)) emit_zextw(bpf_to_rv_reg(BPF_REG_0, ctx), bpf_to_rv_reg(BPF_REG_0, ctx), ctx); ? ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi 2024-03-24 18:40 ` Alexei Starovoitov @ 2024-03-25 15:27 ` Pu Lehui 2024-03-25 18:34 ` Alexei Starovoitov 0 siblings, 1 reply; 6+ messages in thread From: Pu Lehui @ 2024-03-25 15:27 UTC (permalink / raw) To: Alexei Starovoitov Cc: bpf, linux-riscv, Network Development, Björn Töpel, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Palmer Dabbelt, Luke Nelson, Pu Lehui On 2024/3/25 2:40, Alexei Starovoitov wrote: > On Sun, Mar 24, 2024 at 3:32 AM Pu Lehui <pulehui@huaweicloud.com> wrote: [SNIP] >> >> diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c >> index 869e4282a2c4..e3fc39370f7d 100644 >> --- a/arch/riscv/net/bpf_jit_comp64.c >> +++ b/arch/riscv/net/bpf_jit_comp64.c >> @@ -1454,6 +1454,22 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, >> if (ret < 0) >> return ret; >> >> + if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { >> + const struct btf_func_model *fm; >> + int idx; >> + >> + fm = bpf_jit_find_kfunc_model(ctx->prog, insn); >> + if (!fm) >> + return -EINVAL; >> + >> + for (idx = 0; idx < fm->nr_args; idx++) { >> + u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx); >> + >> + if (fm->arg_size[idx] == sizeof(int)) >> + emit_sextw(reg, reg, ctx); >> + } >> + } >> + > > The btf_func_model usage looks good. > Glad that no new flags were necessary, since both int and uint > need to be sign extend the existing arg_size was enough. > > Since we're at it. Do we need to do zero extension of return value ? > There is > __bpf_kfunc int bpf_kfunc_call_test2(struct sock *sk, u32 a, u32 b) > but the selftest with it is too simple: > return bpf_kfunc_call_test2((struct sock *)sk, 1, 2); > > Could you extend this selftest with a return of large int/uint > with 31th bit set to force sign extension in native Sorry for late. riscv64 will sign-extend int/uint return values. I thought this would be a good test, so I tried the following: ``` u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; <-- here change int to u32 int kfunc_call_test2(struct __sk_buff *skb) { long tmp; tmp = bpf_kfunc_call_test2(0xfffffff0, 2); return (tmp >> 32) + tmp; } ``` As expected, if the return value is sign-extended, the bpf program will return 0xfffffff1. If the return value is zero-extended, the bpf program will return 0xfffffff2. But in fact, riscv returns 0xfffffff2. Upon further discovery, it seems clang will compensate for unsigned return values. Curious! for example: ``` u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; int kfunc_call_test2(struct __sk_buff *skb) { long tmp; tmp = bpf_kfunc_call_test2(0xfffffff0, 2); bpf_printk("tmp: 0x%lx", tmp); return (tmp >> 32) + tmp; } ``` and the bytecode will be: ``` 0: 18 01 00 00 00 00 00 f0 00 00 00 00 00 00 00 00 r1 = 0xf0000000 ll 2: b7 02 00 00 02 00 00 00 r2 = 0x2 3: 85 10 00 00 ff ff ff ff call -0x1 4: bf 06 00 00 00 00 00 00 r6 = r0 5: bf 63 00 00 00 00 00 00 r3 = r6 6: 67 03 00 00 20 00 00 00 r3 <<= 0x20 <-- zero extension 7: 77 03 00 00 20 00 00 00 r3 >>= 0x20 8: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll 10: b7 02 00 00 0b 00 00 00 r2 = 0xb 11: 85 00 00 00 06 00 00 00 call 0x6 12: bf 60 00 00 00 00 00 00 r0 = r6 13: 95 00 00 00 00 00 00 00 exit ``` another example: ``` u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; int kfunc_call_test2(struct __sk_buff *skb) { long tmp; tmp = bpf_kfunc_call_test2(0xfffffff0, 2); return (tmp >> 20) + tmp; <-- change from 32 to 20 } ``` and the bytecode will be: ``` 0: 18 01 00 00 00 00 00 f0 00 00 00 00 00 00 00 00 r1 = 0xf0000000 ll 2: b7 02 00 00 02 00 00 00 r2 = 0x2 3: 85 10 00 00 ff ff ff ff call -0x1 4: 18 02 00 00 00 00 f0 ff 00 00 00 00 00 00 00 00 r2 = 0xfff00000 ll <-- 32-bit truncation 6: bf 01 00 00 00 00 00 00 r1 = r0 7: 5f 21 00 00 00 00 00 00 r1 &= r2 8: 77 01 00 00 14 00 00 00 r1 >>= 0x14 9: 0f 01 00 00 00 00 00 00 r1 += r0 10: bf 10 00 00 00 00 00 00 r0 = r1 11: 95 00 00 00 00 00 00 00 exit ``` It is difficult to construct this test case. > kernel risc-v code ? > I suspect the bpf side will be confused. > Which would mean that risc-v JIT in addition to: > if (insn->src_reg != BPF_PSEUDO_CALL) > emit_mv(bpf_to_rv_reg(BPF_REG_0, ctx), RV_REG_A0, ctx); > > need to conditionally do: > if (fm->ret_size == sizeof(int)) > emit_zextw(bpf_to_rv_reg(BPF_REG_0, ctx), > bpf_to_rv_reg(BPF_REG_0, ctx), ctx); > ? Agree on zero-extending int/uint return values when returning from kfunc to bpf ctx. I will add it in next version. Thanks. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi 2024-03-25 15:27 ` Pu Lehui @ 2024-03-25 18:34 ` Alexei Starovoitov 2024-03-26 1:32 ` Pu Lehui 0 siblings, 1 reply; 6+ messages in thread From: Alexei Starovoitov @ 2024-03-25 18:34 UTC (permalink / raw) To: Pu Lehui Cc: bpf, linux-riscv, Network Development, Björn Töpel, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Palmer Dabbelt, Luke Nelson, Pu Lehui On Mon, Mar 25, 2024 at 8:28 AM Pu Lehui <pulehui@huaweicloud.com> wrote: > > > > On 2024/3/25 2:40, Alexei Starovoitov wrote: > > On Sun, Mar 24, 2024 at 3:32 AM Pu Lehui <pulehui@huaweicloud.com> wrote: > [SNIP] > >> > >> diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c > >> index 869e4282a2c4..e3fc39370f7d 100644 > >> --- a/arch/riscv/net/bpf_jit_comp64.c > >> +++ b/arch/riscv/net/bpf_jit_comp64.c > >> @@ -1454,6 +1454,22 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, > >> if (ret < 0) > >> return ret; > >> > >> + if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { > >> + const struct btf_func_model *fm; > >> + int idx; > >> + > >> + fm = bpf_jit_find_kfunc_model(ctx->prog, insn); > >> + if (!fm) > >> + return -EINVAL; > >> + > >> + for (idx = 0; idx < fm->nr_args; idx++) { > >> + u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx); > >> + > >> + if (fm->arg_size[idx] == sizeof(int)) > >> + emit_sextw(reg, reg, ctx); > >> + } > >> + } > >> + > > > > The btf_func_model usage looks good. > > Glad that no new flags were necessary, since both int and uint > > need to be sign extend the existing arg_size was enough. > > > > Since we're at it. Do we need to do zero extension of return value ? > > There is > > __bpf_kfunc int bpf_kfunc_call_test2(struct sock *sk, u32 a, u32 b) > > but the selftest with it is too simple: > > return bpf_kfunc_call_test2((struct sock *)sk, 1, 2); > > > Could you extend this selftest with a return of large int/uint > > with 31th bit set to force sign extension in native > > Sorry for late. riscv64 will sign-extend int/uint return values. I > thought this would be a good test, so I tried the following: > ``` > u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; <-- here change int to u32 > int kfunc_call_test2(struct __sk_buff *skb) > { > long tmp; > > tmp = bpf_kfunc_call_test2(0xfffffff0, 2); > return (tmp >> 32) + tmp; > } > ``` > As expected, if the return value is sign-extended, the bpf program will > return 0xfffffff1. If the return value is zero-extended, the bpf program > will return 0xfffffff2. But in fact, riscv returns 0xfffffff2. Upon > further discovery, it seems clang will compensate for unsigned return > values. Curious! > for example: > ``` > u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; > int kfunc_call_test2(struct __sk_buff *skb) > { > long tmp; > > tmp = bpf_kfunc_call_test2(0xfffffff0, 2); > bpf_printk("tmp: 0x%lx", tmp); > return (tmp >> 32) + tmp; > } > ``` > and the bytecode will be: > ``` > 0: 18 01 00 00 00 00 00 f0 00 00 00 00 00 00 00 00 r1 = > 0xf0000000 ll > 2: b7 02 00 00 02 00 00 00 r2 = 0x2 > 3: 85 10 00 00 ff ff ff ff call -0x1 > 4: bf 06 00 00 00 00 00 00 r6 = r0 > 5: bf 63 00 00 00 00 00 00 r3 = r6 > 6: 67 03 00 00 20 00 00 00 r3 <<= 0x20 <-- zero extension > 7: 77 03 00 00 20 00 00 00 r3 >>= 0x20 > 8: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll > 10: b7 02 00 00 0b 00 00 00 r2 = 0xb > 11: 85 00 00 00 06 00 00 00 call 0x6 > 12: bf 60 00 00 00 00 00 00 r0 = r6 > 13: 95 00 00 00 00 00 00 00 exit > ``` > > another example: > ``` > u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; > int kfunc_call_test2(struct __sk_buff *skb) > { > long tmp; > > tmp = bpf_kfunc_call_test2(0xfffffff0, 2); > return (tmp >> 20) + tmp; <-- change from 32 to 20 > } > ``` > and the bytecode will be: > ``` > 0: 18 01 00 00 00 00 00 f0 00 00 00 00 00 00 00 00 r1 = > 0xf0000000 ll > 2: b7 02 00 00 02 00 00 00 r2 = 0x2 > 3: 85 10 00 00 ff ff ff ff call -0x1 > 4: 18 02 00 00 00 00 f0 ff 00 00 00 00 00 00 00 00 r2 = > 0xfff00000 ll <-- 32-bit truncation > 6: bf 01 00 00 00 00 00 00 r1 = r0 > 7: 5f 21 00 00 00 00 00 00 r1 &= r2 > 8: 77 01 00 00 14 00 00 00 r1 >>= 0x14 > 9: 0f 01 00 00 00 00 00 00 r1 += r0 > 10: bf 10 00 00 00 00 00 00 r0 = r1 > 11: 95 00 00 00 00 00 00 00 exit > ``` > > It is difficult to construct this test case. Yeah. I also tried a bunch of experiments with llvm and gcc-bpf. Both compilers emit zero extension when u32 is being used as u64. > > kernel risc-v code ? > > I suspect the bpf side will be confused. > > Which would mean that risc-v JIT in addition to: > > if (insn->src_reg != BPF_PSEUDO_CALL) > > emit_mv(bpf_to_rv_reg(BPF_REG_0, ctx), RV_REG_A0, ctx); > > > > need to conditionally do: > > if (fm->ret_size == sizeof(int)) > > emit_zextw(bpf_to_rv_reg(BPF_REG_0, ctx), > > bpf_to_rv_reg(BPF_REG_0, ctx), ctx); > > ? > > Agree on zero-extending int/uint return values when returning from > kfunc to bpf ctx. I will add it in next version. Thanks. Looking at existing compilers behavior it's probably unnecessary. I think this patch is fine as-is. I'll apply it shortly. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi 2024-03-25 18:34 ` Alexei Starovoitov @ 2024-03-26 1:32 ` Pu Lehui 0 siblings, 0 replies; 6+ messages in thread From: Pu Lehui @ 2024-03-26 1:32 UTC (permalink / raw) To: Alexei Starovoitov Cc: bpf, linux-riscv, Network Development, Björn Töpel, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Palmer Dabbelt, Luke Nelson, Pu Lehui On 2024/3/26 2:34, Alexei Starovoitov wrote: > On Mon, Mar 25, 2024 at 8:28 AM Pu Lehui <pulehui@huaweicloud.com> wrote: >> >> >> >> On 2024/3/25 2:40, Alexei Starovoitov wrote: >>> On Sun, Mar 24, 2024 at 3:32 AM Pu Lehui <pulehui@huaweicloud.com> wrote: >> [SNIP] >>>> >>>> diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c >>>> index 869e4282a2c4..e3fc39370f7d 100644 >>>> --- a/arch/riscv/net/bpf_jit_comp64.c >>>> +++ b/arch/riscv/net/bpf_jit_comp64.c >>>> @@ -1454,6 +1454,22 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, >>>> if (ret < 0) >>>> return ret; >>>> >>>> + if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { >>>> + const struct btf_func_model *fm; >>>> + int idx; >>>> + >>>> + fm = bpf_jit_find_kfunc_model(ctx->prog, insn); >>>> + if (!fm) >>>> + return -EINVAL; >>>> + >>>> + for (idx = 0; idx < fm->nr_args; idx++) { >>>> + u8 reg = bpf_to_rv_reg(BPF_REG_1 + idx, ctx); >>>> + >>>> + if (fm->arg_size[idx] == sizeof(int)) >>>> + emit_sextw(reg, reg, ctx); >>>> + } >>>> + } >>>> + >>> >>> The btf_func_model usage looks good. >>> Glad that no new flags were necessary, since both int and uint >>> need to be sign extend the existing arg_size was enough. >>> >>> Since we're at it. Do we need to do zero extension of return value ? >>> There is >>> __bpf_kfunc int bpf_kfunc_call_test2(struct sock *sk, u32 a, u32 b) >>> but the selftest with it is too simple: >>> return bpf_kfunc_call_test2((struct sock *)sk, 1, 2); > >>> Could you extend this selftest with a return of large int/uint >>> with 31th bit set to force sign extension in native >> >> Sorry for late. riscv64 will sign-extend int/uint return values. I >> thought this would be a good test, so I tried the following: >> ``` >> u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; <-- here change int to u32 >> int kfunc_call_test2(struct __sk_buff *skb) >> { >> long tmp; >> >> tmp = bpf_kfunc_call_test2(0xfffffff0, 2); >> return (tmp >> 32) + tmp; >> } >> ``` >> As expected, if the return value is sign-extended, the bpf program will >> return 0xfffffff1. If the return value is zero-extended, the bpf program >> will return 0xfffffff2. But in fact, riscv returns 0xfffffff2. Upon >> further discovery, it seems clang will compensate for unsigned return >> values. Curious! >> for example: >> ``` >> u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; >> int kfunc_call_test2(struct __sk_buff *skb) >> { >> long tmp; >> >> tmp = bpf_kfunc_call_test2(0xfffffff0, 2); >> bpf_printk("tmp: 0x%lx", tmp); >> return (tmp >> 32) + tmp; >> } >> ``` >> and the bytecode will be: >> ``` >> 0: 18 01 00 00 00 00 00 f0 00 00 00 00 00 00 00 00 r1 = >> 0xf0000000 ll >> 2: b7 02 00 00 02 00 00 00 r2 = 0x2 >> 3: 85 10 00 00 ff ff ff ff call -0x1 >> 4: bf 06 00 00 00 00 00 00 r6 = r0 >> 5: bf 63 00 00 00 00 00 00 r3 = r6 >> 6: 67 03 00 00 20 00 00 00 r3 <<= 0x20 <-- zero extension >> 7: 77 03 00 00 20 00 00 00 r3 >>= 0x20 >> 8: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll >> 10: b7 02 00 00 0b 00 00 00 r2 = 0xb >> 11: 85 00 00 00 06 00 00 00 call 0x6 >> 12: bf 60 00 00 00 00 00 00 r0 = r6 >> 13: 95 00 00 00 00 00 00 00 exit >> ``` >> >> another example: >> ``` >> u32 bpf_kfunc_call_test2(u32 a, u32 b) __ksym; >> int kfunc_call_test2(struct __sk_buff *skb) >> { >> long tmp; >> >> tmp = bpf_kfunc_call_test2(0xfffffff0, 2); >> return (tmp >> 20) + tmp; <-- change from 32 to 20 >> } >> ``` >> and the bytecode will be: >> ``` >> 0: 18 01 00 00 00 00 00 f0 00 00 00 00 00 00 00 00 r1 = >> 0xf0000000 ll >> 2: b7 02 00 00 02 00 00 00 r2 = 0x2 >> 3: 85 10 00 00 ff ff ff ff call -0x1 >> 4: 18 02 00 00 00 00 f0 ff 00 00 00 00 00 00 00 00 r2 = >> 0xfff00000 ll <-- 32-bit truncation >> 6: bf 01 00 00 00 00 00 00 r1 = r0 >> 7: 5f 21 00 00 00 00 00 00 r1 &= r2 >> 8: 77 01 00 00 14 00 00 00 r1 >>= 0x14 >> 9: 0f 01 00 00 00 00 00 00 r1 += r0 >> 10: bf 10 00 00 00 00 00 00 r0 = r1 >> 11: 95 00 00 00 00 00 00 00 exit >> ``` >> >> It is difficult to construct this test case. > > Yeah. > I also tried a bunch of experiments with llvm and gcc-bpf. > Both compilers emit zero extension when u32 is being used as u64. > >>> kernel risc-v code ? >>> I suspect the bpf side will be confused. >>> Which would mean that risc-v JIT in addition to: >>> if (insn->src_reg != BPF_PSEUDO_CALL) >>> emit_mv(bpf_to_rv_reg(BPF_REG_0, ctx), RV_REG_A0, ctx); >>> >>> need to conditionally do: >>> if (fm->ret_size == sizeof(int)) >>> emit_zextw(bpf_to_rv_reg(BPF_REG_0, ctx), >>> bpf_to_rv_reg(BPF_REG_0, ctx), ctx); >>> ? >> >> Agree on zero-extending int/uint return values when returning from >> kfunc to bpf ctx. I will add it in next version. Thanks. > > Looking at existing compilers behavior it's probably unnecessary. > I think this patch is fine as-is. > I'll apply it shortly. Alright, feel free to apply it. Thanks ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-03-26 1:32 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-03-24 10:33 [PATCH bpf] riscv, bpf: Fix kfunc parameters incompatibility between bpf and riscv abi Pu Lehui 2024-03-24 18:26 ` Puranjay Mohan 2024-03-24 18:40 ` Alexei Starovoitov 2024-03-25 15:27 ` Pu Lehui 2024-03-25 18:34 ` Alexei Starovoitov 2024-03-26 1:32 ` Pu Lehui
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox