forked from mirrors/linux
		
	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: Baowen Zheng <baowen.zheng@corigine.com> Signed-off-by: Louis Peens <louis.peens@corigine.com> Signed-off-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									c54e1d920f
								
							
						
					
					
						commit
						8cbfe939ab
					
				
					 17 changed files with 254 additions and 23 deletions
				
			
		|  | @ -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
 | ||||
|  |  | |||
|  | @ -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) | ||||
| { | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -705,7 +705,9 @@ static int tcf_csum_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		entry->csum_flags = tcf_csum_update_flags(act); | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		return -EOPNOTSUPP; | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		fl_action->id = FLOW_ACTION_CSUM; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -1505,7 +1505,9 @@ static int tcf_ct_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		entry->ct.flow_table = tcf_ct_ft(act); | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		return -EOPNOTSUPP; | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		fl_action->id = FLOW_ACTION_CT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -272,6 +272,17 @@ static int tcf_gact_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		} | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		if (is_tcf_gact_ok(act)) | ||||
| 			fl_action->id = FLOW_ACTION_ACCEPT; | ||||
| 		else if (is_tcf_gact_shot(act)) | ||||
| 			fl_action->id = FLOW_ACTION_DROP; | ||||
| 		else if (is_tcf_gact_trap(act)) | ||||
| 			fl_action->id = FLOW_ACTION_TRAP; | ||||
| 		else if (is_tcf_gact_goto_chain(act)) | ||||
| 			fl_action->id = FLOW_ACTION_GOTO; | ||||
| 		else | ||||
| 			return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -637,7 +637,9 @@ static int tcf_gate_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 			return err; | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		return -EOPNOTSUPP; | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		fl_action->id = FLOW_ACTION_GATE; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -482,6 +482,17 @@ static int tcf_mirred_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		} | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		if (is_tcf_mirred_egress_redirect(act)) | ||||
| 			fl_action->id = FLOW_ACTION_REDIRECT; | ||||
| 		else if (is_tcf_mirred_egress_mirror(act)) | ||||
| 			fl_action->id = FLOW_ACTION_MIRRED; | ||||
| 		else if (is_tcf_mirred_ingress_redirect(act)) | ||||
| 			fl_action->id = FLOW_ACTION_REDIRECT_INGRESS; | ||||
| 		else if (is_tcf_mirred_ingress_mirror(act)) | ||||
| 			fl_action->id = FLOW_ACTION_MIRRED_INGRESS; | ||||
| 		else | ||||
| 			return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -415,8 +415,22 @@ static int tcf_mpls_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		} | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		switch (tcf_mpls_action(act)) { | ||||
| 		case TCA_MPLS_ACT_PUSH: | ||||
| 			fl_action->id = FLOW_ACTION_MPLS_PUSH; | ||||
| 			break; | ||||
| 		case TCA_MPLS_ACT_POP: | ||||
| 			fl_action->id = FLOW_ACTION_MPLS_POP; | ||||
| 			break; | ||||
| 		case TCA_MPLS_ACT_MODIFY: | ||||
| 			fl_action->id = FLOW_ACTION_MPLS_MANGLE; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return -EOPNOTSUPP; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -421,7 +421,9 @@ static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		entry->police.mtu = tcf_police_tcfp_mtu(act); | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		return -EOPNOTSUPP; | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		fl_action->id = FLOW_ACTION_POLICE; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -303,7 +303,9 @@ static int tcf_sample_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		tcf_offload_sample_get_group(entry, act); | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		return -EOPNOTSUPP; | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		fl_action->id = FLOW_ACTION_SAMPLE; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -347,6 +347,15 @@ static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data | |||
| 		} | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		if (is_tcf_skbedit_mark(act)) | ||||
| 			fl_action->id = FLOW_ACTION_MARK; | ||||
| 		else if (is_tcf_skbedit_ptype(act)) | ||||
| 			fl_action->id = FLOW_ACTION_PTYPE; | ||||
| 		else if (is_tcf_skbedit_priority(act)) | ||||
| 			fl_action->id = FLOW_ACTION_PRIORITY; | ||||
| 		else | ||||
| 			return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -827,6 +827,13 @@ static int tcf_tunnel_key_offload_act_setup(struct tc_action *act, | |||
| 		} | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		if (is_tcf_tunnel_set(act)) | ||||
| 			fl_action->id = FLOW_ACTION_TUNNEL_ENCAP; | ||||
| 		else if (is_tcf_tunnel_release(act)) | ||||
| 			fl_action->id = FLOW_ACTION_TUNNEL_DECAP; | ||||
| 		else | ||||
| 			return -EOPNOTSUPP; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -395,8 +395,22 @@ static int tcf_vlan_offload_act_setup(struct tc_action *act, void *entry_data, | |||
| 		} | ||||
| 		*index_inc = 1; | ||||
| 	} else { | ||||
| 		struct flow_offload_action *fl_action = entry_data; | ||||
| 
 | ||||
| 		switch (tcf_vlan_action(act)) { | ||||
| 		case TCA_VLAN_ACT_PUSH: | ||||
| 			fl_action->id = FLOW_ACTION_VLAN_PUSH; | ||||
| 			break; | ||||
| 		case TCA_VLAN_ACT_POP: | ||||
| 			fl_action->id = FLOW_ACTION_VLAN_POP; | ||||
| 			break; | ||||
| 		case TCA_VLAN_ACT_MODIFY: | ||||
| 			fl_action->id = FLOW_ACTION_VLAN_MANGLE; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return -EOPNOTSUPP; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -3488,8 +3488,8 @@ static int tc_setup_offload_act(struct tc_action *act, | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| int tc_setup_offload_action(struct flow_action *flow_action, | ||||
| 			    const struct tcf_exts *exts) | ||||
| int tc_setup_action(struct flow_action *flow_action, | ||||
| 		    struct tc_action *actions[]) | ||||
| { | ||||
| 	int i, j, index, err = 0; | ||||
| 	struct tc_action *act; | ||||
|  | @ -3498,11 +3498,11 @@ int tc_setup_offload_action(struct flow_action *flow_action, | |||
| 	BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE); | ||||
| 	BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED); | ||||
| 
 | ||||
| 	if (!exts) | ||||
| 	if (!actions) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	j = 0; | ||||
| 	tcf_exts_for_each_action(i, act, exts) { | ||||
| 	tcf_act_for_each_action(i, act, actions) { | ||||
| 		struct flow_action_entry *entry; | ||||
| 
 | ||||
| 		entry = &flow_action->entries[j]; | ||||
|  | @ -3531,6 +3531,19 @@ int tc_setup_offload_action(struct flow_action *flow_action, | |||
| 	spin_unlock_bh(&act->tcfa_lock); | ||||
| 	goto err_out; | ||||
| } | ||||
| 
 | ||||
| int tc_setup_offload_action(struct flow_action *flow_action, | ||||
| 			    const struct tcf_exts *exts) | ||||
| { | ||||
| #ifdef CONFIG_NET_CLS_ACT | ||||
| 	if (!exts) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return tc_setup_action(flow_action, exts->actions); | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
| EXPORT_SYMBOL(tc_setup_offload_action); | ||||
| 
 | ||||
| unsigned int tcf_exts_num_actions(struct tcf_exts *exts) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Baowen Zheng
						Baowen Zheng