aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/net
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@linux.vnet.ibm.com>2015-06-09 00:51:06 -0400
committerDavid S. Miller <davem@davemloft.net>2015-06-09 14:47:10 -0400
commit6651ee070b3124fe9b9db383e3a895a0e4aded65 (patch)
treecd1662179dd84ff08d76eb85037900538540c3fc /arch/s390/net
parent941742f49762ba4c908510f036b09a46c1b14513 (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.h10
-rw-r--r--arch/s390/net/bpf_jit_comp.c106
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 */
287static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) 310static 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 */
302static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re) 325static 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 */
375static void bpf_jit_prologue(struct bpf_jit *jit) 398static 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))