BPF JIT for PPC64
Subsystems (5)
RFC PATCH 1/1 BPF JIT for PPC64
arch/powerpc/Kconfig | 1 + arch/powerpc/Makefile | 3 +- arch/powerpc/include/asm/ppc-opcode.h | 40 ++ arch/powerpc/net/Makefile | 4 + arch/powerpc/net/bpf_jit.S | 138 +++++++ arch/powerpc/net/bpf_jit.h | 226 +++++++++++ arch/powerpc/net/bpf_jit_comp.c | 697 +++++++++++++++++++++++++++++++++ 7 files changed, 1108 insertions(+), 1 deletions(-)
arch/powerpc/Kconfig +1 -0
@@ -134,6 +134,7 @@ config PPC select GENERIC_IRQ_SHOW_LEVEL select HAVE_RCU_TABLE_FREE if SMP select HAVE_SYSCALL_TRACEPOINTS + select HAVE_BPF_JIT if PPC64 config EARLY_PRINTK bool
arch/powerpc/Makefile +2 -1
@@ -154,7 +154,8 @@ core-y += arch/powerpc/kernel/ \ arch/powerpc/lib/ \ arch/powerpc/sysdev/ \ arch/powerpc/platforms/ \ - arch/powerpc/math-emu/ + arch/powerpc/math-emu/ \ + arch/powerpc/net/ core-$(CONFIG_XMON) += arch/powerpc/xmon/ core-$(CONFIG_KVM) += arch/powerpc/kvm/
arch/powerpc/include/asm/ppc-opcode.h +40 -0
@@ -71,6 +71,42 @@#define PPC_INST_ERATSX 0x7c000126#define PPC_INST_ERATSX_DOT 0x7c000127 +/* Misc instructions for BPF compiler */ +#define PPC_INST_LD 0xe8000000 +#define PPC_INST_LHZ 0xa0000000 +#define PPC_INST_LWZ 0x80000000 +#define PPC_INST_STD 0xf8000000 +#define PPC_INST_STDU 0xf8000001 +#define PPC_INST_MFLR 0x7c0802a6 +#define PPC_INST_MTLR 0x7c0803a6 +#define PPC_INST_CMPWI 0x2c000000 +#define PPC_INST_CMPDI 0x2c200000 +#define PPC_INST_CMPLW 0x7c000040 +#define PPC_INST_CMPLWI 0x28000000 +#define PPC_INST_ADDI 0x38000000 +#define PPC_INST_ADDIS 0x3c000000 +#define PPC_INST_ADD 0x7c000214 +#define PPC_INST_SUB 0x7c000050 +#define PPC_INST_BLR 0x4e800020 +#define PPC_INST_BLRL 0x4e800021 +#define PPC_INST_MULLW 0x7c0001d6 +#define PPC_INST_MULHWU 0x7c000016 +#define PPC_INST_MULLI 0x1c000000 +#define PPC_INST_DIVWU 0x7c0003d6 +#define PPC_INST_RLWINM 0x54000000 +#define PPC_INST_RLDICR 0x78000004 +#define PPC_INST_SLW 0x7c000030 +#define PPC_INST_SRW 0x7c000430 +#define PPC_INST_AND 0x7c000038 +#define PPC_INST_ANDDOT 0x7c000039 +#define PPC_INST_OR 0x7c000378 +#define PPC_INST_ANDI 0x70000000 +#define PPC_INST_ORI 0x60000000 +#define PPC_INST_ORIS 0x64000000 +#define PPC_INST_NEG 0x7c0000d0 +#define PPC_INST_BRANCH 0x48000000 +#define PPC_INST_BRANCH_COND 0x40800000 +/* macros to insert fields into opcodes */ #define __PPC_RA (a) (((a) & 0x1f) << 16)#define __PPC_RB (b) (((b) & 0x1f) << 11)
@@ -83,6 +119,10 @@#define __PPC_T_TLB (t) (((t) & 0x3) << 21)#define __PPC_WC (w) (((w) & 0x3) << 21)#define __PPC_WS (w) (((w) & 0x1f) << 11) +#define __PPC_SH (s) __PPC_WS(s) +#define __PPC_MB (s) (((s) & 0x1f) << 6) +#define __PPC_ME (s) (((s) & 0x1f) << 1) +#define __PPC_BI (s) (((s) & 0x1f) << 16)/* * Only use the larx hint bit on 64bit CPUs. e500v1/v2 based CPUs will treat a
arch/powerpc/net/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# +# Arch-specific network modules +# +obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
arch/powerpc/net/bpf_jit.S new file +138 -0
138 lines not on this page · raw message
arch/powerpc/net/bpf_jit.h new file +226 -0
226 lines not on this page · raw message
arch/powerpc/net/bpf_jit_comp.c new file +697 -0
697 lines not on this page · raw message
On Fri, 2011-06-24 at 16:02 +1000, Matt Evans wrote:
[...]
arch/powerpc/net/bpf_jit_comp.c line 167
Quoted 9 lines
arch/powerpc/net/bpf_jit_comp.c new file +9
-0+case BPF_S_ALU_ADD_K :/* A += K; */ +if (!K ) +break ; +if (K <32768 ) +PPC_ADDI (r_A, r_A,K ); +else +PPC_ADDI (r_A, r_A,IMM_L (K )); +PPC_ADDIS (r_A, r_A,IMM_HA (K )); +break ;
Missing braces.
arch/powerpc/net/bpf_jit_comp.c line 180
Quoted 13 lines
arch/powerpc/net/bpf_jit_comp.c new file +13
-0+case BPF_S_ALU_SUB_X :/* A -= X; */ + ctx->seen |=SEEN_XREG ; +PPC_SUB (r_A, r_A, r_X); +break ; +case BPF_S_ALU_SUB_K :/* A -= K */ +if (!K ) +break ; +if (K <32768 ) +PPC_ADDI (r_A, r_A, -K ); +else +PPC_ADDI (r_A, r_A,IMM_L (-K )); +PPC_ADDIS (r_A, r_A,IMM_HA (-K )); +break ;
[...]
Here as well.
Ben.
Signature · 3 lines
Ben Hutchings, Senior Software Engineer, Solarflare Not speaking for my employer; that's the marketing department's job. They asked us to note that Solarflare product names are trademarked.
Quoted 29 lines · Ben Hutchings
On Fri, 2011-06-24 at 16:02 +1000, Matt Evans wrote:
[...]arch/powerpc/net/bpf_jit_comp.c line 167
Quoted 9 lines
arch/powerpc/net/bpf_jit_comp.c new file +9
-0+case BPF_S_ALU_ADD_K :/* A += K; */ +if (!K ) +break ; +if (K <32768 ) +PPC_ADDI (r_A, r_A,K ); +else +PPC_ADDI (r_A, r_A,IMM_L (K )); +PPC_ADDIS (r_A, r_A,IMM_HA (K )); +break ;Missing braces.
Quoted 13 lines
arch/powerpc/net/bpf_jit_comp.c new file +13
-0+case BPF_S_ALU_SUB_X :/* A -= X; */ + ctx->seen |=SEEN_XREG ; +PPC_SUB (r_A, r_A, r_X); +break ; +case BPF_S_ALU_SUB_K :/* A -= K */ +if (!K ) +break ; +if (K <32768 ) +PPC_ADDI (r_A, r_A, -K ); +else +PPC_ADDI (r_A, r_A,IMM_L (-K )); +PPC_ADDIS (r_A, r_A,IMM_HA (-K )); +break ;[...]
Quoted 1 line
Here as well.
Thanks, Ben -- oops! :) Really, just the ADDISes need to be conditional, too.
Cheers,
Matt
Review
Quoted 9 lines · Matt Evans
(quoted diff) +5
-0+ stdu r1, -128(r1); \ + addi r5, r1, 128+BPF_PPC_STACK_BASIC+(2*8); \ + addi r1, r1, 128; \ + PPC_STD(r_M + i, 1, -128 + (8*i)); + PPC_LD(r_M + i, 1, -128 + (8*i));
s/128/BPF_PPC_STACK_SAVE/?
Andreas.
Signature · 3 lines
Andreas Schwab, [email protected] GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5 "And now for something completely different."
Quoted 6 lines · Andreas Schwab
Quoted 3 lines · Matt Evans
arch/powerpc/net/bpf_jit.S new file +2
-0+ stdu r1, -128(r1); \ + addi r5, r1, 128+BPF_PPC_STACK_BASIC+(2*8); \
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +3
-0+ addi r1, r1, 128; \ + PPC_STD(r_M + i, 1, -128 + (8*i)); + PPC_LD(r_M + i, 1, -128 + (8*i));s/128/BPF_PPC_STACK_SAVE/?
I am not sure using registers to hold MEM[] is a win if MEM[idx] is used once in the filter
# tcpdump "tcp[20]+tcp[21]=0" -d (000) ldh [12] (001) jeq #0x800 jt 2 jf 15 (002) ldb [23] (003) jeq #0x6 jt 4 jf 15 (004) ldh [20] (005) jset #0x1fff jt 15 jf 6 (006) ldxb 4*([14]&0xf) (007) ldb [x + 34] (008) st M[1] (009) ldb [x + 35] (010) tax (011) ld M[1] (012) add x (013) jeq #0x0 jt 14 jf 15 (014) ret #65535 (015) ret #0
In this sample, we use M[1] once ( one store, one load)
So saving previous register content on stack in prologue, and restoring
it in epilogue actually slow down the code, and adds two instructions in filter asm code.
This also makes epilogue code not easy (not possible as a matter of fact) to unwind in helper function
In x86_64 implementation, I chose bpf_error be able to force an exception, not returning to JIT code but directly to bpf_func() caller
bpf_error:
# force a return 0 from jit handler
xor %eax,%eax
mov -8(%rbp),%rbx
leaveq
ret_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://lists.ozlabs.org/listinfo/linuxppc-dev
Hi Eric,
Quoted 37 lines · Eric Dumazet
Quoted 6 lines · Andreas Schwab
Quoted 3 lines · Matt Evans
arch/powerpc/net/bpf_jit.S new file +2
-0+ stdu r1, -128(r1); \ + addi r5, r1, 128+BPF_PPC_STACK_BASIC+(2*8); \Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +3
-0+ addi r1, r1, 128; \ + PPC_STD(r_M + i, 1, -128 + (8*i)); + PPC_LD(r_M + i, 1, -128 + (8*i));s/128/BPF_PPC_STACK_SAVE/?
I am not sure using registers to hold MEM[] is a win if MEM[idx] is used once in the filter
# tcpdump "tcp[20]+tcp[21]=0" -d (000) ldh [12] (001) jeq #0x800 jt 2 jf 15 (002) ldb [23] (003) jeq #0x6 jt 4 jf 15 (004) ldh [20] (005) jset #0x1fff jt 15 jf 6 (006) ldxb 4*([14]&0xf) (007) ldb [x + 34] (008) st M[1] (009) ldb [x + 35] (010) tax (011) ld M[1] (012) add x (013) jeq #0x0 jt 14 jf 15 (014) ret #65535 (015) ret #0
Quoted 5 lines
In this sample, we use M[1] once ( one store, one load)
So saving previous register content on stack in prologue, and restoring it in epilogue actually slow down the code, and adds two instructions in filter asm code.
The x86 version generates all accesses of M[x] as a load or store to the stackframe, so your example would result in one store + one load to/from the stack. More than one access would result in N stores/loads. By having M[] live in r16-31 on PPC, there is a one-off cost of saving/restoring the (non-volatile) register to the stack plus a per-use cost of a register-register move (MR, which is pretty cheap compared to loads/stores!).
You are correct in that, for a single store/load of M[1] above, I'll generate a STD, MR, MR, LD, but this extra cost of the two MRs is pretty small. With the current implementation the gains seen when accessing M[x] /more/ than once will IMHO more than justify this. (For several M[x] accesses, x86 would have several mem ops, PPC would have several reg-reg moves and one load+store.)
An obvious alternative would be to use some of the three free volatile registers first (in the hope that "most" filters won't use >3 M[] locations), with a simple register allocator. This would save the (non-volatile reg) spill/fill to stack, too. In the interest of simplicity I didn't want to do that on a first pass.
Quoted 6 lines
This also makes epilogue code not easy (not possible as a matter of fact) to unwind in helper function
In
x86_64implementation, I chosebpf_errorbe able to force an exception, not returning to JIT code but directly tobpf_func()caller
Quoted 6 lines
bpf_error:# force a return 0 from jit handler xor %eax,%eax mov -8(%rbp),%rbx leaveq ret
Yep, if I use non-volatile regs a return isn't just a simple stack "pop". Currently, I've an extra branch in the return path to hit the common epilogue. This could be optimised such that the out of line error path jumps directly to the common epilogue to return (rather than back to the callsite, checking a flag and /then/ to the epilogue) to speed up the non-error case. However, it's just a question of getting to the (existing) epilogue code to clean up; it doesn't need to be unwound in the helper function. I don't think this issue is a strong argument against having M[] exist in registers, though.
From the current position, I think going in the direction of using volatile regs (without backup/restore cost) is better than going in the direction of making all M[] references stack accesses. Do you think it's bearable to continue as it is and then perform that optimisation later?
Also, thanks for reading/commenting on the patch!
Cheers,
Matt
Quoted 6 lines · Andreas Schwab
Quoted 3 lines · Matt Evans
arch/powerpc/net/bpf_jit.S new file +2
-0+ stdu r1, -128(r1); \ + addi r5, r1, 128+BPF_PPC_STACK_BASIC+(2*8); \
Quoted 7 lines
Review
Quoted 5 lines
(quoted diff) +3
-0+ addi r1, r1, 128; \ + PPC_STD(r_M + i, 1, -128 + (8*i)); + PPC_LD(r_M + i, 1, -128 + (8*i));s/128/BPF_PPC_STACK_SAVE/?
Actually, that's a different 128, but that nicely illustrates that I should've #defined something more recognisable :-) The second set, with -128, is actually in the save area for non-volatile regs, whereas the first is just a stackframe size.
Cheers,
Matt