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_acl.h"
9 #include "prestera_flow.h"
10 #include "prestera_span.h"
11 #include "prestera_flower.h"
12 
13 static LIST_HEAD(prestera_block_cb_list);
14 
15 static int prestera_flow_block_mall_cb(struct prestera_flow_block *block,
16 				       struct tc_cls_matchall_offload *f)
17 {
18 	switch (f->command) {
19 	case TC_CLSMATCHALL_REPLACE:
20 		return prestera_span_replace(block, f);
21 	case TC_CLSMATCHALL_DESTROY:
22 		prestera_span_destroy(block);
23 		return 0;
24 	default:
25 		return -EOPNOTSUPP;
26 	}
27 }
28 
29 static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,
30 					 struct flow_cls_offload *f)
31 {
32 	switch (f->command) {
33 	case FLOW_CLS_REPLACE:
34 		return prestera_flower_replace(block, f);
35 	case FLOW_CLS_DESTROY:
36 		prestera_flower_destroy(block, f);
37 		return 0;
38 	case FLOW_CLS_STATS:
39 		return prestera_flower_stats(block, f);
40 	case FLOW_CLS_TMPLT_CREATE:
41 		return prestera_flower_tmplt_create(block, f);
42 	case FLOW_CLS_TMPLT_DESTROY:
43 		prestera_flower_tmplt_destroy(block, f);
44 		return 0;
45 	default:
46 		return -EOPNOTSUPP;
47 	}
48 }
49 
50 static int prestera_flow_block_cb(enum tc_setup_type type,
51 				  void *type_data, void *cb_priv)
52 {
53 	struct prestera_flow_block *block = cb_priv;
54 
55 	switch (type) {
56 	case TC_SETUP_CLSFLOWER:
57 		return prestera_flow_block_flower_cb(block, type_data);
58 	case TC_SETUP_CLSMATCHALL:
59 		return prestera_flow_block_mall_cb(block, type_data);
60 	default:
61 		return -EOPNOTSUPP;
62 	}
63 }
64 
65 static void prestera_flow_block_destroy(void *cb_priv)
66 {
67 	struct prestera_flow_block *block = cb_priv;
68 
69 	prestera_flower_template_cleanup(block);
70 
71 	WARN_ON(!list_empty(&block->template_list));
72 	WARN_ON(!list_empty(&block->binding_list));
73 
74 	kfree(block);
75 }
76 
77 static struct prestera_flow_block *
78 prestera_flow_block_create(struct prestera_switch *sw,
79 			   struct net *net,
80 			   bool ingress)
81 {
82 	struct prestera_flow_block *block;
83 
84 	block = kzalloc(sizeof(*block), GFP_KERNEL);
85 	if (!block)
86 		return NULL;
87 
88 	INIT_LIST_HEAD(&block->binding_list);
89 	INIT_LIST_HEAD(&block->template_list);
90 	block->net = net;
91 	block->sw = sw;
92 	block->ingress = ingress;
93 
94 	return block;
95 }
96 
97 static void prestera_flow_block_release(void *cb_priv)
98 {
99 	struct prestera_flow_block *block = cb_priv;
100 
101 	prestera_flow_block_destroy(block);
102 }
103 
104 static bool
105 prestera_flow_block_is_bound(const struct prestera_flow_block *block)
106 {
107 	return block->ruleset_zero;
108 }
109 
110 static struct prestera_flow_block_binding *
111 prestera_flow_block_lookup(struct prestera_flow_block *block,
112 			   struct prestera_port *port)
113 {
114 	struct prestera_flow_block_binding *binding;
115 
116 	list_for_each_entry(binding, &block->binding_list, list)
117 		if (binding->port == port)
118 			return binding;
119 
120 	return NULL;
121 }
122 
123 static int prestera_flow_block_bind(struct prestera_flow_block *block,
124 				    struct prestera_port *port)
125 {
126 	struct prestera_flow_block_binding *binding;
127 	int err;
128 
129 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
130 	if (!binding)
131 		return -ENOMEM;
132 
133 	binding->span_id = PRESTERA_SPAN_INVALID_ID;
134 	binding->port = port;
135 
136 	if (prestera_flow_block_is_bound(block)) {
137 		err = prestera_acl_ruleset_bind(block->ruleset_zero, port);
138 		if (err)
139 			goto err_ruleset_bind;
140 	}
141 
142 	list_add(&binding->list, &block->binding_list);
143 	return 0;
144 
145 err_ruleset_bind:
146 	kfree(binding);
147 	return err;
148 }
149 
150 static int prestera_flow_block_unbind(struct prestera_flow_block *block,
151 				      struct prestera_port *port)
152 {
153 	struct prestera_flow_block_binding *binding;
154 
155 	binding = prestera_flow_block_lookup(block, port);
156 	if (!binding)
157 		return -ENOENT;
158 
159 	list_del(&binding->list);
160 
161 	if (prestera_flow_block_is_bound(block))
162 		prestera_acl_ruleset_unbind(block->ruleset_zero, port);
163 
164 	kfree(binding);
165 	return 0;
166 }
167 
168 static struct prestera_flow_block *
169 prestera_flow_block_get(struct prestera_switch *sw,
170 			struct flow_block_offload *f,
171 			bool *register_block,
172 			bool ingress)
173 {
174 	struct prestera_flow_block *block;
175 	struct flow_block_cb *block_cb;
176 
177 	block_cb = flow_block_cb_lookup(f->block,
178 					prestera_flow_block_cb, sw);
179 	if (!block_cb) {
180 		block = prestera_flow_block_create(sw, f->net, ingress);
181 		if (!block)
182 			return ERR_PTR(-ENOMEM);
183 
184 		block_cb = flow_block_cb_alloc(prestera_flow_block_cb,
185 					       sw, block,
186 					       prestera_flow_block_release);
187 		if (IS_ERR(block_cb)) {
188 			prestera_flow_block_destroy(block);
189 			return ERR_CAST(block_cb);
190 		}
191 
192 		block->block_cb = block_cb;
193 		*register_block = true;
194 	} else {
195 		block = flow_block_cb_priv(block_cb);
196 		*register_block = false;
197 	}
198 
199 	flow_block_cb_incref(block_cb);
200 
201 	return block;
202 }
203 
204 static void prestera_flow_block_put(struct prestera_flow_block *block)
205 {
206 	struct flow_block_cb *block_cb = block->block_cb;
207 
208 	if (flow_block_cb_decref(block_cb))
209 		return;
210 
211 	flow_block_cb_free(block_cb);
212 	prestera_flow_block_destroy(block);
213 }
214 
215 static int prestera_setup_flow_block_bind(struct prestera_port *port,
216 					  struct flow_block_offload *f, bool ingress)
217 {
218 	struct prestera_switch *sw = port->sw;
219 	struct prestera_flow_block *block;
220 	struct flow_block_cb *block_cb;
221 	bool register_block;
222 	int err;
223 
224 	block = prestera_flow_block_get(sw, f, &register_block, ingress);
225 	if (IS_ERR(block))
226 		return PTR_ERR(block);
227 
228 	block_cb = block->block_cb;
229 
230 	err = prestera_flow_block_bind(block, port);
231 	if (err)
232 		goto err_block_bind;
233 
234 	if (register_block) {
235 		flow_block_cb_add(block_cb, f);
236 		list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);
237 	}
238 
239 	if (ingress)
240 		port->ingress_flow_block = block;
241 	else
242 		port->egress_flow_block = block;
243 
244 	return 0;
245 
246 err_block_bind:
247 	prestera_flow_block_put(block);
248 
249 	return err;
250 }
251 
252 static void prestera_setup_flow_block_unbind(struct prestera_port *port,
253 					     struct flow_block_offload *f, bool ingress)
254 {
255 	struct prestera_switch *sw = port->sw;
256 	struct prestera_flow_block *block;
257 	struct flow_block_cb *block_cb;
258 	int err;
259 
260 	block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw);
261 	if (!block_cb)
262 		return;
263 
264 	block = flow_block_cb_priv(block_cb);
265 
266 	prestera_span_destroy(block);
267 
268 	err = prestera_flow_block_unbind(block, port);
269 	if (err)
270 		goto error;
271 
272 	if (!flow_block_cb_decref(block_cb)) {
273 		flow_block_cb_remove(block_cb, f);
274 		list_del(&block_cb->driver_list);
275 	}
276 error:
277 	if (ingress)
278 		port->ingress_flow_block = NULL;
279 	else
280 		port->egress_flow_block = NULL;
281 }
282 
283 static int prestera_setup_flow_block_clsact(struct prestera_port *port,
284 					    struct flow_block_offload *f,
285 					    bool ingress)
286 {
287 	f->driver_block_list = &prestera_block_cb_list;
288 
289 	switch (f->command) {
290 	case FLOW_BLOCK_BIND:
291 		return prestera_setup_flow_block_bind(port, f, ingress);
292 	case FLOW_BLOCK_UNBIND:
293 		prestera_setup_flow_block_unbind(port, f, ingress);
294 		return 0;
295 	default:
296 		return -EOPNOTSUPP;
297 	}
298 }
299 
300 int prestera_flow_block_setup(struct prestera_port *port,
301 			      struct flow_block_offload *f)
302 {
303 	switch (f->binder_type) {
304 	case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
305 		return prestera_setup_flow_block_clsact(port, f, true);
306 	case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
307 		return prestera_setup_flow_block_clsact(port, f, false);
308 	default:
309 		return -EOPNOTSUPP;
310 	}
311 }
312