diff options
| author | Mircea Gherzan <mgherzan@gmail.com> | 2012-03-16 08:37:12 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-03-24 05:38:56 -0400 |
| commit | ddecdfcea0ae891f782ae853771c867ab51024c2 (patch) | |
| tree | bf344a86217258b88425834ef513585d71f58861 | |
| parent | 09f05d8529ff4aa92311c1a55ce35ac98cb59b8c (diff) | |
ARM: 7259/3: net: JIT compiler for packet filters
Based of Matt Evans's PPC64 implementation.
The compiler generates ARM instructions but interworking is
supported for Thumb2 kernels.
Supports both little and big endian. Unaligned loads are emitted
for ARMv6+. Not all the BPF opcodes that deal with ancillary data
are supported. The scratch memory of the filter lives on the stack.
Hardware integer division is used if it is available.
Enabled in the same way as for x86-64 and PPC64:
echo 1 > /proc/sys/net/core/bpf_jit_enable
A value greater than 1 enables opcode output.
Signed-off-by: Mircea Gherzan <mgherzan@gmail.com>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | arch/arm/Kconfig | 1 | ||||
| -rw-r--r-- | arch/arm/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/net/Makefile | 3 | ||||
| -rw-r--r-- | arch/arm/net/bpf_jit_32.c | 915 | ||||
| -rw-r--r-- | arch/arm/net/bpf_jit_32.h | 190 |
5 files changed, 1110 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ee6c7154c9a1..746ab6d6a205 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -34,6 +34,7 @@ config ARM | |||
| 34 | select GENERIC_IRQ_SHOW | 34 | select GENERIC_IRQ_SHOW |
| 35 | select CPU_PM if (SUSPEND || CPU_IDLE) | 35 | select CPU_PM if (SUSPEND || CPU_IDLE) |
| 36 | select GENERIC_PCI_IOMAP | 36 | select GENERIC_PCI_IOMAP |
| 37 | select HAVE_BPF_JIT | ||
| 37 | help | 38 | help |
| 38 | The ARM series is a line of low-power-consumption RISC chip designs | 39 | The ARM series is a line of low-power-consumption RISC chip designs |
| 39 | licensed by ARM Ltd and targeted at embedded applications and | 40 | licensed by ARM Ltd and targeted at embedded applications and |
diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 40319d91bb7f..93d63be8fa59 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile | |||
| @@ -253,6 +253,7 @@ core-$(CONFIG_VFP) += arch/arm/vfp/ | |||
| 253 | 253 | ||
| 254 | # If we have a machine-specific directory, then include it in the build. | 254 | # If we have a machine-specific directory, then include it in the build. |
| 255 | core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ | 255 | core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ |
| 256 | core-y += arch/arm/net/ | ||
| 256 | core-y += $(machdirs) $(platdirs) | 257 | core-y += $(machdirs) $(platdirs) |
| 257 | 258 | ||
| 258 | drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ | 259 | drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ |
diff --git a/arch/arm/net/Makefile b/arch/arm/net/Makefile new file mode 100644 index 000000000000..c2c10841b6be --- /dev/null +++ b/arch/arm/net/Makefile | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | # ARM-specific networking code | ||
| 2 | |||
| 3 | obj-$(CONFIG_BPF_JIT) += bpf_jit_32.o | ||
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c new file mode 100644 index 000000000000..62135849f48b --- /dev/null +++ b/arch/arm/net/bpf_jit_32.c | |||
| @@ -0,0 +1,915 @@ | |||
| 1 | /* | ||
| 2 | * Just-In-Time compiler for BPF filters on 32bit ARM | ||
| 3 | * | ||
| 4 | * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; version 2 of the License. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/bitops.h> | ||
| 12 | #include <linux/compiler.h> | ||
| 13 | #include <linux/errno.h> | ||
| 14 | #include <linux/filter.h> | ||
| 15 | #include <linux/moduleloader.h> | ||
| 16 | #include <linux/netdevice.h> | ||
| 17 | #include <linux/string.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <asm/cacheflush.h> | ||
| 20 | #include <asm/hwcap.h> | ||
| 21 | |||
| 22 | #include "bpf_jit_32.h" | ||
| 23 | |||
| 24 | /* | ||
| 25 | * ABI: | ||
| 26 | * | ||
| 27 | * r0 scratch register | ||
| 28 | * r4 BPF register A | ||
| 29 | * r5 BPF register X | ||
| 30 | * r6 pointer to the skb | ||
| 31 | * r7 skb->data | ||
| 32 | * r8 skb_headlen(skb) | ||
| 33 | */ | ||
| 34 | |||
| 35 | #define r_scratch ARM_R0 | ||
| 36 | /* r1-r3 are (also) used for the unaligned loads on the non-ARMv7 slowpath */ | ||
| 37 | #define r_off ARM_R1 | ||
| 38 | #define r_A ARM_R4 | ||
| 39 | #define r_X ARM_R5 | ||
| 40 | #define r_skb ARM_R6 | ||
| 41 | #define r_skb_data ARM_R7 | ||
| 42 | #define r_skb_hl ARM_R8 | ||
| 43 | |||
| 44 | #define SCRATCH_SP_OFFSET 0 | ||
| 45 | #define SCRATCH_OFF(k) (SCRATCH_SP_OFFSET + (k)) | ||
| 46 | |||
| 47 | #define SEEN_MEM ((1 << BPF_MEMWORDS) - 1) | ||
| 48 | #define SEEN_MEM_WORD(k) (1 << (k)) | ||
| 49 | #define SEEN_X (1 << BPF_MEMWORDS) | ||
| 50 | #define SEEN_CALL (1 << (BPF_MEMWORDS + 1)) | ||
| 51 | #define SEEN_SKB (1 << (BPF_MEMWORDS + 2)) | ||
| 52 | #define SEEN_DATA (1 << (BPF_MEMWORDS + 3)) | ||
| 53 | |||
| 54 | #define FLAG_NEED_X_RESET (1 << 0) | ||
| 55 | |||
| 56 | struct jit_ctx { | ||
| 57 | const struct sk_filter *skf; | ||
| 58 | unsigned idx; | ||
| 59 | unsigned prologue_bytes; | ||
| 60 | int ret0_fp_idx; | ||
| 61 | u32 seen; | ||
| 62 | u32 flags; | ||
| 63 | u32 *offsets; | ||
| 64 | u32 *target; | ||
| 65 | #if __LINUX_ARM_ARCH__ < 7 | ||
| 66 | u16 epilogue_bytes; | ||
| 67 | u16 imm_count; | ||
| 68 | u32 *imms; | ||
| 69 | #endif | ||
| 70 | }; | ||
| 71 | |||
| 72 | int bpf_jit_enable __read_mostly; | ||
| 73 | |||
| 74 | static u64 jit_get_skb_b(struct sk_buff *skb, unsigned offset) | ||
| 75 | { | ||
| 76 | u8 ret; | ||
| 77 | int err; | ||
| 78 | |||
| 79 | err = skb_copy_bits(skb, offset, &ret, 1); | ||
| 80 | |||
| 81 | return (u64)err << 32 | ret; | ||
| 82 | } | ||
| 83 | |||
| 84 | static u64 jit_get_skb_h(struct sk_buff *skb, unsigned offset) | ||
| 85 | { | ||
| 86 | u16 ret; | ||
| 87 | int err; | ||
| 88 | |||
| 89 | err = skb_copy_bits(skb, offset, &ret, 2); | ||
| 90 | |||
| 91 | return (u64)err << 32 | ntohs(ret); | ||
| 92 | } | ||
| 93 | |||
| 94 | static u64 jit_get_skb_w(struct sk_buff *skb, unsigned offset) | ||
| 95 | { | ||
| 96 | u32 ret; | ||
| 97 | int err; | ||
| 98 | |||
| 99 | err = skb_copy_bits(skb, offset, &ret, 4); | ||
| 100 | |||
| 101 | return (u64)err << 32 | ntohl(ret); | ||
| 102 | } | ||
| 103 | |||
| 104 | /* | ||
| 105 | * Wrapper that handles both OABI and EABI and assures Thumb2 interworking | ||
| 106 | * (where the assembly routines like __aeabi_uidiv could cause problems). | ||
| 107 | */ | ||
| 108 | static u32 jit_udiv(u32 dividend, u32 divisor) | ||
| 109 | { | ||
| 110 | return dividend / divisor; | ||
| 111 | } | ||
| 112 | |||
| 113 | static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx) | ||
| 114 | { | ||
| 115 | if (ctx->target != NULL) | ||
| 116 | ctx->target[ctx->idx] = inst | (cond << 28); | ||
| 117 | |||
| 118 | ctx->idx++; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* | ||
| 122 | * Emit an instruction that will be executed unconditionally. | ||
| 123 | */ | ||
| 124 | static inline void emit(u32 inst, struct jit_ctx *ctx) | ||
| 125 | { | ||
| 126 | _emit(ARM_COND_AL, inst, ctx); | ||
| 127 | } | ||
| 128 | |||
| 129 | static u16 saved_regs(struct jit_ctx *ctx) | ||
| 130 | { | ||
| 131 | u16 ret = 0; | ||
| 132 | |||
| 133 | if ((ctx->skf->len > 1) || | ||
| 134 | (ctx->skf->insns[0].code == BPF_S_RET_A)) | ||
| 135 | ret |= 1 << r_A; | ||
| 136 | |||
| 137 | #ifdef CONFIG_FRAME_POINTER | ||
| 138 | ret |= (1 << ARM_FP) | (1 << ARM_IP) | (1 << ARM_LR) | (1 << ARM_PC); | ||
| 139 | #else | ||
| 140 | if (ctx->seen & SEEN_CALL) | ||
| 141 | ret |= 1 << ARM_LR; | ||
| 142 | #endif | ||
| 143 | if (ctx->seen & (SEEN_DATA | SEEN_SKB)) | ||
| 144 | ret |= 1 << r_skb; | ||
| 145 | if (ctx->seen & SEEN_DATA) | ||
| 146 | ret |= (1 << r_skb_data) | (1 << r_skb_hl); | ||
| 147 | if (ctx->seen & SEEN_X) | ||
| 148 | ret |= 1 << r_X; | ||
| 149 | |||
| 150 | return ret; | ||
| 151 | } | ||
| 152 | |||
| 153 | static inline int mem_words_used(struct jit_ctx *ctx) | ||
| 154 | { | ||
| 155 | /* yes, we do waste some stack space IF there are "holes" in the set" */ | ||
| 156 | return fls(ctx->seen & SEEN_MEM); | ||
| 157 | } | ||
| 158 | |||
| 159 | static inline bool is_load_to_a(u16 inst) | ||
| 160 | { | ||
| 161 | switch (inst) { | ||
| 162 | case BPF_S_LD_W_LEN: | ||
| 163 | case BPF_S_LD_W_ABS: | ||
| 164 | case BPF_S_LD_H_ABS: | ||
| 165 | case BPF_S_LD_B_ABS: | ||
