Commit 8bc46548 authored by David S. Miller's avatar David S. Miller

Merge branch 'nfp-bpf-support-direct-packet-access'

Jakub Kicinski says:

====================
nfp: bpf: support direct packet access

The core of this series is direct packet access support.  With a
small change to the verifier, the offloaded code can now make
use of DPA.  We need to be careful to use kernel (after initial
translation) offsets in our JIT.  Direct packet access also brings
us to the problem of eBPF endianness.  After considering the
changes necessary we decided to not support translation on both
BE and LE hosts, for now.

This series contains two fixes - one for compare instructions and
one for ineffective jne optimization.  I chose to include fixes
in this set because the code in -net works only with unreleased
PoC FW (ABI version 1) and therefore nobody outside of Netronome
can exercise it anyway.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 40d0af56 bfddbc8a
...@@ -42,9 +42,11 @@ ...@@ -42,9 +42,11 @@
static bool nfp_net_ebpf_capable(struct nfp_net *nn) static bool nfp_net_ebpf_capable(struct nfp_net *nn)
{ {
#ifdef __LITTLE_ENDIAN
if (nn->cap & NFP_NET_CFG_CTRL_BPF && if (nn->cap & NFP_NET_CFG_CTRL_BPF &&
nn_readb(nn, NFP_NET_CFG_BPF_ABI) == NFP_NET_BPF_ABI) nn_readb(nn, NFP_NET_CFG_BPF_ABI) == NFP_NET_BPF_ABI)
return true; return true;
#endif
return false; return false;
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/bpf_verifier.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -96,6 +97,7 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *); ...@@ -96,6 +97,7 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
/** /**
* struct nfp_insn_meta - BPF instruction wrapper * struct nfp_insn_meta - BPF instruction wrapper
* @insn: BPF instruction * @insn: BPF instruction
* @ptr: pointer type for memory operations
* @off: index of first generated machine instruction (in nfp_prog.prog) * @off: index of first generated machine instruction (in nfp_prog.prog)
* @n: eBPF instruction number * @n: eBPF instruction number
* @skip: skip this instruction (optimized out) * @skip: skip this instruction (optimized out)
...@@ -104,6 +106,7 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *); ...@@ -104,6 +106,7 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *);
*/ */
struct nfp_insn_meta { struct nfp_insn_meta {
struct bpf_insn insn; struct bpf_insn insn;
struct bpf_reg_state ptr;
unsigned int off; unsigned int off;
unsigned short n; unsigned short n;
bool skip; bool skip;
......
...@@ -112,12 +112,19 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog, ...@@ -112,12 +112,19 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog,
} }
static int static int
nfp_bpf_check_ctx_ptr(struct nfp_prog *nfp_prog, nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
const struct bpf_verifier_env *env, u8 reg) const struct bpf_verifier_env *env, u8 reg)
{ {
if (env->cur_state.regs[reg].type != PTR_TO_CTX) if (env->cur_state.regs[reg].type != PTR_TO_CTX &&
env->cur_state.regs[reg].type != PTR_TO_PACKET)
return -EINVAL; return -EINVAL;
if (meta->ptr.type != NOT_INIT &&
meta->ptr.type != env->cur_state.regs[reg].type)
return -EINVAL;
meta->ptr = env->cur_state.regs[reg];
return 0; return 0;
} }
...@@ -145,10 +152,10 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) ...@@ -145,10 +152,10 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
return nfp_bpf_check_exit(priv->prog, env); return nfp_bpf_check_exit(priv->prog, env);
if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM)) if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM))
return nfp_bpf_check_ctx_ptr(priv->prog, env, return nfp_bpf_check_ptr(priv->prog, meta, env,
meta->insn.src_reg); meta->insn.src_reg);
if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM)) if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM))
return nfp_bpf_check_ctx_ptr(priv->prog, env, return nfp_bpf_check_ptr(priv->prog, meta, env,
meta->insn.dst_reg); meta->insn.dst_reg);
return 0; return 0;
......
...@@ -40,8 +40,11 @@ ...@@ -40,8 +40,11 @@
#include "nfp_asm.h" #include "nfp_asm.h"
const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = { const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = {
[CMD_TGT_WRITE8] = { 0x00, 0x42 }, [CMD_TGT_WRITE8_SWAP] = { 0x02, 0x42 },
[CMD_TGT_READ8] = { 0x01, 0x43 }, [CMD_TGT_READ8] = { 0x01, 0x43 },
[CMD_TGT_READ32] = { 0x00, 0x5c },
[CMD_TGT_READ32_LE] = { 0x01, 0x5c },
[CMD_TGT_READ32_SWAP] = { 0x02, 0x5c },
[CMD_TGT_READ_LE] = { 0x01, 0x40 }, [CMD_TGT_READ_LE] = { 0x01, 0x40 },
[CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 }, [CMD_TGT_READ_SWAP_LE] = { 0x03, 0x40 },
}; };
......
...@@ -153,6 +153,7 @@ enum shf_op { ...@@ -153,6 +153,7 @@ enum shf_op {
enum shf_sc { enum shf_sc {
SHF_SC_R_ROT = 0, SHF_SC_R_ROT = 0,
SHF_SC_NONE = SHF_SC_R_ROT,
SHF_SC_R_SHF = 1, SHF_SC_R_SHF = 1,
SHF_SC_L_SHF = 2, SHF_SC_L_SHF = 2,
SHF_SC_R_DSHF = 3, SHF_SC_R_DSHF = 3,
...@@ -216,7 +217,10 @@ struct cmd_tgt_act { ...@@ -216,7 +217,10 @@ struct cmd_tgt_act {
enum cmd_tgt_map { enum cmd_tgt_map {
CMD_TGT_READ8, CMD_TGT_READ8,
CMD_TGT_WRITE8, CMD_TGT_WRITE8_SWAP,
CMD_TGT_READ32,
CMD_TGT_READ32_LE,
CMD_TGT_READ32_SWAP,
CMD_TGT_READ_LE, CMD_TGT_READ_LE,
CMD_TGT_READ_SWAP_LE, CMD_TGT_READ_SWAP_LE,
__CMD_TGT_MAP_SIZE, __CMD_TGT_MAP_SIZE,
......
...@@ -813,6 +813,36 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, ...@@ -813,6 +813,36 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
return err; return err;
} }
static bool analyzer_is_valid_access(struct bpf_verifier_env *env, int off,
struct bpf_insn_access_aux *info)
{
switch (env->prog->type) {
case BPF_PROG_TYPE_XDP:
switch (off) {
case offsetof(struct xdp_buff, data):
info->reg_type = PTR_TO_PACKET;
return true;
case offsetof(struct xdp_buff, data_end):
info->reg_type = PTR_TO_PACKET_END;
return true;
}
return false;
case BPF_PROG_TYPE_SCHED_CLS:
switch (off) {
case offsetof(struct sk_buff, data):
info->reg_type = PTR_TO_PACKET;
return true;
case offsetof(struct sk_buff, cb) +
offsetof(struct bpf_skb_data_end, data_end):
info->reg_type = PTR_TO_PACKET_END;
return true;
}
return false;
default:
return false;
}
}
/* check access to 'struct bpf_context' fields. Supports fixed offsets only */ /* check access to 'struct bpf_context' fields. Supports fixed offsets only */
static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
enum bpf_access_type t, enum bpf_reg_type *reg_type) enum bpf_access_type t, enum bpf_reg_type *reg_type)
...@@ -821,11 +851,12 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, ...@@ -821,11 +851,12 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
.reg_type = *reg_type, .reg_type = *reg_type,
}; };
/* for analyzer ctx accesses are already validated and converted */ if (env->analyzer_ops) {
if (env->analyzer_ops) if (analyzer_is_valid_access(env, off, &info)) {
*reg_type = info.reg_type;
return 0; return 0;
}
if (env->prog->aux->ops->is_valid_access && } else if (env->prog->aux->ops->is_valid_access &&
env->prog->aux->ops->is_valid_access(off, size, t, &info)) { env->prog->aux->ops->is_valid_access(off, size, t, &info)) {
/* A non zero info.ctx_field_size indicates that this field is a /* A non zero info.ctx_field_size indicates that this field is a
* candidate for later verifier transformation to load the whole * candidate for later verifier transformation to load the whole
......
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