18afd552dSSerhiy Boiko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28afd552dSSerhiy Boiko /* Copyright (c) 2019-2022 Marvell International Ltd. All rights reserved */
38afd552dSSerhiy Boiko 
48afd552dSSerhiy Boiko #include <linux/kernel.h>
58afd552dSSerhiy Boiko #include <linux/list.h>
68afd552dSSerhiy Boiko 
78afd552dSSerhiy Boiko #include "prestera.h"
88afd552dSSerhiy Boiko #include "prestera_hw.h"
98afd552dSSerhiy Boiko #include "prestera_flow.h"
108afd552dSSerhiy Boiko #include "prestera_flower.h"
118afd552dSSerhiy Boiko #include "prestera_matchall.h"
128afd552dSSerhiy Boiko #include "prestera_span.h"
138afd552dSSerhiy Boiko 
prestera_mall_prio_check(struct prestera_flow_block * block,struct tc_cls_matchall_offload * f)1444af9571SMaksym Glubokiy static int prestera_mall_prio_check(struct prestera_flow_block *block,
1544af9571SMaksym Glubokiy 				    struct tc_cls_matchall_offload *f)
1644af9571SMaksym Glubokiy {
1744af9571SMaksym Glubokiy 	u32 flower_prio_min;
1844af9571SMaksym Glubokiy 	u32 flower_prio_max;
1944af9571SMaksym Glubokiy 	int err;
2044af9571SMaksym Glubokiy 
2144af9571SMaksym Glubokiy 	err = prestera_flower_prio_get(block, f->common.chain_index,
2244af9571SMaksym Glubokiy 				       &flower_prio_min, &flower_prio_max);
2344af9571SMaksym Glubokiy 	if (err == -ENOENT)
2444af9571SMaksym Glubokiy 		/* No flower filters installed on this chain. */
2544af9571SMaksym Glubokiy 		return 0;
2644af9571SMaksym Glubokiy 
2744af9571SMaksym Glubokiy 	if (err) {
2844af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities");
2944af9571SMaksym Glubokiy 		return err;
3044af9571SMaksym Glubokiy 	}
3144af9571SMaksym Glubokiy 
3244af9571SMaksym Glubokiy 	if (f->common.prio <= flower_prio_max && !block->ingress) {
3344af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules");
3444af9571SMaksym Glubokiy 		return -EOPNOTSUPP;
3544af9571SMaksym Glubokiy 	}
3644af9571SMaksym Glubokiy 	if (f->common.prio >= flower_prio_min && block->ingress) {
3744af9571SMaksym Glubokiy 		NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing flower rules");
3844af9571SMaksym Glubokiy 		return -EOPNOTSUPP;
3944af9571SMaksym Glubokiy 	}
4044af9571SMaksym Glubokiy 
4144af9571SMaksym Glubokiy 	return 0;
4244af9571SMaksym Glubokiy }
4344af9571SMaksym Glubokiy 
prestera_mall_prio_get(struct prestera_flow_block * block,u32 * prio_min,u32 * prio_max)4444af9571SMaksym Glubokiy int prestera_mall_prio_get(struct prestera_flow_block *block,
4544af9571SMaksym Glubokiy 			   u32 *prio_min, u32 *prio_max)
4644af9571SMaksym Glubokiy {
4744af9571SMaksym Glubokiy 	if (!block->mall.bound)
4844af9571SMaksym Glubokiy 		return -ENOENT;
4944af9571SMaksym Glubokiy 
5044af9571SMaksym Glubokiy 	*prio_min = block->mall.prio_min;
5144af9571SMaksym Glubokiy 	*prio_max = block->mall.prio_max;
5244af9571SMaksym Glubokiy 	return 0;
5344af9571SMaksym Glubokiy }
5444af9571SMaksym Glubokiy 
prestera_mall_prio_update(struct prestera_flow_block * block,struct tc_cls_matchall_offload * f)5544af9571SMaksym Glubokiy static void prestera_mall_prio_update(struct prestera_flow_block *block,
5644af9571SMaksym Glubokiy 				      struct tc_cls_matchall_offload *f)
5744af9571SMaksym Glubokiy {
5844af9571SMaksym Glubokiy 	block->mall.prio_min = min(block->mall.prio_min, f->common.prio);
5944af9571SMaksym Glubokiy 	block->mall.prio_max = max(block->mall.prio_max, f->common.prio);
6044af9571SMaksym Glubokiy }
6144af9571SMaksym Glubokiy 
prestera_mall_replace(struct prestera_flow_block * block,struct tc_cls_matchall_offload * f)628afd552dSSerhiy Boiko int prestera_mall_replace(struct prestera_flow_block *block,
638afd552dSSerhiy Boiko 			  struct tc_cls_matchall_offload *f)
648afd552dSSerhiy Boiko {
658afd552dSSerhiy Boiko 	struct prestera_flow_block_binding *binding;
668afd552dSSerhiy Boiko 	__be16 protocol = f->common.protocol;
678afd552dSSerhiy Boiko 	struct flow_action_entry *act;
688afd552dSSerhiy Boiko 	struct prestera_port *port;
698afd552dSSerhiy Boiko 	int err;
708afd552dSSerhiy Boiko 
718afd552dSSerhiy Boiko 	if (!flow_offload_has_one_action(&f->rule->action)) {
728afd552dSSerhiy Boiko 		NL_SET_ERR_MSG(f->common.extack,
738afd552dSSerhiy Boiko 			       "Only singular actions are supported");
748afd552dSSerhiy Boiko 		return -EOPNOTSUPP;
758afd552dSSerhiy Boiko 	}
768afd552dSSerhiy Boiko 
778afd552dSSerhiy Boiko 	act = &f->rule->action.entries[0];
788afd552dSSerhiy Boiko 
798afd552dSSerhiy Boiko 	if (!prestera_netdev_check(act->dev)) {
808afd552dSSerhiy Boiko 		NL_SET_ERR_MSG(f->common.extack,
818afd552dSSerhiy Boiko 			       "Only Marvell Prestera port is supported");
828afd552dSSerhiy Boiko 		return -EINVAL;
838afd552dSSerhiy Boiko 	}
848afd552dSSerhiy Boiko 	if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
858afd552dSSerhiy Boiko 		return -EOPNOTSUPP;
868afd552dSSerhiy Boiko 	if (act->id != FLOW_ACTION_MIRRED)
878afd552dSSerhiy Boiko 		return -EOPNOTSUPP;
888afd552dSSerhiy Boiko 	if (protocol != htons(ETH_P_ALL))
898afd552dSSerhiy Boiko 		return -EOPNOTSUPP;
908afd552dSSerhiy Boiko 
9144af9571SMaksym Glubokiy 	err = prestera_mall_prio_check(block, f);
9244af9571SMaksym Glubokiy 	if (err)
9344af9571SMaksym Glubokiy 		return err;
9444af9571SMaksym Glubokiy 
958afd552dSSerhiy Boiko 	port = netdev_priv(act->dev);
968afd552dSSerhiy Boiko 
978afd552dSSerhiy Boiko 	list_for_each_entry(binding, &block->binding_list, list) {
988c448c2bSSerhiy Boiko 		err = prestera_span_rule_add(binding, port, block->ingress);
99*fb4a5dfcSSerhiy Boiko 		if (err == -EEXIST)
100*fb4a5dfcSSerhiy Boiko 			return err;
1018afd552dSSerhiy Boiko 		if (err)
1028afd552dSSerhiy Boiko 			goto rollback;
1038afd552dSSerhiy Boiko 	}
1048afd552dSSerhiy Boiko 
10544af9571SMaksym Glubokiy 	prestera_mall_prio_update(block, f);
10644af9571SMaksym Glubokiy 
10744af9571SMaksym Glubokiy 	block->mall.bound = true;
1088afd552dSSerhiy Boiko 	return 0;
1098afd552dSSerhiy Boiko 
1108afd552dSSerhiy Boiko rollback:
1118afd552dSSerhiy Boiko 	list_for_each_entry_continue_reverse(binding,
1128afd552dSSerhiy Boiko 					     &block->binding_list, list)
1138c448c2bSSerhiy Boiko 		prestera_span_rule_del(binding, block->ingress);
1148afd552dSSerhiy Boiko 	return err;
1158afd552dSSerhiy Boiko }
1168afd552dSSerhiy Boiko 
prestera_mall_destroy(struct prestera_flow_block * block)1178afd552dSSerhiy Boiko void prestera_mall_destroy(struct prestera_flow_block *block)
1188afd552dSSerhiy Boiko {
1198afd552dSSerhiy Boiko 	struct prestera_flow_block_binding *binding;
1208afd552dSSerhiy Boiko 
1218afd552dSSerhiy Boiko 	list_for_each_entry(binding, &block->binding_list, list)
1228c448c2bSSerhiy Boiko 		prestera_span_rule_del(binding, block->ingress);
1238c448c2bSSerhiy Boiko 
12444af9571SMaksym Glubokiy 	block->mall.prio_min = UINT_MAX;
12544af9571SMaksym Glubokiy 	block->mall.prio_max = 0;
12644af9571SMaksym Glubokiy 	block->mall.bound = false;
1278afd552dSSerhiy Boiko }
128