1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
4 #include <linux/kernel.h>
5 #include <linux/list.h>
8 #include "prestera_acl.h"
9 #include "prestera_flow.h"
10 #include "prestera_span.h"
11 #include "prestera_flower.h"
13 static LIST_HEAD(prestera_block_cb_list);
15 static int prestera_flow_block_mall_cb(struct prestera_flow_block *block,
16 struct tc_cls_matchall_offload *f)
19 case TC_CLSMATCHALL_REPLACE:
20 return prestera_span_replace(block, f);
21 case TC_CLSMATCHALL_DESTROY:
22 prestera_span_destroy(block);
29 static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,
30 struct flow_cls_offload *f)
33 case FLOW_CLS_REPLACE:
34 return prestera_flower_replace(block, f);
35 case FLOW_CLS_DESTROY:
36 prestera_flower_destroy(block, f);
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);
50 static int prestera_flow_block_cb(enum tc_setup_type type,
51 void *type_data, void *cb_priv)
53 struct prestera_flow_block *block = cb_priv;
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);
65 static void prestera_flow_block_destroy(void *cb_priv)
67 struct prestera_flow_block *block = cb_priv;
69 prestera_flower_template_cleanup(block);
71 WARN_ON(!list_empty(&block->template_list));
72 WARN_ON(!list_empty(&block->binding_list));
77 static struct prestera_flow_block *
78 prestera_flow_block_create(struct prestera_switch *sw, struct net *net)
80 struct prestera_flow_block *block;
82 block = kzalloc(sizeof(*block), GFP_KERNEL);
86 INIT_LIST_HEAD(&block->binding_list);
87 INIT_LIST_HEAD(&block->template_list);
94 static void prestera_flow_block_release(void *cb_priv)
96 struct prestera_flow_block *block = cb_priv;
98 prestera_flow_block_destroy(block);
102 prestera_flow_block_is_bound(const struct prestera_flow_block *block)
104 return block->ruleset_zero;
107 static struct prestera_flow_block_binding *
108 prestera_flow_block_lookup(struct prestera_flow_block *block,
109 struct prestera_port *port)
111 struct prestera_flow_block_binding *binding;
113 list_for_each_entry(binding, &block->binding_list, list)
114 if (binding->port == port)
120 static int prestera_flow_block_bind(struct prestera_flow_block *block,
121 struct prestera_port *port)
123 struct prestera_flow_block_binding *binding;
126 binding = kzalloc(sizeof(*binding), GFP_KERNEL);
130 binding->span_id = PRESTERA_SPAN_INVALID_ID;
131 binding->port = port;
133 if (prestera_flow_block_is_bound(block)) {
134 err = prestera_acl_ruleset_bind(block->ruleset_zero, port);
136 goto err_ruleset_bind;
139 list_add(&binding->list, &block->binding_list);
147 static int prestera_flow_block_unbind(struct prestera_flow_block *block,
148 struct prestera_port *port)
150 struct prestera_flow_block_binding *binding;
152 binding = prestera_flow_block_lookup(block, port);
156 list_del(&binding->list);
158 if (prestera_flow_block_is_bound(block))
159 prestera_acl_ruleset_unbind(block->ruleset_zero, port);
165 static struct prestera_flow_block *
166 prestera_flow_block_get(struct prestera_switch *sw,
167 struct flow_block_offload *f,
168 bool *register_block)
170 struct prestera_flow_block *block;
171 struct flow_block_cb *block_cb;
173 block_cb = flow_block_cb_lookup(f->block,
174 prestera_flow_block_cb, sw);
176 block = prestera_flow_block_create(sw, f->net);
178 return ERR_PTR(-ENOMEM);
180 block_cb = flow_block_cb_alloc(prestera_flow_block_cb,
182 prestera_flow_block_release);
183 if (IS_ERR(block_cb)) {
184 prestera_flow_block_destroy(block);
185 return ERR_CAST(block_cb);
188 block->block_cb = block_cb;
189 *register_block = true;
191 block = flow_block_cb_priv(block_cb);
192 *register_block = false;
195 flow_block_cb_incref(block_cb);
200 static void prestera_flow_block_put(struct prestera_flow_block *block)
202 struct flow_block_cb *block_cb = block->block_cb;
204 if (flow_block_cb_decref(block_cb))
207 flow_block_cb_free(block_cb);
208 prestera_flow_block_destroy(block);
211 static int prestera_setup_flow_block_bind(struct prestera_port *port,
212 struct flow_block_offload *f)
214 struct prestera_switch *sw = port->sw;
215 struct prestera_flow_block *block;
216 struct flow_block_cb *block_cb;
220 block = prestera_flow_block_get(sw, f, ®ister_block);
222 return PTR_ERR(block);
224 block_cb = block->block_cb;
226 err = prestera_flow_block_bind(block, port);
230 if (register_block) {
231 flow_block_cb_add(block_cb, f);
232 list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);
235 port->flow_block = block;
239 prestera_flow_block_put(block);
244 static void prestera_setup_flow_block_unbind(struct prestera_port *port,
245 struct flow_block_offload *f)
247 struct prestera_switch *sw = port->sw;
248 struct prestera_flow_block *block;
249 struct flow_block_cb *block_cb;
252 block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw);
256 block = flow_block_cb_priv(block_cb);
258 prestera_span_destroy(block);
260 err = prestera_flow_block_unbind(block, port);
264 if (!flow_block_cb_decref(block_cb)) {
265 flow_block_cb_remove(block_cb, f);
266 list_del(&block_cb->driver_list);
269 port->flow_block = NULL;
272 int prestera_flow_block_setup(struct prestera_port *port,
273 struct flow_block_offload *f)
275 if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
278 f->driver_block_list = &prestera_block_cb_list;
280 switch (f->command) {
281 case FLOW_BLOCK_BIND:
282 return prestera_setup_flow_block_bind(port, f);
283 case FLOW_BLOCK_UNBIND:
284 prestera_setup_flow_block_unbind(port, f);