aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Kicinski <jakub.kicinski@netronome.com>2018-03-28 20:48:34 -0400
committerAlexei Starovoitov <ast@kernel.org>2018-03-28 22:36:13 -0400
commitdcb0c27f3c989fecae42593f470a2413434aae28 (patch)
tree660928c1a3e8585e8f47f06ff577a3fdf166d141
parente59ac634908f4ea90066e6db7dd7ae8ca02815ff (diff)
nfp: bpf: add basic support for atomic adds
Implement atomic add operation for 32 and 64 bit values. Depend on the verifier to ensure alignment. Values have to be kept in big endian and swapped upon read/write. For now only support atomic add of a constant. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com> Reviewed-by: Jiong Wang <jiong.wang@netronome.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/jit.c45
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/main.h17
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/offload.c45
-rw-r--r--drivers/net/ethernet/netronome/nfp/bpf/verifier.c105
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_asm.c1
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_asm.h2
6 files changed, 212 insertions, 3 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
index d8df56087961..c7fdb8c7ae17 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c
@@ -2127,6 +2127,49 @@ static int mem_stx8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
2127 return mem_stx(nfp_prog, meta, 8); 2127 return mem_stx(nfp_prog, meta, 8);
2128} 2128}
2129 2129
2130static int
2131mem_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, bool is64)
2132{
2133 swreg addra, addrb, off, prev_alu = imm_a(nfp_prog);
2134 u8 dst_gpr = meta->insn.dst_reg * 2;
2135 u8 src_gpr = meta->insn.src_reg * 2;
2136
2137 off = ur_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog));
2138
2139 /* If insn has an offset add to the address */
2140 if (!meta->insn.off) {
2141 addra = reg_a(dst_gpr);
2142 addrb = reg_b(dst_gpr + 1);
2143 } else {
2144 emit_alu(nfp_prog, imma_a(nfp_prog),
2145 reg_a(dst_gpr), ALU_OP_ADD, off);
2146 emit_alu(nfp_prog, imma_b(nfp_prog),
2147 reg_a(dst_gpr + 1), ALU_OP_ADD_C, reg_imm(0));
2148 addra = imma_a(nfp_prog);
2149 addrb = imma_b(nfp_prog);
2150 }
2151
2152 wrp_immed(nfp_prog, prev_alu,
2153 FIELD_PREP(CMD_OVE_DATA, 2) |
2154 CMD_OVE_LEN |
2155 FIELD_PREP(CMD_OV_LEN, 0x8 | is64 << 2));
2156 wrp_reg_or_subpart(nfp_prog, prev_alu, reg_b(src_gpr), 2, 2);
2157 emit_cmd_indir(nfp_prog, CMD_TGT_ADD_IMM, CMD_MODE_40b_BA, 0,
2158 addra, addrb, 0, false);
2159
2160 return 0;
2161}
2162
2163static int mem_xadd4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
2164{
2165 return mem_xadd(nfp_prog, meta, false);
2166}
2167
2168static int mem_xadd8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
2169{
2170 return mem_xadd(nfp_prog, meta, true);
2171}
2172
2130static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) 2173static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
2131{ 2174{
2132 emit_br(nfp_prog, BR_UNC, meta->insn.off, 0); 2175 emit_br(nfp_prog, BR_UNC, meta->insn.off, 0);
@@ -2390,6 +2433,8 @@ static const instr_cb_t instr_cb[256] = {
2390 [BPF_STX | BPF_MEM | BPF_H] = mem_stx2, 2433 [BPF_STX | BPF_MEM | BPF_H] = mem_stx2,
2391 [BPF_STX | BPF_MEM | BPF_W] = mem_stx4, 2434 [BPF_STX | BPF_MEM | BPF_W] = mem_stx4,
2392 [BPF_STX | BPF_MEM | BPF_DW] = mem_stx8, 2435 [BPF_STX | BPF_MEM | BPF_DW] = mem_stx8,
2436 [BPF_STX | BPF_XADD | BPF_W] = mem_xadd4,
2437 [BPF_STX | BPF_XADD | BPF_DW] = mem_xadd8,
2393 [BPF_ST | BPF_MEM | BPF_B] = mem_st1, 2438 [BPF_ST | BPF_MEM | BPF_B] = mem_st1,
2394 [BPF_ST | BPF_MEM | BPF_H] = mem_st2, 2439 [BPF_ST | BPF_MEM | BPF_H] = mem_st2,
2395 [BPF_ST | BPF_MEM | BPF_W] = mem_st4, 2440 [BPF_ST | BPF_MEM | BPF_W] = mem_st4,
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h
index 26bb491224b3..877be7143991 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/main.h
+++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h
@@ -72,6 +72,7 @@ enum nfp_relo_type {
72#define BR_OFF_RELO 15000 72#define BR_OFF_RELO 15000
73 73
74enum static_regs { 74enum static_regs {
75 STATIC_REG_IMMA = 20, /* Bank AB */
75 STATIC_REG_IMM = 21, /* Bank AB */ 76 STATIC_REG_IMM = 21, /* Bank AB */
76 STATIC_REG_STACK = 22, /* Bank A */ 77 STATIC_REG_STACK = 22, /* Bank A */
77 STATIC_REG_PKT_LEN = 22, /* Bank B */ 78 STATIC_REG_PKT_LEN = 22, /* Bank B */
@@ -91,6 +92,8 @@ enum pkt_vec {
91#define pptr_reg(np) pv_ctm_ptr(np) 92#define pptr_reg(np) pv_ctm_ptr(np)
92#define imm_a(np) reg_a(STATIC_REG_IMM) 93#define imm_a(np) reg_a(STATIC_REG_IMM)
93#define imm_b(np) reg_b(STATIC_REG_IMM) 94#define imm_b(np) reg_b(STATIC_REG_IMM)
95#define imma_a(np) reg_a(STATIC_REG_IMMA)
96#define imma_b(np) reg_b(STATIC_REG_IMMA)
94#define imm_both(np) reg_both(STATIC_REG_IMM) 97#define imm_both(np) reg_both(STATIC_REG_IMM)
95 98
96#define NFP_BPF_ABI_FLAGS reg_imm(0) 99#define NFP_BPF_ABI_FLAGS reg_imm(0)
@@ -169,18 +172,27 @@ struct nfp_app_bpf {
169 } helpers; 172 } helpers;
170}; 173};
171 174
175enum nfp_bpf_map_use {
176 NFP_MAP_UNUSED = 0,
177 NFP_MAP_USE_READ,
178 NFP_MAP_USE_WRITE,
179 NFP_MAP_USE_ATOMIC_CNT,
180};
181
172/** 182/**
173 * struct nfp_bpf_map - private per-map data attached to BPF maps for offload 183 * struct nfp_bpf_map - private per-map data attached to BPF maps for offload
174 * @offmap: pointer to the offloaded BPF map 184 * @offmap: pointer to the offloaded BPF map
175 * @bpf: back pointer to bpf app private structure 185 * @bpf: back pointer to bpf app private structure
176 * @tid: table id identifying map on datapath 186 * @tid: table id identifying map on datapath
177 * @l: link on the nfp_app_bpf->map_list list 187 * @l: link on the nfp_app_bpf->map_list list
188 * @use_map: map of how the value is used (in 4B chunks)
178 */ 189 */
179struct nfp_bpf_map { 190struct nfp_bpf_map {
180 struct bpf_offloaded_map *offmap; 191 struct bpf_offloaded_map *offmap;
181 struct nfp_app_bpf *bpf; 192 struct nfp_app_bpf *bpf;
182 u32 tid; 193 u32 tid;
183 struct list_head l; 194 struct list_head l;
195 enum nfp_bpf_map_use use_map[];
184}; 196};
185 197
186struct nfp_prog; 198struct nfp_prog;
@@ -320,6 +332,11 @@ static inline bool is_mbpf_classic_store_pkt(const struct nfp_insn_meta *meta)
320 return is_mbpf_classic_store(meta) && meta->ptr.type == PTR_TO_PACKET; 332 return is_mbpf_classic_store(meta) && meta->ptr.type == PTR_TO_PACKET;
321} 333}
322 334
335static inline bool is_mbpf_xadd(const struct nfp_insn_meta *meta)
336{
337 return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_XADD);
338}
339
323/** 340/**
324 * struct nfp_prog - nfp BPF program 341 * struct nfp_prog - nfp BPF program
325 * @bpf: backpointer to the bpf app priv structure 342 * @bpf: backpointer to the bpf app priv structure
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
index 0a7732385469..42d98792bd25 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c
@@ -164,6 +164,41 @@ static int nfp_bpf_destroy(struct nfp_net *nn, struct bpf_prog *prog)
164 return 0; 164 return 0;
165} 165}
166 166
167/* Atomic engine requires values to be in big endian, we need to byte swap
168 * the value words used with xadd.
169 */
170static void nfp_map_bpf_byte_swap(struct nfp_bpf_map *nfp_map, void *value)
171{
172 u32 *word = value;
173 unsigned int i;
174
175 for (i = 0; i < DIV_ROUND_UP(nfp_map->offmap->map.value_size, 4); i++)
176 if (nfp_map->use_map[i] == NFP_MAP_USE_ATOMIC_CNT)
177 word[i] = (__force u32)cpu_to_be32(word[i]);
178}
179
180static int
181nfp_bpf_map_lookup_entry(struct bpf_offloaded_map *offmap,
182 void *key, void *value)
183{
184 int err;
185
186 err = nfp_bpf_ctrl_lookup_entry(offmap, key, value);
187 if (err)
188 return err;
189
190 nfp_map_bpf_byte_swap(offmap->dev_priv, value);
191 return 0;
192}
193
194static int
195nfp_bpf_map_update_entry(struct bpf_offloaded_map *offmap,
196 void *key, void *value, u64 flags)
197{
198 nfp_map_bpf_byte_swap(offmap->dev_priv, value);
199 return nfp_bpf_ctrl_update_entry(offmap, key, value, flags);
200}
201
167static int 202static int
168nfp_bpf_map_get_next_key(struct bpf_offloaded_map *offmap, 203nfp_bpf_map_get_next_key(struct bpf_offloaded_map *offmap,
169 void *key, void *next_key) 204 void *key, void *next_key)
@@ -183,8 +218,8 @@ nfp_bpf_map_delete_elem(struct bpf_offloaded_map *offmap, void *key)
183 218
184static const struct bpf_map_dev_ops nfp_bpf_map_ops = { 219static const struct bpf_map_dev_ops nfp_bpf_map_ops = {
185 .map_get_next_key = nfp_bpf_map_get_next_key, 220 .map_get_next_key = nfp_bpf_map_get_next_key,
186 .map_lookup_elem = nfp_bpf_ctrl_lookup_entry, 221 .map_lookup_elem = nfp_bpf_map_lookup_entry,
187 .map_update_elem = nfp_bpf_ctrl_update_entry, 222 .map_update_elem = nfp_bpf_map_update_entry,
188 .map_delete_elem = nfp_bpf_map_delete_elem, 223 .map_delete_elem = nfp_bpf_map_delete_elem,
189}; 224};
190 225
@@ -192,6 +227,7 @@ static int
192nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap) 227nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap)
193{ 228{
194 struct nfp_bpf_map *nfp_map; 229 struct nfp_bpf_map *nfp_map;
230 unsigned int use_map_size;
195 long long int res; 231 long long int res;
196 232
197 if (!bpf->maps.types) 233 if (!bpf->maps.types)
@@ -226,7 +262,10 @@ nfp_bpf_map_alloc(struct nfp_app_bpf *bpf, struct bpf_offloaded_map *offmap)
226 return -ENOMEM; 262 return -ENOMEM;
227 } 263 }
228 264
229 nfp_map = kzalloc(sizeof(*nfp_map), GFP_USER); 265 use_map_size = DIV_ROUND_UP(offmap->map.value_size, 4) *
266 FIELD_SIZEOF(struct nfp_bpf_map, use_map[0]);
267
268 nfp_map = kzalloc(sizeof(*nfp_map) + use_map_size, GFP_USER);
230 if (!nfp_map) 269 if (!nfp_map)
231 return -ENOMEM; 270 return -ENOMEM;
232 271
diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
index 7d67ffc897dd..40619efea77d 100644
--- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c
@@ -285,6 +285,72 @@ nfp_bpf_check_stack_access(struct nfp_prog *nfp_prog,
285 return -EINVAL; 285 return -EINVAL;
286} 286}
287 287
288static const char *nfp_bpf_map_use_name(enum nfp_bpf_map_use use)
289{
290 static const char * const names[] = {
291 [NFP_MAP_UNUSED] = "unused",
292 [NFP_MAP_USE_READ] = "read",
293 [NFP_MAP_USE_WRITE] = "write",
294 [NFP_MAP_USE_ATOMIC_CNT] = "atomic",
295 };
296
297 if (use >= ARRAY_SIZE(names) || !names[use])
298 return "unknown";
299 return names[use];
300}
301
302static int
303nfp_bpf_map_mark_used_one(struct bpf_verifier_env *env,
304 struct nfp_bpf_map *nfp_map,
305 unsigned int off, enum nfp_bpf_map_use use)
306{
307 if (nfp_map->use_map[off / 4] != NFP_MAP_UNUSED &&
308 nfp_map->use_map[off / 4] != use) {
309 pr_vlog(env, "map value use type conflict %s vs %s off: %u\n",
310 nfp_bpf_map_use_name(nfp_map->use_map[off / 4]),
311 nfp_bpf_map_use_name(use), off);
312 return -EOPNOTSUPP;
313 }
314
315 nfp_map->use_map[off / 4] = use;
316
317 return 0;
318}
319
320static int
321nfp_bpf_map_mark_used(struct bpf_verifier_env *env, struct nfp_insn_meta *meta,
322 const struct bpf_reg_state *reg,
323 enum nfp_bpf_map_use use)
324{
325 struct bpf_offloaded_map *offmap;
326 struct nfp_bpf_map *nfp_map;
327 unsigned int size, off;
328 int i, err;
329
330 if (!tnum_is_const(reg->var_off)) {
331 pr_vlog(env, "map value offset is variable\n");
332 return -EOPNOTSUPP;
333 }
334
335 off = reg->var_off.value + meta->insn.off + reg->off;
336 size = BPF_LDST_BYTES(&meta->insn);
337 offmap = map_to_offmap(reg->map_ptr);
338 nfp_map = offmap->dev_priv;
339
340 if (off + size > offmap->map.value_size) {
341 pr_vlog(env, "map value access out-of-bounds\n");
342 return -EINVAL;
343 }
344
345 for (i = 0; i < size; i += 4 - (off + i) % 4) {
346 err = nfp_bpf_map_mark_used_one(env, nfp_map, off + i, use);
347 if (err)
348 return err;
349 }
350
351 return 0;
352}
353
288static int 354static int
289nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, 355nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
290 struct bpf_verifier_env *env, u8 reg_no) 356 struct bpf_verifier_env *env, u8 reg_no)
@@ -307,10 +373,22 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
307 } 373 }
308 374
309 if (reg->type == PTR_TO_MAP_VALUE) { 375 if (reg->type == PTR_TO_MAP_VALUE) {
376 if (is_mbpf_load(meta)) {
377 err = nfp_bpf_map_mark_used(env, meta, reg,
378 NFP_MAP_USE_READ);
379 if (err)
380 return err;
381 }
310 if (is_mbpf_store(meta)) { 382 if (is_mbpf_store(meta)) {
311 pr_vlog(env, "map writes not supported\n"); 383 pr_vlog(env, "map writes not supported\n");
312 return -EOPNOTSUPP; 384 return -EOPNOTSUPP;
313 } 385 }
386 if (is_mbpf_xadd(meta)) {
387 err = nfp_bpf_map_mark_used(env, meta, reg,
388 NFP_MAP_USE_ATOMIC_CNT);
389 if (err)
390 return err;
391 }
314 } 392 }
315 393
316 if (meta->ptr.type != NOT_INIT && meta->ptr.type != reg->type) { 394 if (meta->ptr.type != NOT_INIT && meta->ptr.type != reg->type) {
@@ -325,6 +403,31 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
325} 403}
326 404
327static int 405static int
406nfp_bpf_check_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
407 struct bpf_verifier_env *env)
408{
409 const struct bpf_reg_state *sreg = cur_regs(env) + meta->insn.src_reg;
410 const struct bpf_reg_state *dreg = cur_regs(env) + meta->insn.dst_reg;
411
412 if (dreg->type != PTR_TO_MAP_VALUE) {
413 pr_vlog(env, "atomic add not to a map value pointer: %d\n",
414 dreg->type);
415 return -EOPNOTSUPP;
416 }
417 if (sreg->type != SCALAR_VALUE ||
418 sreg->var_off.value > 0xffff || sreg->var_off.mask > 0xffff) {
419 char tn_buf[48];
420
421 tnum_strn(tn_buf, sizeof(tn_buf), sreg->var_off);
422 pr_vlog(env, "atomic add not of a small constant scalar: %s\n",
423 tn_buf);
424 return -EOPNOTSUPP;
425 }
426
427 return nfp_bpf_check_ptr(nfp_prog, meta, env, meta->insn.dst_reg);
428}
429
430static int
328nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) 431nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
329{ 432{
330 struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv; 433 struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv;
@@ -356,6 +459,8 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
356 if (is_mbpf_store(meta)) 459 if (is_mbpf_store(meta))
357 return nfp_bpf_check_ptr(nfp_prog, meta, env, 460 return nfp_bpf_check_ptr(nfp_prog, meta, env,
358 meta->insn.dst_reg); 461 meta->insn.dst_reg);
462 if (is_mbpf_xadd(meta))
463 return nfp_bpf_check_xadd(nfp_prog, meta, env);
359 464
360 return 0; 465 return 0;
361} 466}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.c b/drivers/net/ethernet/netronome/nfp/nfp_asm.c
index 1e597600c693..3c0107ac9a2c 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.c
@@ -48,6 +48,7 @@ const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
48 [CMD_TGT_READ32_SWAP] = { 0x02, 0x5c }, 48 [CMD_TGT_READ32_SWAP] = { 0x02, 0x5c },
49 [CMD_TGT_READ_LE] = { 0x01, 0x40 }, 49 [CMD_TGT_READ_LE] = { 0x01, 0x40 },
50 [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 }, 50 [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 },
51 [CMD_TGT_ADD_IMM] = { 0x02, 0x47 },
51}; 52};
52 53
53static bool unreg_is_imm(u16 reg) 54static bool unreg_is_imm(u16 reg)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.h b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
index 150d28f9cd52..185192590a17 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_asm.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.h
@@ -238,6 +238,7 @@ enum cmd_tgt_map {
238 CMD_TGT_READ32_SWAP, 238 CMD_TGT_READ32_SWAP,
239 CMD_TGT_READ_LE, 239 CMD_TGT_READ_LE,
240 CMD_TGT_READ_SWAP_LE, 240 CMD_TGT_READ_SWAP_LE,
241 CMD_TGT_ADD_IMM,
241 __CMD_TGT_MAP_SIZE, 242 __CMD_TGT_MAP_SIZE,
242}; 243};
243 244
@@ -254,6 +255,7 @@ enum cmd_ctx_swap {
254 CMD_CTX_NO_SWAP = 3, 255 CMD_CTX_NO_SWAP = 3,
255}; 256};
256 257
258#define CMD_OVE_DATA GENMASK(5, 3)
257#define CMD_OVE_LEN BIT(7) 259#define CMD_OVE_LEN BIT(7)
258#define CMD_OV_LEN GENMASK(12, 8) 260#define CMD_OV_LEN GENMASK(12, 8)
259 261