forked from mirrors/linux
		
	flow_offload: add skip_hw and skip_sw to control if offload the action
We add skip_hw and skip_sw for user to control if offload the action to hardware. We also add in_hw_count for user to indicate if the action is offloaded to any hardware. Signed-off-by: Baowen Zheng <baowen.zheng@corigine.com> Signed-off-by: Simon Horman <simon.horman@corigine.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									8cbfe939ab
								
							
						
					
					
						commit
						7adc576512
					
				
					 3 changed files with 84 additions and 9 deletions
				
			
		|  | @ -44,6 +44,7 @@ struct tc_action { | ||||||
| 	u8			hw_stats; | 	u8			hw_stats; | ||||||
| 	u8			used_hw_stats; | 	u8			used_hw_stats; | ||||||
| 	bool			used_hw_stats_valid; | 	bool			used_hw_stats_valid; | ||||||
|  | 	u32			in_hw_count; | ||||||
| }; | }; | ||||||
| #define tcf_index	common.tcfa_index | #define tcf_index	common.tcfa_index | ||||||
| #define tcf_refcnt	common.tcfa_refcnt | #define tcf_refcnt	common.tcfa_refcnt | ||||||
|  |  | ||||||
|  | @ -19,13 +19,16 @@ enum { | ||||||
| 	TCA_ACT_FLAGS, | 	TCA_ACT_FLAGS, | ||||||
| 	TCA_ACT_HW_STATS, | 	TCA_ACT_HW_STATS, | ||||||
| 	TCA_ACT_USED_HW_STATS, | 	TCA_ACT_USED_HW_STATS, | ||||||
|  | 	TCA_ACT_IN_HW_COUNT, | ||||||
| 	__TCA_ACT_MAX | 	__TCA_ACT_MAX | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* See other TCA_ACT_FLAGS_ * flags in include/net/act_api.h. */ | /* See other TCA_ACT_FLAGS_ * flags in include/net/act_api.h. */ | ||||||
| #define TCA_ACT_FLAGS_NO_PERCPU_STATS 1 /* Don't use percpu allocator for | #define TCA_ACT_FLAGS_NO_PERCPU_STATS (1 << 0) /* Don't use percpu allocator for | ||||||
| 					 * actions stats. | 						* actions stats. | ||||||
| 					 */ | 						*/ | ||||||
|  | #define TCA_ACT_FLAGS_SKIP_HW	(1 << 1) /* don't offload action to HW */ | ||||||
|  | #define TCA_ACT_FLAGS_SKIP_SW	(1 << 2) /* don't use action in SW */ | ||||||
| 
 | 
 | ||||||
| /* tca HW stats type
 | /* tca HW stats type
 | ||||||
|  * When user does not pass the attribute, he does not care. |  * When user does not pass the attribute, he does not care. | ||||||
|  |  | ||||||
|  | @ -131,6 +131,12 @@ static void free_tcf(struct tc_action *p) | ||||||
| 	kfree(p); | 	kfree(p); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void offload_action_hw_count_set(struct tc_action *act, | ||||||
|  | 					u32 hw_count) | ||||||
|  | { | ||||||
|  | 	act->in_hw_count = hw_count; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) | static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) | ||||||
| { | { | ||||||
| 	if (is_tcf_pedit(act)) | 	if (is_tcf_pedit(act)) | ||||||
|  | @ -139,6 +145,29 @@ static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act) | ||||||
| 		return 1; | 		return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool tc_act_skip_hw(u32 flags) | ||||||
|  | { | ||||||
|  | 	return (flags & TCA_ACT_FLAGS_SKIP_HW) ? true : false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool tc_act_skip_sw(u32 flags) | ||||||
|  | { | ||||||
|  | 	return (flags & TCA_ACT_FLAGS_SKIP_SW) ? true : false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool tc_act_in_hw(struct tc_action *act) | ||||||
|  | { | ||||||
|  | 	return !!act->in_hw_count; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* SKIP_HW and SKIP_SW are mutually exclusive flags. */ | ||||||
|  | static bool tc_act_flags_valid(u32 flags) | ||||||
|  | { | ||||||
|  | 	flags &= TCA_ACT_FLAGS_SKIP_HW | TCA_ACT_FLAGS_SKIP_SW; | ||||||
|  | 
 | ||||||
|  | 	return flags ^ (TCA_ACT_FLAGS_SKIP_HW | TCA_ACT_FLAGS_SKIP_SW); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int offload_action_init(struct flow_offload_action *fl_action, | static int offload_action_init(struct flow_offload_action *fl_action, | ||||||
| 			       struct tc_action *act, | 			       struct tc_action *act, | ||||||
| 			       enum offload_act_command  cmd, | 			       enum offload_act_command  cmd, | ||||||
|  | @ -155,6 +184,7 @@ static int offload_action_init(struct flow_offload_action *fl_action, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int tcf_action_offload_cmd(struct flow_offload_action *fl_act, | static int tcf_action_offload_cmd(struct flow_offload_action *fl_act, | ||||||
|  | 				  u32 *hw_count, | ||||||
| 				  struct netlink_ext_ack *extack) | 				  struct netlink_ext_ack *extack) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
|  | @ -164,6 +194,9 @@ static int tcf_action_offload_cmd(struct flow_offload_action *fl_act, | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
|  | 	if (hw_count) | ||||||
|  | 		*hw_count = err; | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -171,12 +204,17 @@ static int tcf_action_offload_cmd(struct flow_offload_action *fl_act, | ||||||
| static int tcf_action_offload_add(struct tc_action *action, | static int tcf_action_offload_add(struct tc_action *action, | ||||||
| 				  struct netlink_ext_ack *extack) | 				  struct netlink_ext_ack *extack) | ||||||
| { | { | ||||||
|  | 	bool skip_sw = tc_act_skip_sw(action->tcfa_flags); | ||||||
| 	struct tc_action *actions[TCA_ACT_MAX_PRIO] = { | 	struct tc_action *actions[TCA_ACT_MAX_PRIO] = { | ||||||
| 		[0] = action, | 		[0] = action, | ||||||
| 	}; | 	}; | ||||||
| 	struct flow_offload_action *fl_action; | 	struct flow_offload_action *fl_action; | ||||||
|  | 	u32 in_hw_count = 0; | ||||||
| 	int num, err = 0; | 	int num, err = 0; | ||||||
| 
 | 
 | ||||||
|  | 	if (tc_act_skip_hw(action->tcfa_flags)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
| 	num = tcf_offload_act_num_actions_single(action); | 	num = tcf_offload_act_num_actions_single(action); | ||||||
| 	fl_action = offload_action_alloc(num); | 	fl_action = offload_action_alloc(num); | ||||||
| 	if (!fl_action) | 	if (!fl_action) | ||||||
|  | @ -193,7 +231,13 @@ static int tcf_action_offload_add(struct tc_action *action, | ||||||
| 		goto fl_err; | 		goto fl_err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = tcf_action_offload_cmd(fl_action, extack); | 	err = tcf_action_offload_cmd(fl_action, &in_hw_count, extack); | ||||||
|  | 	if (!err) | ||||||
|  | 		offload_action_hw_count_set(action, in_hw_count); | ||||||
|  | 
 | ||||||
|  | 	if (skip_sw && !tc_act_in_hw(action)) | ||||||
|  | 		err = -EINVAL; | ||||||
|  | 
 | ||||||
| 	tc_cleanup_offload_action(&fl_action->action); | 	tc_cleanup_offload_action(&fl_action->action); | ||||||
| 
 | 
 | ||||||
| fl_err: | fl_err: | ||||||
|  | @ -205,13 +249,24 @@ static int tcf_action_offload_add(struct tc_action *action, | ||||||
| static int tcf_action_offload_del(struct tc_action *action) | static int tcf_action_offload_del(struct tc_action *action) | ||||||
| { | { | ||||||
| 	struct flow_offload_action fl_act = {}; | 	struct flow_offload_action fl_act = {}; | ||||||
|  | 	u32 in_hw_count = 0; | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
|  | 	if (!tc_act_in_hw(action)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
| 	err = offload_action_init(&fl_act, action, FLOW_ACT_DESTROY, NULL); | 	err = offload_action_init(&fl_act, action, FLOW_ACT_DESTROY, NULL); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
| 	return tcf_action_offload_cmd(&fl_act, NULL); | 	err = tcf_action_offload_cmd(&fl_act, &in_hw_count, NULL); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	if (action->in_hw_count != in_hw_count) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void tcf_action_cleanup(struct tc_action *p) | static void tcf_action_cleanup(struct tc_action *p) | ||||||
|  | @ -821,6 +876,9 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, | ||||||
| 			jmp_prgcnt -= 1; | 			jmp_prgcnt -= 1; | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if (tc_act_skip_sw(a->tcfa_flags)) | ||||||
|  | 			continue; | ||||||
| repeat: | repeat: | ||||||
| 		ret = a->ops->act(skb, a, res); | 		ret = a->ops->act(skb, a, res); | ||||||
| 		if (ret == TC_ACT_REPEAT) | 		if (ret == TC_ACT_REPEAT) | ||||||
|  | @ -926,6 +984,9 @@ tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref) | ||||||
| 			       a->tcfa_flags, a->tcfa_flags)) | 			       a->tcfa_flags, a->tcfa_flags)) | ||||||
| 		goto nla_put_failure; | 		goto nla_put_failure; | ||||||
| 
 | 
 | ||||||
|  | 	if (nla_put_u32(skb, TCA_ACT_IN_HW_COUNT, a->in_hw_count)) | ||||||
|  | 		goto nla_put_failure; | ||||||
|  | 
 | ||||||
| 	nest = nla_nest_start_noflag(skb, TCA_OPTIONS); | 	nest = nla_nest_start_noflag(skb, TCA_OPTIONS); | ||||||
| 	if (nest == NULL) | 	if (nest == NULL) | ||||||
| 		goto nla_put_failure; | 		goto nla_put_failure; | ||||||
|  | @ -1005,7 +1066,9 @@ static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = { | ||||||
| 	[TCA_ACT_COOKIE]	= { .type = NLA_BINARY, | 	[TCA_ACT_COOKIE]	= { .type = NLA_BINARY, | ||||||
| 				    .len = TC_COOKIE_MAX_SIZE }, | 				    .len = TC_COOKIE_MAX_SIZE }, | ||||||
| 	[TCA_ACT_OPTIONS]	= { .type = NLA_NESTED }, | 	[TCA_ACT_OPTIONS]	= { .type = NLA_NESTED }, | ||||||
| 	[TCA_ACT_FLAGS]		= NLA_POLICY_BITFIELD32(TCA_ACT_FLAGS_NO_PERCPU_STATS), | 	[TCA_ACT_FLAGS]		= NLA_POLICY_BITFIELD32(TCA_ACT_FLAGS_NO_PERCPU_STATS | | ||||||
|  | 							TCA_ACT_FLAGS_SKIP_HW | | ||||||
|  | 							TCA_ACT_FLAGS_SKIP_SW), | ||||||
| 	[TCA_ACT_HW_STATS]	= NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY), | 	[TCA_ACT_HW_STATS]	= NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -1118,8 +1181,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]); | 		hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]); | ||||||
| 		if (tb[TCA_ACT_FLAGS]) | 		if (tb[TCA_ACT_FLAGS]) { | ||||||
| 			userflags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]); | 			userflags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]); | ||||||
|  | 			if (!tc_act_flags_valid(userflags.value)) { | ||||||
|  | 				err = -EINVAL; | ||||||
|  | 				goto err_out; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp, | 		err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp, | ||||||
| 				userflags.value | flags, extack); | 				userflags.value | flags, extack); | ||||||
|  | @ -1194,8 +1262,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla, | ||||||
| 		sz += tcf_action_fill_size(act); | 		sz += tcf_action_fill_size(act); | ||||||
| 		/* Start from index 0 */ | 		/* Start from index 0 */ | ||||||
| 		actions[i - 1] = act; | 		actions[i - 1] = act; | ||||||
| 		if (!tc_act_bind(flags)) | 		if (!tc_act_bind(flags)) { | ||||||
| 			tcf_action_offload_add(act, extack); | 			err = tcf_action_offload_add(act, extack); | ||||||
|  | 			if (tc_act_skip_sw(act->tcfa_flags) && err) | ||||||
|  | 				goto err; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* We have to commit them all together, because if any error happened in
 | 	/* We have to commit them all together, because if any error happened in
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Baowen Zheng
						Baowen Zheng