Merge branch 'cls_bpf-fix-offload-state-tracking-with-block-callbacks'
Jakub Kicinski says: =================== cls_bpf: fix offload state tracking with block callbacks After introduction of block callbacks classifiers can no longer track offload state. cls_bpf used to do that in an attempt to move common code from drivers to the core. Remove that functionality and fix drivers. The user-visible bug this is fixing is that trying to offload a second filter would trigger a spurious DESTROY and in turn disable the already installed one. =================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
a8fcefe88b
|
@ -82,10 +82,33 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn)
|
|||
return nfp_net_ebpf_capable(nn) ? "BPF" : "";
|
||||
}
|
||||
|
||||
static int
|
||||
nfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
|
||||
{
|
||||
int err;
|
||||
|
||||
nn->app_priv = kzalloc(sizeof(struct nfp_bpf_vnic), GFP_KERNEL);
|
||||
if (!nn->app_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
err = nfp_app_nic_vnic_alloc(app, nn, id);
|
||||
if (err)
|
||||
goto err_free_priv;
|
||||
|
||||
return 0;
|
||||
err_free_priv:
|
||||
kfree(nn->app_priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn)
|
||||
{
|
||||
struct nfp_bpf_vnic *bv = nn->app_priv;
|
||||
|
||||
if (nn->dp.bpf_offload_xdp)
|
||||
nfp_bpf_xdp_offload(app, nn, NULL);
|
||||
WARN_ON(bv->tc_prog);
|
||||
kfree(bv);
|
||||
}
|
||||
|
||||
static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
|
||||
|
@ -93,6 +116,9 @@ static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
|
|||
{
|
||||
struct tc_cls_bpf_offload *cls_bpf = type_data;
|
||||
struct nfp_net *nn = cb_priv;
|
||||
struct bpf_prog *oldprog;
|
||||
struct nfp_bpf_vnic *bv;
|
||||
int err;
|
||||
|
||||
if (type != TC_SETUP_CLSBPF ||
|
||||
!tc_can_offload(nn->dp.netdev) ||
|
||||
|
@ -100,8 +126,6 @@ static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
|
|||
cls_bpf->common.protocol != htons(ETH_P_ALL) ||
|
||||
cls_bpf->common.chain_index)
|
||||
return -EOPNOTSUPP;
|
||||
if (nn->dp.bpf_offload_xdp)
|
||||
return -EBUSY;
|
||||
|
||||
/* Only support TC direct action */
|
||||
if (!cls_bpf->exts_integrated ||
|
||||
|
@ -110,16 +134,25 @@ static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
switch (cls_bpf->command) {
|
||||
case TC_CLSBPF_REPLACE:
|
||||
return nfp_net_bpf_offload(nn, cls_bpf->prog, true);
|
||||
case TC_CLSBPF_ADD:
|
||||
return nfp_net_bpf_offload(nn, cls_bpf->prog, false);
|
||||
case TC_CLSBPF_DESTROY:
|
||||
return nfp_net_bpf_offload(nn, NULL, true);
|
||||
default:
|
||||
if (cls_bpf->command != TC_CLSBPF_OFFLOAD)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bv = nn->app_priv;
|
||||
oldprog = cls_bpf->oldprog;
|
||||
|
||||
/* Don't remove if oldprog doesn't match driver's state */
|
||||
if (bv->tc_prog != oldprog) {
|
||||
oldprog = NULL;
|
||||
if (!cls_bpf->prog)
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = nfp_net_bpf_offload(nn, cls_bpf->prog, oldprog);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
bv->tc_prog = cls_bpf->prog;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfp_bpf_setup_tc_block(struct net_device *netdev,
|
||||
|
@ -167,7 +200,7 @@ const struct nfp_app_type app_bpf = {
|
|||
|
||||
.extra_cap = nfp_bpf_extra_cap,
|
||||
|
||||
.vnic_alloc = nfp_app_nic_vnic_alloc,
|
||||
.vnic_alloc = nfp_bpf_vnic_alloc,
|
||||
.vnic_free = nfp_bpf_vnic_free,
|
||||
|
||||
.setup_tc = nfp_bpf_setup_tc,
|
||||
|
|
|
@ -172,6 +172,14 @@ struct nfp_prog {
|
|||
struct list_head insns;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nfp_bpf_vnic - per-vNIC BPF priv structure
|
||||
* @tc_prog: currently loaded cls_bpf program
|
||||
*/
|
||||
struct nfp_bpf_vnic {
|
||||
struct bpf_prog *tc_prog;
|
||||
};
|
||||
|
||||
int nfp_bpf_jit(struct nfp_prog *prog);
|
||||
|
||||
extern const struct bpf_ext_analyzer_ops nfp_bpf_analyzer_ops;
|
||||
|
|
|
@ -694,9 +694,7 @@ struct tc_cls_matchall_offload {
|
|||
};
|
||||
|
||||
enum tc_clsbpf_command {
|
||||
TC_CLSBPF_ADD,
|
||||
TC_CLSBPF_REPLACE,
|
||||
TC_CLSBPF_DESTROY,
|
||||
TC_CLSBPF_OFFLOAD,
|
||||
TC_CLSBPF_STATS,
|
||||
};
|
||||
|
||||
|
@ -705,6 +703,7 @@ struct tc_cls_bpf_offload {
|
|||
enum tc_clsbpf_command command;
|
||||
struct tcf_exts *exts;
|
||||
struct bpf_prog *prog;
|
||||
struct bpf_prog *oldprog;
|
||||
const char *name;
|
||||
bool exts_integrated;
|
||||
u32 gen_flags;
|
||||
|
|
|
@ -42,7 +42,6 @@ struct cls_bpf_prog {
|
|||
struct list_head link;
|
||||
struct tcf_result res;
|
||||
bool exts_integrated;
|
||||
bool offloaded;
|
||||
u32 gen_flags;
|
||||
struct tcf_exts exts;
|
||||
u32 handle;
|
||||
|
@ -148,33 +147,37 @@ static bool cls_bpf_is_ebpf(const struct cls_bpf_prog *prog)
|
|||
}
|
||||
|
||||
static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
|
||||
enum tc_clsbpf_command cmd)
|
||||
struct cls_bpf_prog *oldprog)
|
||||
{
|
||||
bool addorrep = cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE;
|
||||
struct tcf_block *block = tp->chain->block;
|
||||
bool skip_sw = tc_skip_sw(prog->gen_flags);
|
||||
struct tc_cls_bpf_offload cls_bpf = {};
|
||||
struct cls_bpf_prog *obj;
|
||||
bool skip_sw;
|
||||
int err;
|
||||
|
||||
skip_sw = prog && tc_skip_sw(prog->gen_flags);
|
||||
obj = prog ?: oldprog;
|
||||
|
||||
tc_cls_common_offload_init(&cls_bpf.common, tp);
|
||||
cls_bpf.command = cmd;
|
||||
cls_bpf.exts = &prog->exts;
|
||||
cls_bpf.prog = prog->filter;
|
||||
cls_bpf.name = prog->bpf_name;
|
||||
cls_bpf.exts_integrated = prog->exts_integrated;
|
||||
cls_bpf.gen_flags = prog->gen_flags;
|
||||
cls_bpf.command = TC_CLSBPF_OFFLOAD;
|
||||
cls_bpf.exts = &obj->exts;
|
||||
cls_bpf.prog = prog ? prog->filter : NULL;
|
||||
cls_bpf.oldprog = oldprog ? oldprog->filter : NULL;
|
||||
cls_bpf.name = obj->bpf_name;
|
||||
cls_bpf.exts_integrated = obj->exts_integrated;
|
||||
cls_bpf.gen_flags = obj->gen_flags;
|
||||
|
||||
err = tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, skip_sw);
|
||||
if (addorrep) {
|
||||
if (prog) {
|
||||
if (err < 0) {
|
||||
cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_DESTROY);
|
||||
cls_bpf_offload_cmd(tp, oldprog, prog);
|
||||
return err;
|
||||
} else if (err > 0) {
|
||||
prog->gen_flags |= TCA_CLS_FLAGS_IN_HW;
|
||||
}
|
||||
}
|
||||
|
||||
if (addorrep && skip_sw && !(prog->gen_flags & TCA_CLS_FLAGS_IN_HW))
|
||||
if (prog && skip_sw && !(prog->gen_flags & TCA_CLS_FLAGS_IN_HW))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
@ -183,38 +186,17 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog,
|
|||
static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog,
|
||||
struct cls_bpf_prog *oldprog)
|
||||
{
|
||||
struct cls_bpf_prog *obj = prog;
|
||||
enum tc_clsbpf_command cmd;
|
||||
bool skip_sw;
|
||||
int ret;
|
||||
if (prog && oldprog && prog->gen_flags != oldprog->gen_flags)
|
||||
return -EINVAL;
|
||||
|
||||
skip_sw = tc_skip_sw(prog->gen_flags) ||
|
||||
(oldprog && tc_skip_sw(oldprog->gen_flags));
|
||||
if (prog && tc_skip_hw(prog->gen_flags))
|
||||
prog = NULL;
|
||||
if (oldprog && tc_skip_hw(oldprog->gen_flags))
|
||||
oldprog = NULL;
|
||||
if (!prog && !oldprog)
|
||||
return 0;
|
||||
|
||||
if (oldprog && oldprog->offloaded) {
|
||||
if (!tc_skip_hw(prog->gen_flags)) {
|
||||
cmd = TC_CLSBPF_REPLACE;
|
||||
} else if (!tc_skip_sw(prog->gen_flags)) {
|
||||
obj = oldprog;
|
||||
cmd = TC_CLSBPF_DESTROY;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (tc_skip_hw(prog->gen_flags))
|
||||
return skip_sw ? -EINVAL : 0;
|
||||
cmd = TC_CLSBPF_ADD;
|
||||
}
|
||||
|
||||
ret = cls_bpf_offload_cmd(tp, obj, cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
obj->offloaded = true;
|
||||
if (oldprog)
|
||||
oldprog->offloaded = false;
|
||||
|
||||
return 0;
|
||||
return cls_bpf_offload_cmd(tp, prog, oldprog);
|
||||
}
|
||||
|
||||
static void cls_bpf_stop_offload(struct tcf_proto *tp,
|
||||
|
@ -222,25 +204,26 @@ static void cls_bpf_stop_offload(struct tcf_proto *tp,
|
|||
{
|
||||
int err;
|
||||
|
||||
if (!prog->offloaded)
|
||||
return;
|
||||
|
||||
err = cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_DESTROY);
|
||||
if (err) {
|
||||
err = cls_bpf_offload_cmd(tp, NULL, prog);
|
||||
if (err)
|
||||
pr_err("Stopping hardware offload failed: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
prog->offloaded = false;
|
||||
}
|
||||
|
||||
static void cls_bpf_offload_update_stats(struct tcf_proto *tp,
|
||||
struct cls_bpf_prog *prog)
|
||||
{
|
||||
if (!prog->offloaded)
|
||||
return;
|
||||
struct tcf_block *block = tp->chain->block;
|
||||
struct tc_cls_bpf_offload cls_bpf = {};
|
||||
|
||||
cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_STATS);
|
||||
tc_cls_common_offload_init(&cls_bpf.common, tp);
|
||||
cls_bpf.command = TC_CLSBPF_STATS;
|
||||
cls_bpf.exts = &prog->exts;
|
||||
cls_bpf.prog = prog->filter;
|
||||
cls_bpf.name = prog->bpf_name;
|
||||
cls_bpf.exts_integrated = prog->exts_integrated;
|
||||
cls_bpf.gen_flags = prog->gen_flags;
|
||||
|
||||
tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, false);
|
||||
}
|
||||
|
||||
static int cls_bpf_init(struct tcf_proto *tp)
|
||||
|
|
Loading…
Reference in New Issue