diff options
author | Michael Holzheu <holzheu@linux.vnet.ibm.com> | 2015-06-09 00:51:06 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-06-09 14:47:10 -0400 |
commit | 6651ee070b3124fe9b9db383e3a895a0e4aded65 (patch) | |
tree | cd1662179dd84ff08d76eb85037900538540c3fc /arch/s390/net | |
parent | 941742f49762ba4c908510f036b09a46c1b14513 (diff) |
s390/bpf: implement bpf_tail_call() helper
bpf_tail_call() arguments:
- ctx......: Context pointer
- jmp_table: One of BPF_MAP_TYPE_PROG_ARRAY maps used as the jump table
- index....: Index in the jump table
In this implementation s390x JIT does stack unwinding and jumps into the
callee program prologue. Caller and callee use the same stack.
With this patch a tail call generates the following code on s390x:
if (index >= array->map.max_entries)
goto out
000003ff8001c7e4: e31030100016 llgf %r1,16(%r3)
000003ff8001c7ea: ec41001fa065 clgrj %r4,%r1,10,3ff8001c828
if (tail_call_cnt++ > MAX_TAIL_CALL_CNT)
goto out;
000003ff8001c7f0: a7080001 lhi %r0,1
000003ff8001c7f4: eb10f25000fa laal %r1,%r0,592(%r15)
000003ff8001c7fa: ec120017207f clij %r1,32,2,3ff8001c828
prog = array->prog[index];
if (prog == NULL)
goto out;
000003ff8001c800: eb140003000d sllg %r1,%r4,3
000003ff8001c806: e31310800004 lg %r1,128(%r3,%r1)
000003ff8001c80c: ec18000e007d clgij %r1,0,8,3ff8001c828
Restore registers before calling function
000003ff8001c812: eb68f2980004 lmg %r6,%r8,664(%r15)
000003ff8001c818: ebbff2c00004 lmg %r11,%r15,704(%r15)
goto *(prog->bpf_func + tail_call_start);
000003ff8001c81e: e31100200004 lg %r1,32(%r1,%r0)
000003ff8001c824: 47f01006 bc 15,6(%r1)
Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/s390/net')
-rw-r--r-- | arch/s390/net/bpf_jit.h | 10 | ||||
-rw-r--r-- | arch/s390/net/bpf_jit_comp.c | 106 |
2 files changed, 112 insertions, 4 deletions
diff --git a/arch/s390/net/bpf_jit.h b/arch/s390/net/bpf_jit.h index de156ba3bd71..f6498eec9ee1 100644 --- a/arch/s390/net/bpf_jit.h +++ b/arch/s390/net/bpf_jit.h | |||
@@ -28,6 +28,9 @@ extern u8 sk_load_word[], sk_load_half[], sk_load_byte[]; | |||
28 | * | old backchain | | | 28 | * | old backchain | | |
29 | * +---------------+ | | 29 | * +---------------+ | |
30 | * | r15 - r6 | | | 30 | * | r15 - r6 | | |
31 | * +---------------+ | | ||
32 | * | 4 byte align | | | ||
33 | * | tail_call_cnt | | | ||
31 | * BFP -> +===============+ | | 34 | * BFP -> +===============+ | |
32 | * | | | | 35 | * | | | |
33 | * | BPF stack | | | 36 | * | BPF stack | | |
@@ -46,14 +49,17 @@ extern u8 sk_load_word[], sk_load_half[], sk_load_byte[]; | |||
46 | * R15 -> +---------------+ + low | 49 | * R15 -> +---------------+ + low |
47 | * | 50 | * |
48 | * We get 160 bytes stack space from calling function, but only use | 51 | * We get 160 bytes stack space from calling function, but only use |
49 | * 11 * 8 byte (old backchain + r15 - r6) for storing registers. | 52 | * 12 * 8 byte for old backchain, r15..r6, and tail_call_cnt. |
50 | */ | 53 | */ |
51 | #define STK_SPACE (MAX_BPF_STACK + 8 + 4 + 4 + 160) | 54 | #define STK_SPACE (MAX_BPF_STACK + 8 + 4 + 4 + 160) |
52 | #define STK_160_UNUSED (160 - 11 * 8) | 55 | #define STK_160_UNUSED (160 - 12 * 8) |
53 | #define STK_OFF (STK_SPACE - STK_160_UNUSED) | 56 | #define STK_OFF (STK_SPACE - STK_160_UNUSED) |
54 | #define STK_OFF_TMP 160 /* Offset of tmp buffer on stack */ | 57 | #define STK_OFF_TMP 160 /* Offset of tmp buffer on stack */ |
55 | #define STK_OFF_HLEN 168 /* Offset of SKB header length on stack */ | 58 | #define STK_OFF_HLEN 168 /* Offset of SKB header length on stack */ |
56 | 59 | ||
60 | #define STK_OFF_R6 (160 - 11 * 8) /* Offset of r6 on stack */ | ||
61 | #define STK_OFF_TCCNT (160 - 12 * 8) /* Offset of tail_call_cnt on stack */ | ||
62 | |||
57 | /* Offset to skip condition code check */ | 63 | /* Offset to skip condition code check */ |
58 | #define OFF_OK 4 | 64 | #define OFF_OK 4 |
59 | 65 | ||
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 55423d8be580..d3766dd67e23 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/netdevice.h> | 21 | #include <linux/netdevice.h> |
22 | #include <linux/filter.h> | 22 | #include <linux/filter.h> |
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | #include <linux/bpf.h> | ||
24 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
25 | #include <asm/dis.h> | 26 | #include <asm/dis.h> |
26 | #include "bpf_jit.h" | 27 | #include "bpf_jit.h" |
@@ -40,6 +41,8 @@ struct bpf_jit { | |||
40 | int base_ip; /* Base address for literal pool */ | 41 | int base_ip; /* Base address for literal pool */ |
41 | int ret0_ip; /* Address of return 0 */ | 42 | int ret0_ip; /* Address of return 0 */ |
42 | int exit_ip; /* Address of exit */ | 43 | int exit_ip; /* Address of exit */ |
44 | int tail_call_start; /* Tail call start offset */ | ||
45 | int labels[1]; /* Labels for local jumps */ | ||
43 | }; | 46 | }; |
44 | 47 | ||
45 | #define BPF_SIZE_MAX 4096 /* Max size for program */ | 48 | #define BPF_SIZE_MAX 4096 /* Max size for program */ |
@@ -49,6 +52,7 @@ struct bpf_jit { | |||
49 | #define SEEN_RET0 4 /* ret0_ip points to a valid return 0 */ | 52 | #define SEEN_RET0 4 /* ret0_ip points to a valid return 0 */ |
50 | #define SEEN_LITERAL 8 /* code uses literals */ | 53 | #define SEEN_LITERAL 8 /* code uses literals */ |
51 | #define SEEN_FUNC 16 /* calls C functions */ | 54 | #define SEEN_FUNC 16 /* calls C functions */ |
55 | #define SEEN_TAIL_CALL 32 /* code uses tail calls */ | ||
52 | #define SEEN_STACK (SEEN_FUNC | SEEN_MEM | SEEN_SKB) | 56 | #define SEEN_STACK (SEEN_FUNC | SEEN_MEM | SEEN_SKB) |
53 | 57 | ||
54 | /* | 58 | /* |
@@ -60,6 +64,7 @@ struct bpf_jit { | |||
60 | #define REG_L (__MAX_BPF_REG+3) /* Literal pool register */ | 64 | #define REG_L (__MAX_BPF_REG+3) /* Literal pool register */ |
61 | #define REG_15 (__MAX_BPF_REG+4) /* Register 15 */ | 65 | #define REG_15 (__MAX_BPF_REG+4) /* Register 15 */ |
62 | #define REG_0 REG_W0 /* Register 0 */ | 66 | #define REG_0 REG_W0 /* Register 0 */ |
67 | #define REG_1 REG_W1 /* Register 1 */ | ||
63 | #define REG_2 BPF_REG_1 /* Register 2 */ | 68 | #define REG_2 BPF_REG_1 /* Register 2 */ |
64 | #define REG_14 BPF_REG_0 /* Register 14 */ | 69 | #define REG_14 BPF_REG_0 /* Register 14 */ |
65 | 70 | ||
@@ -223,6 +228,24 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) | |||
223 | REG_SET_SEEN(b3); \ | 228 | REG_SET_SEEN(b3); \ |
224 | }) | 229 | }) |
225 | 230 | ||
231 | #define EMIT6_PCREL_LABEL(op1, op2, b1, b2, label, mask) \ | ||
232 | ({ \ | ||
233 | int rel = (jit->labels[label] - jit->prg) >> 1; \ | ||
234 | _EMIT6(op1 | reg(b1, b2) << 16 | (rel & 0xffff), \ | ||
235 | op2 | mask << 12); \ | ||
236 | REG_SET_SEEN(b1); \ | ||
237 | REG_SET_SEEN(b2); \ | ||
238 | }) | ||
239 | |||
240 | #define EMIT6_PCREL_IMM_LABEL(op1, op2, b1, imm, label, mask) \ | ||
241 | ({ \ | ||
242 | int rel = (jit->labels[label] - jit->prg) >> 1; \ | ||
243 | _EMIT6(op1 | (reg_high(b1) | mask) << 16 | \ | ||
244 | (rel & 0xffff), op2 | (imm & 0xff) << 8); \ | ||
245 | REG_SET_SEEN(b1); \ | ||
246 | BUILD_BUG_ON(((unsigned long) imm) > 0xff); \ | ||
247 | }) | ||
248 | |||
226 | #define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \ | 249 | #define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \ |
227 | ({ \ | 250 | ({ \ |
228 | /* Branch instruction needs 6 bytes */ \ | 251 | /* Branch instruction needs 6 bytes */ \ |
@@ -286,7 +309,7 @@ static void jit_fill_hole(void *area, unsigned int size) | |||
286 | */ | 309 | */ |
287 | static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) | 310 | static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) |
288 | { | 311 | { |
289 | u32 off = 72 + (rs - 6) * 8; | 312 | u32 off = STK_OFF_R6 + (rs - 6) * 8; |
290 | 313 | ||
291 | if (rs == re) | 314 | if (rs == re) |
292 | /* stg %rs,off(%r15) */ | 315 | /* stg %rs,off(%r15) */ |
@@ -301,7 +324,7 @@ static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) | |||
301 | */ | 324 | */ |
302 | static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re) | 325 | static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re) |
303 | { | 326 | { |
304 | u32 off = 72 + (rs - 6) * 8; | 327 | u32 off = STK_OFF_R6 + (rs - 6) * 8; |
305 | 328 | ||
306 | if (jit->seen & SEEN_STACK) | 329 | if (jit->seen & SEEN_STACK) |
307 | off += STK_OFF; | 330 | off += STK_OFF; |
@@ -374,6 +397,16 @@ static void save_restore_regs(struct bpf_jit *jit, int op) | |||
374 | */ | 397 | */ |
375 | static void bpf_jit_prologue(struct bpf_jit *jit) | 398 | static void bpf_jit_prologue(struct bpf_jit *jit) |
376 | { | 399 | { |
400 | if (jit->seen & SEEN_TAIL_CALL) { | ||
401 | /* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */ | ||
402 | _EMIT6(0xd703f000 | STK_OFF_TCCNT, 0xf000 | STK_OFF_TCCNT); | ||
403 | } else { | ||
404 | /* j tail_call_start: NOP if no tail calls are used */ | ||
405 | EMIT4_PCREL(0xa7f40000, 6); | ||
406 | _EMIT2(0); | ||
407 | } | ||
408 | /* Tail calls have to skip above initialization */ | ||
409 | jit->tail_call_start = jit->prg; | ||
377 | /* Save registers */ | 410 | /* Save registers */ |
378 | save_restore_regs(jit, REGS_SAVE); | 411 | save_restore_regs(jit, REGS_SAVE); |
379 | /* Setup literal pool */ | 412 | /* Setup literal pool */ |
@@ -951,6 +984,75 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, int i | |||
951 | EMIT4(0xb9040000, BPF_REG_0, REG_2); | 984 | EMIT4(0xb9040000, BPF_REG_0, REG_2); |
952 | break; | 985 | break; |
953 | } | 986 | } |
987 | case BPF_JMP | BPF_CALL | BPF_X: | ||
988 | /* | ||
989 | * Implicit input: | ||
990 | * B1: pointer to ctx | ||
991 | * B2: pointer to bpf_array | ||
992 | * B3: index in bpf_array | ||
993 | */ | ||
994 | jit->seen |= SEEN_TAIL_CALL; | ||
995 | |||
996 | /* | ||
997 | * if (index >= array->map.max_entries) | ||
998 | * goto out; | ||
999 | */ | ||
1000 | |||
1001 | /* llgf %w1,map.max_entries(%b2) */ | ||
1002 | EMIT6_DISP_LH(0xe3000000, 0x0016, REG_W1, REG_0, BPF_REG_2, | ||
1003 | offsetof(struct bpf_array, map.max_entries)); | ||
1004 | /* clgrj %b3,%w1,0xa,label0: if %b3 >= %w1 goto out */ | ||
1005 | EMIT6_PCREL_LABEL(0xec000000, 0x0065, BPF_REG_3, | ||
1006 | REG_W1, 0, 0xa); | ||
1007 | |||
1008 | /* | ||
1009 | * if (tail_call_cnt++ > MAX_TAIL_CALL_CNT) | ||
1010 | * goto out; | ||
1011 | */ | ||
1012 | |||
1013 | if (jit->seen & SEEN_STACK) | ||
1014 | off = STK_OFF_TCCNT + STK_OFF; | ||
1015 | else | ||
1016 | off = STK_OFF_TCCNT; | ||
1017 | /* lhi %w0,1 */ | ||
1018 | EMIT4_IMM(0xa7080000, REG_W0, 1); | ||
1019 | /* laal %w1,%w0,off(%r15) */ | ||
1020 | EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W1, REG_W0, REG_15, off); | ||
1021 | /* clij %w1,MAX_TAIL_CALL_CNT,0x2,label0 */ | ||
1022 | EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007f, REG_W1, | ||
1023 | MAX_TAIL_CALL_CNT, 0, 0x2); | ||
1024 | |||
1025 | /* | ||
1026 | * prog = array->prog[index]; | ||
1027 | * if (prog == NULL) | ||
1028 | * goto out; | ||
1029 | */ | ||
1030 | |||
1031 | /* sllg %r1,%b3,3: %r1 = index * 8 */ | ||
1032 | EMIT6_DISP_LH(0xeb000000, 0x000d, REG_1, BPF_REG_3, REG_0, 3); | ||
1033 | /* lg %r1,prog(%b2,%r1) */ | ||
1034 | EMIT6_DISP_LH(0xe3000000, 0x0004, REG_1, BPF_REG_2, | ||
1035 | REG_1, offsetof(struct bpf_array, prog)); | ||
1036 | /* clgij %r1,0,0x8,label0 */ | ||
1037 | EMIT6_PCREL_IMM_LABEL(0xec000000, 0x007d, REG_1, 0, 0, 0x8); | ||
1038 | |||
1039 | /* | ||
1040 | * Restore registers before calling function | ||
1041 | */ | ||
1042 | save_restore_regs(jit, REGS_RESTORE); | ||
1043 | |||
1044 | /* | ||
1045 | * goto *(prog->bpf_func + tail_call_start); | ||
1046 | */ | ||
1047 | |||
1048 | /* lg %r1,bpf_func(%r1) */ | ||
1049 | EMIT6_DISP_LH(0xe3000000, 0x0004, REG_1, REG_1, REG_0, | ||
1050 | offsetof(struct bpf_prog, bpf_func)); | ||
1051 | /* bc 0xf,tail_call_start(%r1) */ | ||
1052 | _EMIT4(0x47f01000 + jit->tail_call_start); | ||
1053 | /* out: */ | ||
1054 | jit->labels[0] = jit->prg; | ||
1055 | break; | ||
954 | case BPF_JMP | BPF_EXIT: /* return b0 */ | 1056 | case BPF_JMP | BPF_EXIT: /* return b0 */ |
955 | last = (i == fp->len - 1) ? 1 : 0; | 1057 | last = (i == fp->len - 1) ? 1 : 0; |
956 | if (last && !(jit->seen & SEEN_RET0)) | 1058 | if (last && !(jit->seen & SEEN_RET0)) |