Commit 8cbfe939 authored by Baowen Zheng's avatar Baowen Zheng Committed by David S. Miller
Browse files

flow_offload: allow user to offload tc action to net device



Use flow_indr_dev_register/flow_indr_dev_setup_offload to
offload tc action.

We need to call tc_cleanup_flow_action to clean up tc action entry since
in tc_setup_action, some actions may hold dev refcnt, especially the mirror
action.

Signed-off-by: default avatarBaowen Zheng <baowen.zheng@corigine.com>
Signed-off-by: default avatarLouis Peens <louis.peens@corigine.com>
Signed-off-by: default avatarSimon Horman <simon.horman@corigine.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c54e1d92
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -920,6 +920,7 @@ enum tc_setup_type {
	TC_SETUP_QDISC_TBF,
	TC_SETUP_QDISC_FIFO,
	TC_SETUP_QDISC_HTB,
	TC_SETUP_ACT,
};

/* These structures hold the attributes of bpf state that are being passed
+17 −0
Original line number Diff line number Diff line
@@ -551,6 +551,23 @@ struct flow_cls_offload {
	u32 classid;
};

enum offload_act_command  {
	FLOW_ACT_REPLACE,
	FLOW_ACT_DESTROY,
	FLOW_ACT_STATS,
};

struct flow_offload_action {
	struct netlink_ext_ack *extack; /* NULL in FLOW_ACT_STATS process*/
	enum offload_act_command  command;
	enum flow_action_id id;
	u32 index;
	struct flow_stats stats;
	struct flow_action action;
};

struct flow_offload_action *offload_action_alloc(unsigned int num_actions);

static inline struct flow_rule *
flow_cls_offload_flow_rule(struct flow_cls_offload *flow_cmd)
{
+5 −0
Original line number Diff line number Diff line
@@ -262,6 +262,9 @@ static inline void tcf_exts_put_net(struct tcf_exts *exts)
	for (; 0; (void)(i), (void)(a), (void)(exts))
#endif

#define tcf_act_for_each_action(i, a, actions) \
	for (i = 0; i < TCA_ACT_MAX_PRIO && ((a) = actions[i]); i++)

static inline void
tcf_exts_stats_update(const struct tcf_exts *exts,
		      u64 bytes, u64 packets, u64 drops, u64 lastuse,
@@ -539,6 +542,8 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
int tc_setup_offload_action(struct flow_action *flow_action,
			    const struct tcf_exts *exts);
void tc_cleanup_offload_action(struct flow_action *flow_action);
int tc_setup_action(struct flow_action *flow_action,
		    struct tc_action *actions[]);

int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
		     void *type_data, bool err_stop, bool rtnl_held);
+34 −8
Original line number Diff line number Diff line
@@ -27,6 +27,26 @@ struct flow_rule *flow_rule_alloc(unsigned int num_actions)
}
EXPORT_SYMBOL(flow_rule_alloc);

struct flow_offload_action *offload_action_alloc(unsigned int num_actions)
{
	struct flow_offload_action *fl_action;
	int i;

	fl_action = kzalloc(struct_size(fl_action, action.entries, num_actions),
			    GFP_KERNEL);
	if (!fl_action)
		return NULL;

	fl_action->action.num_entries = num_actions;
	/* Pre-fill each action hw_stats with DONT_CARE.
	 * Caller can override this if it wants stats for a given action.
	 */
	for (i = 0; i < num_actions; i++)
		fl_action->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE;

	return fl_action;
}

#define FLOW_DISSECTOR_MATCH(__rule, __type, __out)				\
	const struct flow_match *__m = &(__rule)->match;			\
	struct flow_dissector *__d = (__m)->dissector;				\
