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_flower.h"
11 #include "prestera_matchall.h"
12 #include "prestera_span.h"
14 static LIST_HEAD(prestera_block_cb_list);
16 static int prestera_flow_block_mall_cb(struct prestera_flow_block *block,
17 struct tc_cls_matchall_offload *f)
20 case TC_CLSMATCHALL_REPLACE:
21 return prestera_mall_replace(block, f);
22 case TC_CLSMATCHALL_DESTROY:
23 prestera_mall_destroy(block);
30 static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,
31 struct flow_cls_offload *f)
34 case FLOW_CLS_REPLACE:
35 return prestera_flower_replace(block, f);
36 case FLOW_CLS_DESTROY:
37 prestera_flower_destroy(block, f);
40 return prestera_flower_stats(block, f);
41 case FLOW_CLS_TMPLT_CREATE:
42 return prestera_flower_tmplt_create(block, f);
43 case FLOW_CLS_TMPLT_DESTROY:
44 prestera_flower_tmplt_destroy(block, f);
51 static int prestera_flow_block_cb(enum tc_setup_type type,
52 void *type_data, void *cb_priv)
54 struct prestera_flow_block *block = cb_priv;
57 case TC_SETUP_CLSFLOWER:
58 return prestera_flow_block_flower_cb(block, type_data);
59 case TC_SETUP_CLSMATCHALL:
60 return prestera_flow_block_mall_cb(block, type_data);
66 static void prestera_flow_block_destroy(void *cb_priv)
68 struct prestera_flow_block *block = cb_priv;
70 prestera_flower_template_cleanup(block);
72 WARN_ON(!list_empty(&block->template_list));
73 WARN_ON(!list_empty(&block->binding_list));
78 static struct prestera_flow_block *
79 prestera_flow_block_create(struct prestera_switch *sw,
83 struct prestera_flow_block *block;
85 block = kzalloc(sizeof(*block), GFP_KERNEL);
89 INIT_LIST_HEAD(&block->binding_list);
90 INIT_LIST_HEAD(&block->template_list);
93 block->mall.prio_min = UINT_MAX;
94 block->mall.prio_max = 0;
95 block->mall.bound = false;
96 block->ingress = ingress;
101 static void prestera_flow_block_release(void *cb_priv)
103 struct prestera_flow_block *block = cb_priv;
105 prestera_flow_block_destroy(block);
109 prestera_flow_block_is_bound(const struct prestera_flow_block *block)
111 return block->ruleset_zero;
114 static struct prestera_flow_block_binding *
115 prestera_flow_block_lookup(struct prestera_flow_block *block,
116 struct prestera_port *port)
118 struct prestera_flow_block_binding *binding;
120 list_for_each_entry(binding, &block->binding_list, list)
121 if (binding->port == port)
127 static int prestera_flow_block_bind(struct prestera_flow_block *block,
128 struct prestera_port *port)
130 struct prestera_flow_block_binding *binding;
133 binding = kzalloc(sizeof(*binding), GFP_KERNEL);
137 binding->span_id = PRESTERA_SPAN_INVALID_ID;
138 binding->port = port;
140 if (prestera_flow_block_is_bound(block)) {
141 err = prestera_acl_ruleset_bind(block->ruleset_zero, port);
143 goto err_ruleset_bind;
146 list_add(&binding->list, &block->binding_list);
154 static int prestera_flow_block_unbind(struct prestera_flow_block *block,
155 struct prestera_port *port)
157 struct prestera_flow_block_binding *binding;
159 binding = prestera_flow_block_lookup(block, port);
163 list_del(&binding->list);
165 if (prestera_flow_block_is_bound(block))
166 prestera_acl_ruleset_unbind(block->ruleset_zero, port);
172 static struct prestera_flow_block *
173 prestera_flow_block_get(struct prestera_switch *sw,
174 struct flow_block_offload *f,
175 bool *register_block,
178 struct prestera_flow_block *block;
179 struct flow_block_cb *block_cb;
181 block_cb = flow_block_cb_lookup(f->block,
182 prestera_flow_block_cb, sw);
184 block = prestera_flow_block_create(sw, f->net, ingress);
186 return ERR_PTR(-ENOMEM);
188 block_cb = flow_block_cb_alloc(prestera_flow_block_cb,
190 prestera_flow_block_release);
191 if (IS_ERR(block_cb)) {
192 prestera_flow_block_destroy(block);
193 return ERR_CAST(block_cb);
196 block->block_cb = block_cb;
197 *register_block = true;
199 block = flow_block_cb_priv(block_cb);
200 *register_block = false;
203 flow_block_cb_incref(block_cb);
208 static void prestera_flow_block_put(struct prestera_flow_block *block)
210 struct flow_block_cb *block_cb = block->block_cb;
212 if (flow_block_cb_decref(block_cb))
215 flow_block_cb_free(block_cb);
216 prestera_flow_block_destroy(block);
219 static int prestera_setup_flow_block_bind(struct prestera_port *port,
220 struct flow_block_offload *f, bool ingress)
222 struct prestera_switch *sw = port->sw;
223 struct prestera_flow_block *block;
224 struct flow_block_cb *block_cb;
228 block = prestera_flow_block_get(sw, f, ®ister_block, ingress);
230 return PTR_ERR(block);
232 block_cb = block->block_cb;
234 err = prestera_flow_block_bind(block, port);
238 if (register_block) {
239 flow_block_cb_add(block_cb, f);
240 list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);
244 port->ingress_flow_block = block;
246 port->egress_flow_block = block;
251 prestera_flow_block_put(block);
256 static void prestera_setup_flow_block_unbind(struct prestera_port *port,
257 struct flow_block_offload *f, bool ingress)
259 struct prestera_switch *sw = port->sw;
260 struct prestera_flow_block *block;
261 struct flow_block_cb *block_cb;
264 block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw);
268 block = flow_block_cb_priv(block_cb);
270 prestera_mall_destroy(block);
272 err = prestera_flow_block_unbind(block, port);
276 if (!flow_block_cb_decref(block_cb)) {
277 flow_block_cb_remove(block_cb, f);
278 list_del(&block_cb->driver_list);
282 port->ingress_flow_block = NULL;
284 port->egress_flow_block = NULL;
287 static int prestera_setup_flow_block_clsact(struct prestera_port *port,
288 struct flow_block_offload *f,
291 f->driver_block_list = &prestera_block_cb_list;
293 switch (f->command) {
294 case FLOW_BLOCK_BIND:
295 return prestera_setup_flow_block_bind(port, f, ingress);
296 case FLOW_BLOCK_UNBIND:
297 prestera_setup_flow_block_unbind(port, f, ingress);
304 int prestera_flow_block_setup(struct prestera_port *port,
305 struct flow_block_offload *f)
307 switch (f->binder_type) {
308 case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:
309 return prestera_setup_flow_block_clsact(port, f, true);
310 case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:
311 return prestera_setup_flow_block_clsact(port, f, false);