Skip to content

Commit ce07614

Browse files
rnavmpe
authored andcommitted
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 <[email protected]> Signed-off-by: Michael Ellerman <[email protected]>
1 parent 7b847f5 commit ce07614

File tree

4 files changed

+126
-28
lines changed

4 files changed

+126
-28
lines changed

arch/powerpc/include/asm/ppc-opcode.h

+2
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
#define PPC_INST_STWU 0x94000000
237237
#define PPC_INST_MFLR 0x7c0802a6
238238
#define PPC_INST_MTLR 0x7c0803a6
239+
#define PPC_INST_MTCTR 0x7c0903a6
239240
#define PPC_INST_CMPWI 0x2c000000
240241
#define PPC_INST_CMPDI 0x2c200000
241242
#define PPC_INST_CMPW 0x7c000000
@@ -250,6 +251,7 @@
250251
#define PPC_INST_SUB 0x7c000050
251252
#define PPC_INST_BLR 0x4e800020
252253
#define PPC_INST_BLRL 0x4e800021
254+
#define PPC_INST_BCTR 0x4e800420
253255
#define PPC_INST_MULLD 0x7c0001d2
254256
#define PPC_INST_MULLW 0x7c0001d6
255257
#define PPC_INST_MULHWU 0x7c000016

arch/powerpc/net/bpf_jit.h

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
#define PPC_BLR() EMIT(PPC_INST_BLR)
4141
#define PPC_BLRL() EMIT(PPC_INST_BLRL)
4242
#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))
4345
#define PPC_ADDI(d, a, i) EMIT(PPC_INST_ADDI | ___PPC_RT(d) | \
4446
___PPC_RA(a) | IMM_L(i))
4547
#define PPC_MR(d, a) PPC_OR(d, a, a)

arch/powerpc/net/bpf_jit64.h

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ DECLARE_LOAD_FUNC(sk_load_byte);
8888
#define SEEN_FUNC 0x1000 /* might call external helpers */
8989
#define SEEN_STACK 0x2000 /* uses BPF stack */
9090
#define SEEN_SKB 0x4000 /* uses sk_buff */
91+
#define SEEN_TAILCALL 0x8000 /* uses tail calls */
9192

9293
struct codegen_context {
9394
/*

arch/powerpc/net/bpf_jit_comp64.c

+121-28
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/filter.h>
1818
#include <linux/if_vlan.h>
1919
#include <asm/kprobes.h>
20+
#include <linux/bpf.h>
2021

2122
#include "bpf_jit64.h"
2223

@@ -77,6 +78,11 @@ static int bpf_jit_stack_local(struct codegen_context *ctx)
7778
return -(BPF_PPC_STACK_SAVE + 16);
7879
}
7980

81+
static int bpf_jit_stack_tailcallcnt(struct codegen_context *ctx)
82+
{
83+
return bpf_jit_stack_local(ctx) + 8;
84+
}
85+
8086
static int bpf_jit_stack_offsetof(struct codegen_context *ctx, int reg)
8187
{
8288
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)
102108
PPC_BPF_LL(b2p[SKB_DATA_REG], 3, offsetof(struct sk_buff, data));
103109
}
104110

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)
106112
{
107-
#ifdef PPC64_ELF_ABI_v1
108-
/* func points to the function descriptor */
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]);
113+
int i;
114+
114115
/*
115-
* Load TOC from function descriptor at offset 8.
116-
* We can clobber r2 since we get called through a
117-
* function pointer (so caller will save/restore r2)
118-
* and since we don't use a TOC ourself.
116+
* Initialize tail_call_cnt if we do tail calls.
117+
* Otherwise, put in NOPs so that it can be skipped when we are
118+
* invoked through a tail call.
119119
*/
120-
PPC_BPF_LL(2, b2p[TMP_REG_2], 8);
121-
#else
122-
/* We can clobber r12 */
123-
PPC_FUNC_ADDR(12, func);
124-
PPC_MTLR(12);
125-
#endif
126-
PPC_BLRL();
127-
}
120+
if (ctx->seen & SEEN_TAILCALL) {
121+
PPC_LI(b2p[TMP_REG_1], 0);
122+
/* this goes in the redzone */
123+
PPC_BPF_STL(b2p[TMP_REG_1], 1, -(BPF_PPC_STACK_SAVE + 8));
124+
} else {
125+
PPC_NOP();
126+
PPC_NOP();
127+
}
128128

129-
static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
130-
{
131-
int i;
129+
#define BPF_TAILCALL_PROLOGUE_SIZE 8
132130

133131
if (bpf_has_stack_frame(ctx)) {
134132
/*
@@ -170,13 +168,10 @@ static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
170168
STACK_FRAME_MIN_SIZE + MAX_BPF_STACK);
171169
}
172170

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)
174172
{
175173
int i;
176174

177-
/* Move result to r3 */
178-
PPC_MR(3, b2p[BPF_REG_0]);
179-
180175
/* Restore NVRs */
181176
for (i = BPF_REG_6; i <= BPF_REG_10; i++)
182177
if (bpf_is_seen_register(ctx, i))
@@ -198,10 +193,105 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
198193
PPC_MTLR(0);
199194
}
200195
}
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]);
201204

202205
PPC_BLR();
203206
}
204207

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+
205295
/* Assemble the body code between the prologue & epilogue */
206296
static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
207297
struct codegen_context *ctx,
@@ -846,9 +936,12 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
846936
break;
847937

848938
/*
849-
* TODO: Tail call
939+
* Tail call
850940
*/
851941
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;
852945

853946
default:
854947
/*

0 commit comments

Comments
 (0)