@@ -549,19 +569,25 @@ int flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch,
				void (*cleanup)(struct flow_block_cb *block_cb))
{
	struct flow_indr_dev *this;
	u32 count = 0;
	int err;

	mutex_lock(&flow_indr_block_lock);

	if (bo) {
		if (bo->command == FLOW_BLOCK_BIND)
			indir_dev_add(data, dev, sch, type, cleanup, bo);
		else if (bo->command == FLOW_BLOCK_UNBIND)
			indir_dev_remove(data);
	}

	list_for_each_entry(this, &flow_block_indr_dev_list, list)
		this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup);
	list_for_each_entry(this, &flow_block_indr_dev_list, list) {
		err = this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup);
		if (!err)
			count++;
	}

	mutex_unlock(&flow_indr_block_lock);

	return list_empty(&bo->cb_list) ? -EOPNOTSUPP : 0;
	return (bo && list_empty(&bo->cb_list)) ? -EOPNOTSUPP : count;
}
EXPORT_SYMBOL(flow_indr_dev_setup_offload);
+93 −0
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@
#include <net/sock.h>
#include <net/sch_generic.h>
#include <net/pkt_cls.h>
#include <net/tc_act/tc_pedit.h>
#include <net/act_api.h>
#include <net/netlink.h>
#include <net/flow_offload.h>

#ifdef CONFIG_INET
DEFINE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
@@ -129,8 +131,92 @@ static void free_tcf(struct tc_action *p)
	kfree(p);
}

static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act)
{
	if (is_tcf_pedit(act))
		return tcf_pedit_nkeys(act);
	else
		return 1;
}

static int offload_action_init(struct flow_offload_action *fl_action,
			       struct tc_action *act,
			       enum offload_act_command  cmd,
			       struct netlink_ext_ack *extack)
{
	fl_action->extack = extack;
	fl_action->command = cmd;
	fl_action->index = act->tcfa_index;

	if (act->ops->offload_act_setup)
		return act->ops->offload_act_setup(act, fl_action, NULL, false);

	return -EOPNOTSUPP;
}

static int tcf_action_offload_cmd(struct flow_offload_action *fl_act,
				  struct netlink_ext_ack *extack)
{
	int err;

	err = flow_indr_dev_setup_offload(NULL, NULL, TC_SETUP_ACT,
					  fl_act, NULL, NULL);
	if (err < 0)
		return err;

	return 0;
}

/* offload the tc action after it is inserted */
static int tcf_action_offload_add(struct tc_action *action,
				  struct netlink_ext_ack *extack)
{
	struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
		[0] = action,
	};
	struct flow_offload_action *fl_action;
	int num, err = 0;

	num = tcf_offload_act_num_actions_single(action);
	fl_action = offload_action_alloc(num);
	if (!fl_action)
		return -ENOMEM;

	err = offload_action_init(fl_action, action, FLOW_ACT_REPLACE, extack);
	if (err)
		goto fl_err;

	err = tc_setup_action(&fl_action->action, actions);
	if (err) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Failed to setup tc actions for offload\n");
		goto fl_err;
	}

	err = tcf_action_offload_cmd(fl_action, extack);
	tc_cleanup_offload_action(&fl_action->action);

fl_err:
	kfree(fl_action);

	return err;
}

static int tcf_action_offload_del(struct tc_action *action)
{
	struct flow_offload_action fl_act = {};
	int err = 0;

	err = offload_action_init(&fl_act, action, FLOW_ACT_DESTROY, NULL);
	if (err)
		return err;

	return tcf_action_offload_cmd(&fl_act, NULL);
}

static void tcf_action_cleanup(struct tc_action *p)
{
	tcf_action_offload_del(p);
	if (p->ops->cleanup)
		p->ops->cleanup(p);

@@ -1061,6 +1147,11 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
	return ERR_PTR(err);
}

static bool tc_act_bind(u32 flags)
{
	return !!(flags & TCA_ACT_FLAGS_BIND);
}

/* Returns numbers of initialized actions or negative error. */

int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
@@ -1103,6 +1194,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
		sz += tcf_action_fill_size(act);
		/* Start from index 0 */
		actions[i - 1] = act;
		if (!tc_act_bind(flags))
			tcf_action_offload_add(act, extack);
	}

	/* We have to commit them all together, because if any error happened in
Loading