xref: /linux/drivers/net/ethernet/marvell/prestera/prestera_matchall.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2019-2022 Marvell International Ltd. All rights reserved */
3 
4 #include <linux/kernel.h>
5 #include <linux/list.h>
6 
7 #include "prestera.h"
8 #include "prestera_hw.h"
9 #include "prestera_flow.h"
10 #include "prestera_flower.h"
11 #include "prestera_matchall.h"
12 #include "prestera_span.h"
13 
prestera_mall_prio_check(struct prestera_flow_block * block,struct tc_cls_matchall_offload * f)14 static int prestera_mall_prio_check(struct prestera_flow_block *block,
15 				    struct tc_cls_matchall_offload *f)
16 {
17 	u32 flower_prio_min;
18 	u32 flower_prio_max;
19 	int err;
20 
21 	err = prestera_flower_prio_get(block, f->common.chain_index,
22 				       &flower_prio_min, &flower_prio_max);
23 	if (err == -ENOENT)
24 		/* No flower filters installed on this chain. */
25 		return 0;
26 
27 	if (err) {
28 		NL_SET_ERR_MSG(f->common.extack, "Failed to get flower priorities");
29 		return err;
30 	}
31 
32 	if (f->common.prio <= flower_prio_max && !block->ingress) {
33 		NL_SET_ERR_MSG(f->common.extack, "Failed to add in front of existing flower rules");
34 		return -EOPNOTSUPP;
35 	}
36 	if (f->common.prio >= flower_prio_min && block->ingress) {
37 		NL_SET_ERR_MSG(f->common.extack, "Failed to add behind of existing flower rules");
38 		return -EOPNOTSUPP;
39 	}
40 
41 	return 0;
42 }
43 
prestera_mall_prio_get(struct prestera_flow_block * block,u32 * prio_min,u32 * prio_max)44 int prestera_mall_prio_get(struct prestera_flow_block *block,
45 			   u32 *prio_min, u32 *prio_max)
46 {
47 	if (!block->mall.bound)
48 		return -ENOENT;
49 
50 	*prio_min = block->mall.prio_min;
51 	*prio_max = block->mall.prio_max;
52 	return 0;
53 }
54 
prestera_mall_prio_update(struct prestera_flow_block * block,struct tc_cls_matchall_offload * f)55 static void prestera_mall_prio_update(struct prestera_flow_block *block,
56 				      struct tc_cls_matchall_offload *f)
57 {
58 	block->mall.prio_min = min(block->mall.prio_min, f->common.prio);
59 	block->mall.prio_max = max(block->mall.prio_max, f->common.prio);
60 }
61 
prestera_mall_replace(struct prestera_flow_block * block,struct tc_cls_matchall_offload * f)62 int prestera_mall_replace(struct prestera_flow_block *block,
63 			  struct tc_cls_matchall_offload *f)
64 {
65 	struct prestera_flow_block_binding *binding;
66 	__be16 protocol = f->common.protocol;
67 	struct flow_action_entry *act;
68 	struct prestera_port *port;
69 	int err;
70 
71 	if (!flow_offload_has_one_action(&f->rule->action)) {
72 		NL_SET_ERR_MSG(f->common.extack,
73 			       "Only singular actions are supported");
74 		return -EOPNOTSUPP;
75 	}
76 
77 	act = &f->rule->action.entries[0];
78 
79 	if (!prestera_netdev_check(act->dev)) {
80 		NL_SET_ERR_MSG(f->common.extack,
81 			       "Only Marvell Prestera port is supported");
82 		return -EINVAL;
83 	}
84 	if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
85 		return -EOPNOTSUPP;
86 	if (act->id != FLOW_ACTION_MIRRED)
87 		return -EOPNOTSUPP;
88 	if (protocol != htons(ETH_P_ALL))
89 		return -EOPNOTSUPP;
90 
91 	err = prestera_mall_prio_check(block, f);
92 	if (err)
93 		return err;
94 
95 	port = netdev_priv(act->dev);
96 
97 	list_for_each_entry(binding, &block->binding_list, list) {
98 		err = prestera_span_rule_add(binding, port, block->ingress);
99 		if (err == -EEXIST)
100 			return err;
101 		if (err)
102 			goto rollback;
103 	}
104 
105 	prestera_mall_prio_update(block, f);
106 
107 	block->mall.bound = true;
108 	return 0;
109 
110 rollback:
111 	list_for_each_entry_continue_reverse(binding,
112 					     &block->binding_list, list)
113 		prestera_span_rule_del(binding, block->ingress);
114 	return err;
115 }
116 
prestera_mall_destroy(struct prestera_flow_block * block)117 void prestera_mall_destroy(struct prestera_flow_block *block)
118 {
119 	struct prestera_flow_block_binding *binding;
120 
121 	list_for_each_entry(binding, &block->binding_list, list)
122 		prestera_span_rule_del(binding, block->ingress);
123 
124 	block->mall.prio_min = UINT_MAX;
125 	block->mall.prio_max = 0;
126 	block->mall.bound = false;
127 }
128