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