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