diff options
author | Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> | 2016-09-23 16:35:01 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2016-10-04 05:33:19 -0400 |
commit | ce0761419faefbe9e450749ccc879ff88843af12 (patch) | |
tree | e11d3ae238cd82b7d59274f0491c9ca5f47e8e63 | |
parent | 7b847f523fe07b4ad73a01cec49a4da86a9be412 (diff) |
powerpc/bpf: Implement support for tail calls
Tail calls allow JIT'ed eBPF programs to call into other JIT'ed eBPF
programs. This can be achieved either by:
(1) retaining the stack setup by the first eBPF program and having all
subsequent eBPF programs re-using it, or,
(2) by unwinding/tearing down the stack and having each eBPF program
deal with its own stack as it sees fit.
To ensure that this does not create loops, there is a limit to how many
tail calls can be done (currently 32). This requires the JIT'ed code to
maintain a count of the number of tail calls done so far.
Approach (1) is simple, but requires every eBPF program to have (almost)
the same prologue/epilogue, regardless of whether they need it. This is
inefficient for small eBPF programs which may not sometimes need a
prologue at all. As such, to minimize impact of tail call
implementation, we use approach (2) here which needs each eBPF program
in the chain to use its own prologue/epilogue. This is not ideal when
many tail calls are involved and when all the eBPF programs in the chain
have similar prologue/epilogue. However, the impact is restricted to
programs that do tail calls. Individual eBPF programs are not affected.
We maintain the tail call count in a fixed location on the stack and
updated tail call count values are passed in through this. The very
first eBPF program in a chain sets this up to 0 (the first 2
instructions). Subsequent tail calls skip the first two eBPF JIT
instructions to maintain the count. For programs that don't do tail
calls themselves, the first two instructions are NOPs.
Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r-- | arch/powerpc/include/asm/ppc-opcode.h | 2 | ||||
-rw-r--r-- | arch/powerpc/net/bpf_jit.h | 2 | ||||
-rw-r--r-- | arch/powerpc/net/bpf_jit64.h | 1 | ||||
-rw-r--r-- | arch/powerpc/net/bpf_jit_comp64.c | 149 |
4 files changed, 126 insertions, 28 deletions
diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 127ebf5862b4..54ff8ce7fa96 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h | |||
@@ -236,6 +236,7 @@ | |||
236 | #define PPC_INST_STWU 0x94000000 | 236 | #define PPC_INST_STWU 0x94000000 |
237 | #define PPC_INST_MFLR 0x7c0802a6 | 237 | #define PPC_INST_MFLR 0x7c0802a6 |
238 | #define PPC_INST_MTLR 0x7c0803a6 | 238 | #define PPC_INST_MTLR 0x7c0803a6 |
239 | #define PPC_INST_MTCTR 0x7c0903a6 | ||
239 | #define PPC_INST_CMPWI 0x2c000000 | 240 | #define PPC_INST_CMPWI 0x2c000000 |
240 | #define PPC_INST_CMPDI 0x2c200000 | 241 | #define PPC_INST_CMPDI 0x2c200000 |
241 | #define PPC_INST_CMPW 0x7c000000 | 242 | #define PPC_INST_CMPW 0x7c000000 |
@@ -250,6 +251,7 @@ | |||
250 | #define PPC_INST_SUB 0x7c000050 | 251 | #define PPC_INST_SUB 0x7c000050 |
251 | #define PPC_INST_BLR 0x4e800020 | 252 | #define PPC_INST_BLR 0x4e800020 |
252 | #define PPC_INST_BLRL 0x4e800021 | 253 | #define PPC_INST_BLRL 0x4e800021 |
254 | #define PPC_INST_BCTR 0x4e800420 | ||
253 | #define PPC_INST_MULLD 0x7c0001d2 | 255 | #define PPC_INST_MULLD 0x7c0001d2 |
254 | #define PPC_INST_MULLW 0x7c0001d6 | 256 | #define PPC_INST_MULLW 0x7c0001d6 |
255 | #define PPC_INST_MULHWU 0x7c000016 | 257 | #define PPC_INST_MULHWU 0x7c000016 |
diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h index d5301b6f20d0..89f70073dec8 100644 --- a/arch/powerpc/net/bpf_jit.h +++ b/arch/powerpc/net/bpf_jit.h | |||
@@ -40,6 +40,8 @@ | |||
40 | #define PPC_BLR() EMIT(PPC_INST_BLR) | 40 | #define PPC_BLR() EMIT(PPC_INST_BLR) |
41 | #define PPC_BLRL() EMIT(PPC_INST_BLRL) | 41 | #define PPC_BLRL() EMIT(PPC_INST_BLRL) |
42 | #define PPC_MTLR(r) EMIT(PPC_INST_MTLR | ___PPC_RT(r)) | 42 | #define PPC_MTLR(r) EMIT(PPC_INST_MTLR | ___PPC_RT(r)) |
43 | #define PPC_BCTR() EMIT(PPC_INST_BCTR) | ||
44 | #define PPC_MTCTR(r) EMIT(PPC_INST_MTCTR | ___PPC_RT(r)) | ||
43 | #define PPC_ADDI(d, a, i) EMIT(PPC_INST_ADDI | ___PPC_RT(d) | \ | 45 | #define PPC_ADDI(d, a, i) EMIT(PPC_INST_ADDI | ___PPC_RT(d) | \ |
44 | ___PPC_RA(a) | IMM_L(i)) | 46 | ___PPC_RA(a) | IMM_L(i)) |
45 | #define PPC_MR(d, a) PPC_OR(d, a, a) | 47 | #define PPC_MR(d, a) PPC_OR(d, a, a) |
diff --git a/arch/powerpc/net/bpf_jit64.h b/arch/powerpc/net/bpf_jit64.h index a1645d7a4033..038e00bf2b77 100644 --- a/arch/powerpc/net/bpf_jit64.h +++ b/arch/powerpc/net/bpf_jit64.h | |||
@@ -88,6 +88,7 @@ DECLARE_LOAD_FUNC(sk_load_byte); | |||
88 | #define SEEN_FUNC 0x1000 /* might call external helpers */ | 88 | #define SEEN_FUNC 0x1000 /* might call external helpers */ |
89 | #define SEEN_STACK 0x2000 /* uses BPF stack */ | 89 | #define SEEN_STACK 0x2000 /* uses BPF stack */ |
90 | #define SEEN_SKB 0x4000 /* uses sk_buff */ | 90 | #define SEEN_SKB 0x4000 /* uses sk_buff */ |
91 | #define SEEN_TAILCALL 0x8000 /* uses tail calls */ | ||
91 | 92 | ||
92 | struct codegen_context { | 93 | struct codegen_context { |
93 | /* | 94 | /* |
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c index 5f8c91fa612e..3ec29d6fba60 100644 --- a/arch/powerpc/net/bpf_jit_comp64.c +++ b/arch/powerpc/net/bpf_jit_comp64.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/filter.h> | 17 | #include <linux/filter.h> |
18 | #include <linux/if_vlan.h> | 18 | #include <linux/if_vlan.h> |
19 | #include <asm/kprobes.h> | 19 | #include <asm/kprobes.h> |
20 | #include <linux/bpf.h> | ||
20 | 21 | ||
21 | #include "bpf_jit64.h" | 22 | #include "bpf_jit64.h" |
22 | 23 | ||
@@ -77,6 +78,11 @@ static int bpf_jit_stack_local(struct codegen_context *ctx) | |||
77 | return -(BPF_PPC_STACK_SAVE + 16); | 78 | return -(BPF_PPC_STACK_SAVE + 16); |
78 | } | 79 | } |
79 | 80 | ||
81 | static int bpf_jit_stack_tailcallcnt(struct codegen_context *ctx) | ||
82 | { | ||
83 | return bpf_jit_stack_local(ctx) + 8; | ||
84 | } | ||
85 | |||
80 | static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg) | 86 | static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg) |
81 | { | 87 | { |
82 | if (reg >= BPF_PPC_NVR_MIN && reg < 32) | 88 | if (reg >= BPF_PPC_NVR_MIN && reg < 32) |
@@ -102,33 +108,25 @@ static void bpf_jit_emit_skb_loads(u32 *image, struct codegen_context *ctx) | |||
102 | PPC_BPF_LL(b2p[SKB_DATA_REG], 3, offsetof(struct sk_buff, data)); | 108 | PPC_BPF_LL(b2p[SKB_DATA_REG], 3, offsetof(struct sk_buff, data)); |
103 | } | 109 | } |
104 | 110 | ||
105 | static void bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func) | 111 | static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx) |
106 | { | 112 | { |
107 | #ifdef PPC64_ELF_ABI_v1 | 113 | int i; |
108 | /* func points to the function descriptor */ | 114 | |
109 | PPC_LI64(b2p[TMP_REG_2], func); | ||
110 | /* Load actual entry point from function descriptor */ | ||
111 | PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_2], 0); | ||
112 | /* ... and move it to LR */ | ||
113 | PPC_MTLR(b2p[TMP_REG_1]); | ||
114 | /* | 115 | /* |
115 | * Load TOC from function descriptor at offset 8. | 116 | * Initialize tail_call_cnt if we do tail calls. |
116 | * We can clobber r2 since we get called through a | 117 | * Otherwise, put in NOPs so that it can be skipped when we are |
117 | * function pointer (so caller will save/restore r2) | 118 | * invoked through a tail call. |
118 | * and since we don't use a TOC ourself. | ||
119 | */ | 119 | */ |
120 | PPC_BPF_LL(2, b2p[TMP_REG_2], 8); | 120 | if (ctx->seen & SEEN_TAILCALL) { |
121 | #else | 121 | PPC_LI(b2p[TMP_REG_1], 0); |
122 | /* We can clobber r12 */ | 122 | /* this goes in the redzone */ |
123 | PPC_FUNC_ADDR(12, func); | 123 | PPC_BPF_STL(b2p[TMP_REG_1], 1, -(BPF_PPC_STACK_SAVE + 8)); |
124 | PPC_MTLR(12); | 124 | } else { |
125 | #endif | 125 | PPC_NOP(); |
126 | PPC_BLRL(); | 126 | PPC_NOP(); |
127 | } | 127 | } |
128 | 128 | ||
129 | static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx) | 129 | #define BPF_TAILCALL_PROLOGUE_SIZE 8 |
130 | { | ||
131 | int i; | ||
132 | 130 | ||
133 | if (bpf_has_stack_frame(ctx)) { | 131 | if (bpf_has_stack_frame(ctx)) { |
134 | /* | 132 | /* |
@@ -170,13 +168,10 @@ static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx) | |||
170 | STACK_FRAME_MIN_SIZE + MAX_BPF_STACK); | 168 | STACK_FRAME_MIN_SIZE + MAX_BPF_STACK); |
171 | } | 169 | } |
172 | 170 | ||
173 | static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) | 171 | static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx) |
174 | { | 172 | { |
175 | int i; | 173 | int i; |
176 | 174 | ||
177 | /* Move result to r3 */ | ||
178 | PPC_MR(3, b2p[BPF_REG_0]); | ||
179 | |||
180 | /* Restore NVRs */ | 175 | /* Restore NVRs */ |
181 | for (i = BPF_REG_6; i <= BPF_REG_10; i++) | 176 | for (i = BPF_REG_6; i <= BPF_REG_10; i++) |
182 | if (bpf_is_seen_register(ctx, i)) | 177 | if (bpf_is_seen_register(ctx, i)) |
@@ -198,10 +193,105 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) | |||
198 | PPC_MTLR(0); | 193 | PPC_MTLR(0); |
199 | } | 194 | } |
200 | } | 195 | } |
196 | } | ||
197 | |||
198 | static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) | ||
199 | { | ||
200 | bpf_jit_emit_common_epilogue(image, ctx); | ||
201 | |||
202 | /* Move result to r3 */ | ||
203 | PPC_MR(3, b2p[BPF_REG_0]); | ||
201 | 204 | ||
202 | PPC_BLR(); | 205 | PPC_BLR(); |
203 | } | 206 | } |
204 | 207 | ||
208 | static void bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func) | ||
209 | { | ||
210 | #ifdef PPC64_ELF_ABI_v1 | ||
211 | /* func points to the function descriptor */ | ||
212 | PPC_LI64(b2p[TMP_REG_2], func); | ||
213 | /* Load actual entry point from function descriptor */ | ||
214 | PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_2], 0); | ||
215 | /* ... and move it to LR */ | ||
216 | PPC_MTLR(b2p[TMP_REG_1]); | ||
217 | /* | ||
218 | * Load TOC from function descriptor at offset 8. | ||
219 | * We can clobber r2 since we get called through a | ||
220 | * function pointer (so caller will save/restore r2) | ||
221 | * and since we don't use a TOC ourself. | ||
222 | */ | ||
223 | PPC_BPF_LL(2, b2p[TMP_REG_2], 8); | ||
224 | #else | ||
225 | /* We can clobber r12 */ | ||
226 | PPC_FUNC_ADDR(12, func); | ||
227 | PPC_MTLR(12); | ||
228 | #endif | ||
229 | PPC_BLRL(); | ||
230 | } | ||
231 | |||
232 | static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 out) | ||
233 | { | ||
234 | /* | ||
235 | * By now, the eBPF program has already setup parameters in r3, r4 and r5 | ||
236 | * r3/BPF_REG_1 - pointer to ctx -- passed as is to the next bpf program | ||
237 | * r4/BPF_REG_2 - pointer to bpf_array | ||
238 | * r5/BPF_REG_3 - index in bpf_array | ||
239 | */ | ||
240 | int b2p_bpf_array = b2p[BPF_REG_2]; | ||
241 | int b2p_index = b2p[BPF_REG_3]; | ||
242 | |||
243 | /* | ||
244 | * if (index >= array->map.max_entries) | ||
245 | * goto out; | ||
246 | */ | ||
247 | PPC_LWZ(b2p[TMP_REG_1], b2p_bpf_array, offsetof(struct bpf_array, map.max_entries)); | ||
248 | PPC_CMPLW(b2p_index, b2p[TMP_REG_1]); | ||
249 | PPC_BCC(COND_GE, out); | ||
250 | |||
251 | /* | ||
252 | * if (tail_call_cnt > MAX_TAIL_CALL_CNT) | ||
253 | * goto out; | ||
254 | */ | ||
255 | PPC_LD(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx)); | ||
256 | PPC_CMPLWI(b2p[TMP_REG_1], MAX_TAIL_CALL_CNT); | ||
257 | PPC_BCC(COND_GT, out); | ||
258 | |||
259 | /* | ||
260 | * tail_call_cnt++; | ||
261 | */ | ||
262 | PPC_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], 1); | ||
263 | PPC_BPF_STL(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx)); | ||
264 | |||
265 | /* prog = array->ptrs[index]; */ | ||
266 | PPC_MULI(b2p[TMP_REG_1], b2p_index, 8); | ||
267 | PPC_ADD(b2p[TMP_REG_1], b2p[TMP_REG_1], b2p_bpf_array); | ||
268 | PPC_LD(b2p[TMP_REG_1], b2p[TMP_REG_1], offsetof(struct bpf_array, ptrs)); | ||
269 | |||
270 | /* | ||
271 | * if (prog == NULL) | ||
272 | * goto out; | ||
273 | */ | ||
274 | PPC_CMPLDI(b2p[TMP_REG_1], 0); | ||
275 | PPC_BCC(COND_EQ, out); | ||
276 | |||
277 | /* goto *(prog->bpf_func + prologue_size); */ | ||
278 | PPC_LD(b2p[TMP_REG_1], b2p[TMP_REG_1], offsetof(struct bpf_prog, bpf_func)); | ||
279 | #ifdef PPC64_ELF_ABI_v1 | ||
280 | /* skip past the function descriptor */ | ||
281 | PPC_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], | ||
282 | FUNCTION_DESCR_SIZE + BPF_TAILCALL_PROLOGUE_SIZE); | ||
283 | #else | ||
284 | PPC_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], BPF_TAILCALL_PROLOGUE_SIZE); | ||
285 | #endif | ||
286 | PPC_MTCTR(b2p[TMP_REG_1]); | ||
287 | |||
288 | /* tear down stack, restore NVRs, ... */ | ||
289 | bpf_jit_emit_common_epilogue(image, ctx); | ||
290 | |||
291 | PPC_BCTR(); | ||
292 | /* out: */ | ||
293 | } | ||
294 | |||
205 | /* Assemble the body code between the prologue & epilogue */ | 295 | /* Assemble the body code between the prologue & epilogue */ |
206 | static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, | 296 | static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, |
207 | struct codegen_context *ctx, | 297 | struct codegen_context *ctx, |
@@ -846,9 +936,12 @@ common_load: | |||
846 | break; | 936 | break; |
847 | 937 | ||
848 | /* | 938 | /* |
849 | * TODO: Tail call | 939 | * Tail call |
850 | */ | 940 | */ |
851 | case BPF_JMP | BPF_CALL | BPF_X: | 941 | case BPF_JMP | BPF_CALL | BPF_X: |
942 | ctx->seen |= SEEN_TAILCALL; | ||
943 | bpf_jit_emit_tail_call(image, ctx, addrs[i + 1]); | ||
944 | break; | ||
852 | 945 | ||
853 | default: | 946 | default: |
854 | /* | 947 | /* |