1*13defa27SSerhiy Boiko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*13defa27SSerhiy Boiko /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
3*13defa27SSerhiy Boiko 
4*13defa27SSerhiy Boiko #include <linux/kernel.h>
5*13defa27SSerhiy Boiko #include <linux/list.h>
6*13defa27SSerhiy Boiko 
7*13defa27SSerhiy Boiko #include "prestera.h"
8*13defa27SSerhiy Boiko #include "prestera_hw.h"
9*13defa27SSerhiy Boiko #include "prestera_acl.h"
10*13defa27SSerhiy Boiko #include "prestera_span.h"
11*13defa27SSerhiy Boiko 
12*13defa27SSerhiy Boiko struct prestera_span_entry {
13*13defa27SSerhiy Boiko 	struct list_head list;
14*13defa27SSerhiy Boiko 	struct prestera_port *port;
15*13defa27SSerhiy Boiko 	refcount_t ref_count;
16*13defa27SSerhiy Boiko 	u8 id;
17*13defa27SSerhiy Boiko };
18*13defa27SSerhiy Boiko 
19*13defa27SSerhiy Boiko struct prestera_span {
20*13defa27SSerhiy Boiko 	struct prestera_switch *sw;
21*13defa27SSerhiy Boiko 	struct list_head entries;
22*13defa27SSerhiy Boiko };
23*13defa27SSerhiy Boiko 
24*13defa27SSerhiy Boiko static struct prestera_span_entry *
25*13defa27SSerhiy Boiko prestera_span_entry_create(struct prestera_port *port, u8 span_id)
26*13defa27SSerhiy Boiko {
27*13defa27SSerhiy Boiko 	struct prestera_span_entry *entry;
28*13defa27SSerhiy Boiko 
29*13defa27SSerhiy Boiko 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
30*13defa27SSerhiy Boiko 	if (!entry)
31*13defa27SSerhiy Boiko 		return ERR_PTR(-ENOMEM);
32*13defa27SSerhiy Boiko 
33*13defa27SSerhiy Boiko 	refcount_set(&entry->ref_count, 1);
34*13defa27SSerhiy Boiko 	entry->port = port;
35*13defa27SSerhiy Boiko 	entry->id = span_id;
36*13defa27SSerhiy Boiko 	list_add_tail(&entry->list, &port->sw->span->entries);
37*13defa27SSerhiy Boiko 
38*13defa27SSerhiy Boiko 	return entry;
39*13defa27SSerhiy Boiko }
40*13defa27SSerhiy Boiko 
41*13defa27SSerhiy Boiko static void prestera_span_entry_del(struct prestera_span_entry *entry)
42*13defa27SSerhiy Boiko {
43*13defa27SSerhiy Boiko 	list_del(&entry->list);
44*13defa27SSerhiy Boiko 	kfree(entry);
45*13defa27SSerhiy Boiko }
46*13defa27SSerhiy Boiko 
47*13defa27SSerhiy Boiko static struct prestera_span_entry *
48*13defa27SSerhiy Boiko prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
49*13defa27SSerhiy Boiko {
50*13defa27SSerhiy Boiko 	struct prestera_span_entry *entry;
51*13defa27SSerhiy Boiko 
52*13defa27SSerhiy Boiko 	list_for_each_entry(entry, &span->entries, list) {
53*13defa27SSerhiy Boiko 		if (entry->id == span_id)
54*13defa27SSerhiy Boiko 			return entry;
55*13defa27SSerhiy Boiko 	}
56*13defa27SSerhiy Boiko 
57*13defa27SSerhiy Boiko 	return NULL;
58*13defa27SSerhiy Boiko }
59*13defa27SSerhiy Boiko 
60*13defa27SSerhiy Boiko static struct prestera_span_entry *
61*13defa27SSerhiy Boiko prestera_span_entry_find_by_port(struct prestera_span *span,
62*13defa27SSerhiy Boiko 				 struct prestera_port *port)
63*13defa27SSerhiy Boiko {
64*13defa27SSerhiy Boiko 	struct prestera_span_entry *entry;
65*13defa27SSerhiy Boiko 
66*13defa27SSerhiy Boiko 	list_for_each_entry(entry, &span->entries, list) {
67*13defa27SSerhiy Boiko 		if (entry->port == port)
68*13defa27SSerhiy Boiko 			return entry;
69*13defa27SSerhiy Boiko 	}
70*13defa27SSerhiy Boiko 
71*13defa27SSerhiy Boiko 	return NULL;
72*13defa27SSerhiy Boiko }
73*13defa27SSerhiy Boiko 
74*13defa27SSerhiy Boiko static int prestera_span_get(struct prestera_port *port, u8 *span_id)
75*13defa27SSerhiy Boiko {
76*13defa27SSerhiy Boiko 	u8 new_span_id;
77*13defa27SSerhiy Boiko 	struct prestera_switch *sw = port->sw;
78*13defa27SSerhiy Boiko 	struct prestera_span_entry *entry;
79*13defa27SSerhiy Boiko 	int err;
80*13defa27SSerhiy Boiko 
81*13defa27SSerhiy Boiko 	entry = prestera_span_entry_find_by_port(sw->span, port);
82*13defa27SSerhiy Boiko 	if (entry) {
83*13defa27SSerhiy Boiko 		refcount_inc(&entry->ref_count);
84*13defa27SSerhiy Boiko 		*span_id = entry->id;
85*13defa27SSerhiy Boiko 		return 0;
86*13defa27SSerhiy Boiko 	}
87*13defa27SSerhiy Boiko 
88*13defa27SSerhiy Boiko 	err = prestera_hw_span_get(port, &new_span_id);
89*13defa27SSerhiy Boiko 	if (err)
90*13defa27SSerhiy Boiko 		return err;
91*13defa27SSerhiy Boiko 
92*13defa27SSerhiy Boiko 	entry = prestera_span_entry_create(port, new_span_id);
93*13defa27SSerhiy Boiko 	if (IS_ERR(entry)) {
94*13defa27SSerhiy Boiko 		prestera_hw_span_release(sw, new_span_id);
95*13defa27SSerhiy Boiko 		return PTR_ERR(entry);
96*13defa27SSerhiy Boiko 	}
97*13defa27SSerhiy Boiko 
98*13defa27SSerhiy Boiko 	*span_id = new_span_id;
99*13defa27SSerhiy Boiko 	return 0;
100*13defa27SSerhiy Boiko }
101*13defa27SSerhiy Boiko 
102*13defa27SSerhiy Boiko static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
103*13defa27SSerhiy Boiko {
104*13defa27SSerhiy Boiko 	struct prestera_span_entry *entry;
105*13defa27SSerhiy Boiko 	int err;
106*13defa27SSerhiy Boiko 
107*13defa27SSerhiy Boiko 	entry = prestera_span_entry_find_by_id(sw->span, span_id);
108*13defa27SSerhiy Boiko 	if (!entry)
109*13defa27SSerhiy Boiko 		return false;
110*13defa27SSerhiy Boiko 
111*13defa27SSerhiy Boiko 	if (!refcount_dec_and_test(&entry->ref_count))
112*13defa27SSerhiy Boiko 		return 0;
113*13defa27SSerhiy Boiko 
114*13defa27SSerhiy Boiko 	err = prestera_hw_span_release(sw, span_id);
115*13defa27SSerhiy Boiko 	if (err)
116*13defa27SSerhiy Boiko 		return err;
117*13defa27SSerhiy Boiko 
118*13defa27SSerhiy Boiko 	prestera_span_entry_del(entry);
119*13defa27SSerhiy Boiko 	return 0;
120*13defa27SSerhiy Boiko }
121*13defa27SSerhiy Boiko 
122*13defa27SSerhiy Boiko static int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
123*13defa27SSerhiy Boiko 				  struct prestera_port *to_port)
124*13defa27SSerhiy Boiko {
125*13defa27SSerhiy Boiko 	struct prestera_switch *sw = binding->port->sw;
126*13defa27SSerhiy Boiko 	u8 span_id;
127*13defa27SSerhiy Boiko 	int err;
128*13defa27SSerhiy Boiko 
129*13defa27SSerhiy Boiko 	if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
130*13defa27SSerhiy Boiko 		/* port already in mirroring */
131*13defa27SSerhiy Boiko 		return -EEXIST;
132*13defa27SSerhiy Boiko 
133*13defa27SSerhiy Boiko 	err = prestera_span_get(to_port, &span_id);
134*13defa27SSerhiy Boiko 	if (err)
135*13defa27SSerhiy Boiko 		return err;
136*13defa27SSerhiy Boiko 
137*13defa27SSerhiy Boiko 	err = prestera_hw_span_bind(binding->port, span_id);
138*13defa27SSerhiy Boiko 	if (err) {
139*13defa27SSerhiy Boiko 		prestera_span_put(sw, span_id);
140*13defa27SSerhiy Boiko 		return err;
141*13defa27SSerhiy Boiko 	}
142*13defa27SSerhiy Boiko 
143*13defa27SSerhiy Boiko 	binding->span_id = span_id;
144*13defa27SSerhiy Boiko 	return 0;
145*13defa27SSerhiy Boiko }
146*13defa27SSerhiy Boiko 
147*13defa27SSerhiy Boiko static int prestera_span_rule_del(struct prestera_flow_block_binding *binding)
148*13defa27SSerhiy Boiko {
149*13defa27SSerhiy Boiko 	int err;
150*13defa27SSerhiy Boiko 
151*13defa27SSerhiy Boiko 	err = prestera_hw_span_unbind(binding->port);
152*13defa27SSerhiy Boiko 	if (err)
153*13defa27SSerhiy Boiko 		return err;
154*13defa27SSerhiy Boiko 
155*13defa27SSerhiy Boiko 	err = prestera_span_put(binding->port->sw, binding->span_id);
156*13defa27SSerhiy Boiko 	if (err)
157*13defa27SSerhiy Boiko 		return err;
158*13defa27SSerhiy Boiko 
159*13defa27SSerhiy Boiko 	binding->span_id = PRESTERA_SPAN_INVALID_ID;
160*13defa27SSerhiy Boiko 	return 0;
161*13defa27SSerhiy Boiko }
162*13defa27SSerhiy Boiko 
163*13defa27SSerhiy Boiko int prestera_span_replace(struct prestera_flow_block *block,
164*13defa27SSerhiy Boiko 			  struct tc_cls_matchall_offload *f)
165*13defa27SSerhiy Boiko {
166*13defa27SSerhiy Boiko 	struct prestera_flow_block_binding *binding;
167*13defa27SSerhiy Boiko 	__be16 protocol = f->common.protocol;
168*13defa27SSerhiy Boiko 	struct flow_action_entry *act;
169*13defa27SSerhiy Boiko 	struct prestera_port *port;
170*13defa27SSerhiy Boiko 	int err;
171*13defa27SSerhiy Boiko 
172*13defa27SSerhiy Boiko 	if (!flow_offload_has_one_action(&f->rule->action)) {
173*13defa27SSerhiy Boiko 		NL_SET_ERR_MSG(f->common.extack,
174*13defa27SSerhiy Boiko 			       "Only singular actions are supported");
175*13defa27SSerhiy Boiko 		return -EOPNOTSUPP;
176*13defa27SSerhiy Boiko 	}
177*13defa27SSerhiy Boiko 
178*13defa27SSerhiy Boiko 	act = &f->rule->action.entries[0];
179*13defa27SSerhiy Boiko 
180*13defa27SSerhiy Boiko 	if (!prestera_netdev_check(act->dev)) {
181*13defa27SSerhiy Boiko 		NL_SET_ERR_MSG(f->common.extack,
182*13defa27SSerhiy Boiko 			       "Only Marvell Prestera port is supported");
183*13defa27SSerhiy Boiko 		return -EINVAL;
184*13defa27SSerhiy Boiko 	}
185*13defa27SSerhiy Boiko 	if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
186*13defa27SSerhiy Boiko 		return -EOPNOTSUPP;
187*13defa27SSerhiy Boiko 	if (act->id != FLOW_ACTION_MIRRED)
188*13defa27SSerhiy Boiko 		return -EOPNOTSUPP;
189*13defa27SSerhiy Boiko 	if (protocol != htons(ETH_P_ALL))
190*13defa27SSerhiy Boiko 		return -EOPNOTSUPP;
191*13defa27SSerhiy Boiko 
192*13defa27SSerhiy Boiko 	port = netdev_priv(act->dev);
193*13defa27SSerhiy Boiko 
194*13defa27SSerhiy Boiko 	list_for_each_entry(binding, &block->binding_list, list) {
195*13defa27SSerhiy Boiko 		err = prestera_span_rule_add(binding, port);
196*13defa27SSerhiy Boiko 		if (err)
197*13defa27SSerhiy Boiko 			goto rollback;
198*13defa27SSerhiy Boiko 	}
199*13defa27SSerhiy Boiko 
200*13defa27SSerhiy Boiko 	return 0;
201*13defa27SSerhiy Boiko 
202*13defa27SSerhiy Boiko rollback:
203*13defa27SSerhiy Boiko 	list_for_each_entry_continue_reverse(binding,
204*13defa27SSerhiy Boiko 					     &block->binding_list, list)
205*13defa27SSerhiy Boiko 		prestera_span_rule_del(binding);
206*13defa27SSerhiy Boiko 	return err;
207*13defa27SSerhiy Boiko }
208*13defa27SSerhiy Boiko 
209*13defa27SSerhiy Boiko void prestera_span_destroy(struct prestera_flow_block *block)
210*13defa27SSerhiy Boiko {
211*13defa27SSerhiy Boiko 	struct prestera_flow_block_binding *binding;
212*13defa27SSerhiy Boiko 
213*13defa27SSerhiy Boiko 	list_for_each_entry(binding, &block->binding_list, list)
214*13defa27SSerhiy Boiko 		prestera_span_rule_del(binding);
215*13defa27SSerhiy Boiko }
216*13defa27SSerhiy Boiko 
217*13defa27SSerhiy Boiko int prestera_span_init(struct prestera_switch *sw)
218*13defa27SSerhiy Boiko {
219*13defa27SSerhiy Boiko 	struct prestera_span *span;
220*13defa27SSerhiy Boiko 
221*13defa27SSerhiy Boiko 	span = kzalloc(sizeof(*span), GFP_KERNEL);
222*13defa27SSerhiy Boiko 	if (!span)
223*13defa27SSerhiy Boiko 		return -ENOMEM;
224*13defa27SSerhiy Boiko 
225*13defa27SSerhiy Boiko 	INIT_LIST_HEAD(&span->entries);
226*13defa27SSerhiy Boiko 
227*13defa27SSerhiy Boiko 	sw->span = span;
228*13defa27SSerhiy Boiko 	span->sw = sw;
229*13defa27SSerhiy Boiko 
230*13defa27SSerhiy Boiko 	return 0;
231*13defa27SSerhiy Boiko }
232*13defa27SSerhiy Boiko 
233*13defa27SSerhiy Boiko void prestera_span_fini(struct prestera_switch *sw)
234*13defa27SSerhiy Boiko {
235*13defa27SSerhiy Boiko 	struct prestera_span *span = sw->span;
236*13defa27SSerhiy Boiko 
237*13defa27SSerhiy Boiko 	WARN_ON(!list_empty(&span->entries));
238*13defa27SSerhiy Boiko 	kfree(span);
239*13defa27SSerhiy Boiko }
240