Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8fcefe8 authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 21b59443 d3f89b98
Loading
Loading
Loading
Loading
+44 −11
Original line number Diff line number Diff line
@@ -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,
+8 −0
Original line number Diff line number Diff line
@@ -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;
+2 −3
Original line number Diff line number Diff line
@@ -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;
+38 −55
Original line number Diff line number Diff line
@@ -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;

	skip_sw = tc_skip_sw(prog->gen_flags) ||
		(oldprog && tc_skip_sw(oldprog->gen_flags));

	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 {
	if (prog && oldprog && prog->gen_flags != oldprog->gen_flags)
		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;

	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;

	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 = {};

	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;

	cls_bpf_offload_cmd(tp, prog, TC_CLSBPF_STATS);
	tc_setup_cb_call(block, NULL, TC_SETUP_CLSBPF, &cls_bpf, false);
}

static int cls_bpf_init(struct tcf_proto *tp)