Commit 31070e7f authored by David S. Miller's avatar David S. Miller

Merge branch 'nfp-bpf-ABIv2-and-multi-port'

Jakub Kicinski says:

====================
nfp: bpf ABIv2 and multi port

This series migrates our eBPF offload from old PoC firmware to
a redesigned, faster and more feature rich FW.  Marking support
is dropped for now.  We have to teach the JIT about encoding
local memory accesses (one of NFP memory types).  There is also
code to populate the ECC of instructions (PoC had ECC protection
on instruction store disabled).  There is also a minor ld_field
fix and all 64 bit shifts can now be encoded.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1c3dc891 2de1be1d
......@@ -14,6 +14,7 @@ nfp-objs := \
nfpcore/nfp_resource.o \
nfpcore/nfp_rtsym.o \
nfpcore/nfp_target.o \
nfp_asm.o \
nfp_app.o \
nfp_app_nic.o \
nfp_devlink.o \
......
......@@ -89,14 +89,6 @@ nfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
struct nfp_net_bpf_priv *priv;
int ret;
/* Limit to single port, otherwise it's just a NIC */
if (id > 0) {
nfp_warn(app->cpp,
"BPF NIC doesn't support more than one port right now\n");
nn->port = nfp_port_alloc(app, NFP_PORT_INVALID, nn->dp.netdev);
return PTR_ERR_OR_ZERO(nn->port);
}
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
......
......@@ -39,6 +39,7 @@
#include <linux/list.h>
#include <linux/types.h>
#include "../nfp_asm.h"
#include "../nfp_net.h"
/* For branch fixup logic use up-most byte of branch instruction as scratch
......@@ -53,9 +54,13 @@ enum br_special {
};
enum static_regs {
STATIC_REG_PKT = 1,
#define REG_PKT_BANK ALU_DST_A
STATIC_REG_IMM = 2, /* Bank AB */
STATIC_REG_IMM = 21, /* Bank AB */
STATIC_REG_PKT_LEN = 22, /* Bank B */
};
enum pkt_vec {
PKT_VEC_PKT_LEN = 0,
PKT_VEC_PKT_PTR = 2,
};
enum nfp_bpf_action_type {
......@@ -65,39 +70,17 @@ enum nfp_bpf_action_type {
NN_ACT_XDP,
};
/* Software register representation, hardware encoding in asm.h */
#define NN_REG_TYPE GENMASK(31, 24)
#define NN_REG_VAL GENMASK(7, 0)
enum nfp_bpf_reg_type {
NN_REG_GPR_A = BIT(0),
NN_REG_GPR_B = BIT(1),
NN_REG_NNR = BIT(2),
NN_REG_XFER = BIT(3),
NN_REG_IMM = BIT(4),
NN_REG_NONE = BIT(5),
};
#define NN_REG_GPR_BOTH (NN_REG_GPR_A | NN_REG_GPR_B)
#define reg_both(x) ((x) | FIELD_PREP(NN_REG_TYPE, NN_REG_GPR_BOTH))
#define reg_a(x) ((x) | FIELD_PREP(NN_REG_TYPE, NN_REG_GPR_A))
#define reg_b(x) ((x) | FIELD_PREP(NN_REG_TYPE, NN_REG_GPR_B))
#define reg_nnr(x) ((x) | FIELD_PREP(NN_REG_TYPE, NN_REG_NNR))
#define reg_xfer(x) ((x) | FIELD_PREP(NN_REG_TYPE, NN_REG_XFER))
#define reg_imm(x) ((x) | FIELD_PREP(NN_REG_TYPE, NN_REG_IMM))
#define reg_none() (FIELD_PREP(NN_REG_TYPE, NN_REG_NONE))
#define pv_len(np) reg_lm(1, PKT_VEC_PKT_LEN)
#define pv_ctm_ptr(np) reg_lm(1, PKT_VEC_PKT_PTR)
#define pkt_reg(np) reg_a((np)->regs_per_thread - STATIC_REG_PKT)
#define imm_a(np) reg_a((np)->regs_per_thread - STATIC_REG_IMM)
#define imm_b(np) reg_b((np)->regs_per_thread - STATIC_REG_IMM)
#define imm_both(np) reg_both((np)->regs_per_thread - STATIC_REG_IMM)
#define plen_reg(np) reg_b(STATIC_REG_PKT_LEN)
#define pptr_reg(np) pv_ctm_ptr(np)
#define imm_a(np) reg_a(STATIC_REG_IMM)
#define imm_b(np) reg_b(STATIC_REG_IMM)
#define imm_both(np) reg_both(STATIC_REG_IMM)
#define NFP_BPF_ABI_FLAGS reg_nnr(0)
#define NFP_BPF_ABI_FLAGS reg_imm(0)
#define NFP_BPF_ABI_FLAG_MARK 1
#define NFP_BPF_ABI_MARK reg_nnr(1)
#define NFP_BPF_ABI_PKT reg_nnr(2)
#define NFP_BPF_ABI_LEN reg_nnr(3)
struct nfp_prog;
struct nfp_insn_meta;
......
......@@ -36,6 +36,8 @@
#include <net/devlink.h>
#include <trace/events/devlink.h>
#include "nfp_net_repr.h"
struct bpf_prog;
......@@ -271,11 +273,17 @@ static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn,
static inline bool nfp_app_ctrl_tx(struct nfp_app *app, struct sk_buff *skb)
{
trace_devlink_hwmsg(priv_to_devlink(app->pf), false, 0,
skb->data, skb->len);
return nfp_ctrl_tx(app->ctrl, skb);
}
static inline void nfp_app_ctrl_rx(struct nfp_app *app, struct sk_buff *skb)
{
trace_devlink_hwmsg(priv_to_devlink(app->pf), true, 0,
skb->data, skb->len);
app->type->ctrl_msg_rx(app, skb);
}
......
/*
* Copyright (C) 2016-2017 Netronome Systems, Inc.
*
* This software is dual licensed under the GNU General License Version 2,
* June 1991 as shown in the file COPYING in the top-level directory of this
* source tree or the BSD 2-Clause License provided below. You have the
* option to license this software under the complete terms of either license.
*
* The BSD 2-Clause License:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
#include "nfp_asm.h"
const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
[CMD_TGT_WRITE8] = { 0x00, 0x42 },
[CMD_TGT_READ8] = { 0x01, 0x43 },
[CMD_TGT_READ_LE] = { 0x01, 0x40 },
[CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 },
};
static u16 nfp_swreg_to_unreg(swreg reg, bool is_dst)
{
bool lm_id, lm_dec = false;
u16 val = swreg_value(reg);
switch (swreg_type(reg)) {
case NN_REG_GPR_A:
case NN_REG_GPR_B:
case NN_REG_GPR_BOTH:
return val;
case NN_REG_NNR:
return UR_REG_NN | val;
case NN_REG_XFER:
return UR_REG_XFR | val;
case NN_REG_LMEM:
lm_id = swreg_lm_idx(reg);
switch (swreg_lm_mode(reg)) {
case NN_LM_MOD_NONE:
if (val & ~UR_REG_LM_IDX_MAX) {
pr_err("LM offset too large\n");
return 0;
}
return UR_REG_LM | FIELD_PREP(UR_REG_LM_IDX, lm_id) |
val;
case NN_LM_MOD_DEC:
lm_dec = true;
/* fall through */
case NN_LM_MOD_INC:
if (val) {
pr_err("LM offset in inc/dev mode\n");
return 0;
}
return UR_REG_LM | UR_REG_LM_POST_MOD |
FIELD_PREP(UR_REG_LM_IDX, lm_id) |
FIELD_PREP(UR_REG_LM_POST_MOD_DEC, lm_dec);
default:
pr_err("bad LM mode for unrestricted operands %d\n",
swreg_lm_mode(reg));
return 0;
}
case NN_REG_IMM:
if (val & ~0xff) {
pr_err("immediate too large\n");
return 0;
}
return UR_REG_IMM_encode(val);
case NN_REG_NONE:
return is_dst ? UR_REG_NO_DST : REG_NONE;
}
pr_err("unrecognized reg encoding %08x\n", reg);
return 0;
}
int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg,
struct nfp_insn_ur_regs *reg)
{
memset(reg, 0, sizeof(*reg));
/* Decode destination */
if (swreg_type(dst) == NN_REG_IMM)
return -EFAULT;
if (swreg_type(dst) == NN_REG_GPR_B)
reg->dst_ab = ALU_DST_B;
if (swreg_type(dst) == NN_REG_GPR_BOTH)
reg->wr_both = true;
reg->dst = nfp_swreg_to_unreg(dst, true);
/* Decode source operands */
if (swreg_type(lreg) == swreg_type(rreg))
return -EFAULT;
if (swreg_type(lreg) == NN_REG_GPR_B ||
swreg_type(rreg) == NN_REG_GPR_A) {
reg->areg = nfp_swreg_to_unreg(rreg, false);
reg->breg = nfp_swreg_to_unreg(lreg, false);
reg->swap = true;
} else {
reg->areg = nfp_swreg_to_unreg(lreg, false);
reg->breg = nfp_swreg_to_unreg(rreg, false);
}
reg->dst_lmextn = swreg_lmextn(dst);
reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg);
return 0;
}
static u16 nfp_swreg_to_rereg(swreg reg, bool is_dst, bool has_imm8, bool *i8)
{
u16 val = swreg_value(reg);
bool lm_id;
switch (swreg_type(reg)) {
case NN_REG_GPR_A:
case NN_REG_GPR_B:
case NN_REG_GPR_BOTH:
return val;
case NN_REG_XFER:
return RE_REG_XFR | val;
case NN_REG_LMEM:
lm_id = swreg_lm_idx(reg);
if (swreg_lm_mode(reg) != NN_LM_MOD_NONE) {
pr_err("bad LM mode for restricted operands %d\n",
swreg_lm_mode(reg));
return 0;
}
if (val & ~RE_REG_LM_IDX_MAX) {
pr_err("LM offset too large\n");
return 0;
}
return RE_REG_LM | FIELD_PREP(RE_REG_LM_IDX, lm_id) | val;
case NN_REG_IMM:
if (val & ~(0x7f | has_imm8 << 7)) {
pr_err("immediate too large\n");
return 0;
}
*i8 = val & 0x80;
return RE_REG_IMM_encode(val & 0x7f);
case NN_REG_NONE:
return is_dst ? RE_REG_NO_DST : REG_NONE;
case NN_REG_NNR:
pr_err("NNRs used with restricted encoding\n");
return 0;
}
pr_err("unrecognized reg encoding\n");
return 0;
}
int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,
struct nfp_insn_re_regs *reg, bool has_imm8)
{
memset(reg, 0, sizeof(*reg));
/* Decode destination */
if (swreg_type(dst) == NN_REG_IMM)
return -EFAULT;
if (swreg_type(dst) == NN_REG_GPR_B)
reg->dst_ab = ALU_DST_B;
if (swreg_type(dst) == NN_REG_GPR_BOTH)
reg->wr_both = true;
reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL);
/* Decode source operands */
if (swreg_type(lreg) == swreg_type(rreg))
return -EFAULT;
if (swreg_type(lreg) == NN_REG_GPR_B ||
swreg_type(rreg) == NN_REG_GPR_A) {
reg->areg = nfp_swreg_to_rereg(rreg, false, has_imm8, &reg->i8);
reg->breg = nfp_swreg_to_rereg(lreg, false, has_imm8, &reg->i8);
reg->swap = true;
} else {
reg->areg = nfp_swreg_to_rereg(lreg, false, has_imm8, &reg->i8);
reg->breg = nfp_swreg_to_rereg(rreg, false, has_imm8, &reg->i8);
}
reg->dst_lmextn = swreg_lmextn(dst);
reg->src_lmextn = swreg_lmextn(lreg) | swreg_lmextn(rreg);
return 0;
}
#define NFP_USTORE_ECC_POLY_WORDS 7
#define NFP_USTORE_OP_BITS 45
static const u64 nfp_ustore_ecc_polynomials[NFP_USTORE_ECC_POLY_WORDS] = {
0x0ff800007fffULL,
0x11f801ff801fULL,
0x1e387e0781e1ULL,
0x17cb8e388e22ULL,
0x1af5b2c93244ULL,
0x1f56d5525488ULL,
0x0daf69a46910ULL,
};
static bool parity(u64 value)
{
return hweight64(value) & 1;
}
int nfp_ustore_check_valid_no_ecc(u64 insn)
{
if (insn & ~GENMASK_ULL(NFP_USTORE_OP_BITS, 0))
return -EINVAL;
return 0;
}
u64 nfp_ustore_calc_ecc_insn(u64 insn)
{
u8 ecc = 0;
int i;
for (i = 0; i < NFP_USTORE_ECC_POLY_WORDS; i++)
ecc |= parity(nfp_ustore_ecc_polynomials[i] & insn) << i;
return insn | (u64)ecc << NFP_USTORE_OP_BITS;
}
......@@ -34,6 +34,7 @@
#ifndef __NFP_ASM_H__
#define __NFP_ASM_H__ 1
#include <linux/bitfield.h>
#include <linux/types.h>
#define REG_NONE 0
......@@ -43,9 +44,17 @@
#define RE_REG_IMM_encode(x) \
(RE_REG_IMM | ((x) & 0x1f) | (((x) & 0x60) << 1))
#define RE_REG_IMM_MAX 0x07fULL
#define RE_REG_LM 0x050
#define RE_REG_LM_IDX 0x008
#define RE_REG_LM_IDX_MAX 0x7
#define RE_REG_XFR 0x080
#define UR_REG_XFR 0x180
#define UR_REG_LM 0x200
#define UR_REG_LM_IDX 0x020
#define UR_REG_LM_POST_MOD 0x010
#define UR_REG_LM_POST_MOD_DEC 0x001
#define UR_REG_LM_IDX_MAX 0xf
#define UR_REG_NN 0x280
#define UR_REG_NO_DST 0x300
#define UR_REG_IMM UR_REG_NO_DST
......@@ -91,6 +100,7 @@ enum br_ctx_signal_state {
#define OP_BB_DEFBR 0x00000300000ULL
#define OP_BB_ADDR_LO 0x007ffc00000ULL
#define OP_BB_ADDR_HI 0x10000000000ULL
#define OP_BB_SRC_LMEXTN 0x40000000000ULL
#define OP_BALU_BASE 0x0e800000000ULL
#define OP_BA_A_SRC 0x000000003ffULL
......@@ -106,6 +116,8 @@ enum br_ctx_signal_state {
#define OP_IMMED_SHIFT 0x00600000000ULL
#define OP_IMMED_BASE 0x0f000000000ULL
#define OP_IMMED_WR_AB 0x20000000000ULL
#define OP_IMMED_SRC_LMEXTN 0x40000000000ULL
#define OP_IMMED_DST_LMEXTN 0x80000000000ULL
enum immed_width {
IMMED_WIDTH_ALL = 0,
......@@ -130,6 +142,8 @@ enum immed_shift {
#define OP_SHF_OP 0x00e00000000ULL
#define OP_SHF_DST_AB 0x01000000000ULL
#define OP_SHF_WR_AB 0x20000000000ULL
#define OP_SHF_SRC_LMEXTN 0x40000000000ULL
#define OP_SHF_DST_LMEXTN 0x80000000000ULL
enum shf_op {
SHF_OP_NONE = 0,
......@@ -152,6 +166,8 @@ enum shf_sc {
#define OP_ALU_DST_AB 0x01000000000ULL
#define OP_ALU_BASE 0x0a000000000ULL
#define OP_ALU_WR_AB 0x20000000000ULL
#define OP_ALU_SRC_LMEXTN 0x40000000000ULL
#define OP_ALU_DST_LMEXTN 0x80000000000ULL
enum alu_op {
ALU_OP_NONE = 0x00,
......@@ -180,6 +196,8 @@ enum alu_dst_ab {
#define OP_LDF_BMASK 0x0000f000000ULL
#define OP_LDF_SHF 0x001f0000000ULL
#define OP_LDF_WR_AB 0x20000000000ULL
#define OP_LDF_SRC_LMEXTN 0x40000000000ULL
#define OP_LDF_DST_LMEXTN 0x80000000000ULL
#define OP_CMD_A_SRC 0x000000000ffULL
#define OP_CMD_CTX 0x00000000300ULL
......@@ -204,6 +222,8 @@ enum cmd_tgt_map {
__CMD_TGT_MAP_SIZE,
};
extern const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE];
enum cmd_mode {
CMD_MODE_40b_AB = 0,
CMD_MODE_40b_BA = 1,
......@@ -220,6 +240,8 @@ enum cmd_ctx_swap {
#define OP_LCSR_B_SRC 0x000000ffc00ULL
#define OP_LCSR_WRITE 0x00000200000ULL
#define OP_LCSR_ADDR 0x001ffc00000ULL
#define OP_LCSR_SRC_LMEXTN 0x40000000000ULL
#define OP_LCSR_DST_LMEXTN 0x80000000000ULL
enum lcsr_wr_src {
LCSR_WR_AREG,
......@@ -230,4 +252,119 @@ enum lcsr_wr_src {
#define OP_CARB_BASE 0x0e000000000ULL
#define OP_CARB_OR 0x00000010000ULL
/* Software register representation, independent of operand type */
#define NN_REG_TYPE GENMASK(31, 24)
#define NN_REG_LM_IDX GENMASK(23, 22)
#define NN_REG_LM_IDX_HI BIT(23)
#define NN_REG_LM_IDX_LO BIT(22)
#define NN_REG_LM_MOD GENMASK(21, 20)
#define NN_REG_VAL GENMASK(7, 0)
enum nfp_bpf_reg_type {
NN_REG_GPR_A = BIT(0),
NN_REG_GPR_B = BIT(1),
NN_REG_GPR_BOTH = NN_REG_GPR_A | NN_REG_GPR_B,
NN_REG_NNR = BIT(2),
NN_REG_XFER = BIT(3),
NN_REG_IMM = BIT(4),
NN_REG_NONE = BIT(5),
NN_REG_LMEM = BIT(6),
};
enum nfp_bpf_lm_mode {
NN_LM_MOD_NONE = 0,
NN_LM_MOD_INC,
NN_LM_MOD_DEC,
};
#define reg_both(x) __enc_swreg((x), NN_REG_GPR_BOTH)
#define reg_a(x) __enc_swreg((x), NN_REG_GPR_A)
#define reg_b(x) __enc_swreg((x), NN_REG_GPR_B)
#define reg_nnr(x) __enc_swreg((x), NN_REG_NNR)
#define reg_xfer(x) __enc_swreg((x), NN_REG_XFER)
#define reg_imm(x) __enc_swreg((x), NN_REG_IMM)
#define reg_none() __enc_swreg(0, NN_REG_NONE)
#define reg_lm(x, off) __enc_swreg_lm((x), NN_LM_MOD_NONE, (off))
#define reg_lm_inc(x) __enc_swreg_lm((x), NN_LM_MOD_INC, 0)
#define reg_lm_dec(x) __enc_swreg_lm((x), NN_LM_MOD_DEC, 0)
#define __reg_lm(x, mod, off) __enc_swreg_lm((x), (mod), (off))
typedef __u32 __bitwise swreg;
static inline swreg __enc_swreg(u16 id, u8 type)
{
return (__force swreg)(id | FIELD_PREP(NN_REG_TYPE, type));
}
static inline swreg __enc_swreg_lm(u8 id, enum nfp_bpf_lm_mode mode, u8 off)
{
WARN_ON(id > 3 || (off && mode != NN_LM_MOD_NONE));
return (__force swreg)(FIELD_PREP(NN_REG_TYPE, NN_REG_LMEM) |
FIELD_PREP(NN_REG_LM_IDX, id) |
FIELD_PREP(NN_REG_LM_MOD, mode) |
off);
}
static inline u32 swreg_raw(swreg reg)
{
return (__force u32)reg;
}
static inline enum nfp_bpf_reg_type swreg_type(swreg reg)
{
return FIELD_GET(NN_REG_TYPE, swreg_raw(reg));
}
static inline u16 swreg_value(swreg reg)
{
return FIELD_GET(NN_REG_VAL, swreg_raw(reg));
}
static inline bool swreg_lm_idx(swreg reg)
{
return FIELD_GET(NN_REG_LM_IDX_LO, swreg_raw(reg));
}
static inline bool swreg_lmextn(swreg reg)
{
return FIELD_GET(NN_REG_LM_IDX_HI, swreg_raw(reg));
}
static inline enum nfp_bpf_lm_mode swreg_lm_mode(swreg reg)
{
return FIELD_GET(NN_REG_LM_MOD, swreg_raw(reg));
}
struct nfp_insn_ur_regs {
enum alu_dst_ab dst_ab;
u16 dst;
u16 areg, breg;
bool swap;
bool wr_both;
bool dst_lmextn;
bool src_lmextn;
};
struct nfp_insn_re_regs {
enum alu_dst_ab dst_ab;
u8 dst;
u8 areg, breg;
bool swap;
bool wr_both;
bool i8;
bool dst_lmextn;
bool src_lmextn;
};
int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg,
struct nfp_insn_ur_regs *reg);
int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg,
struct nfp_insn_re_regs *reg, bool has_imm8);
#define NFP_USTORE_PREFETCH_WINDOW 8
int nfp_ustore_check_valid_no_ecc(u64 insn);
u64 nfp_ustore_calc_ecc_insn(u64 insn);
#endif
......@@ -255,7 +255,7 @@
* @NFP_NET_CFG_BPF_ADDR: DMA address of the buffer with JITed BPF code
*/
#define NFP_NET_CFG_BPF_ABI 0x0080
#define NFP_NET_BPF_ABI 1
#define NFP_NET_BPF_ABI 2
#define NFP_NET_CFG_BPF_CAP 0x0081
#define NFP_NET_BPF_CAP_RELO (1 << 0) /* seamless reload */
#define NFP_NET_CFG_BPF_MAX_LEN 0x0082
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment