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