Merge tag 'aspeed-5.4-devicetree' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mscc / ocelot_flower.c
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch driver
3  * Copyright (c) 2019 Microsemi Corporation
4  */
5
6 #include <net/pkt_cls.h>
7 #include <net/tc_act/tc_gact.h>
8
9 #include "ocelot_ace.h"
10
11 struct ocelot_port_block {
12         struct ocelot_acl_block *block;
13         struct ocelot_port *port;
14 };
15
16 static u16 get_prio(u32 prio)
17 {
18         /* prio starts from 0x1000 while the ids starts from 0 */
19         return prio >> 16;
20 }
21
22 static int ocelot_flower_parse_action(struct flow_cls_offload *f,
23                                       struct ocelot_ace_rule *rule)
24 {
25         const struct flow_action_entry *a;
26         int i;
27
28         if (f->rule->action.num_entries != 1)
29                 return -EOPNOTSUPP;
30
31         flow_action_for_each(i, a, &f->rule->action) {
32                 switch (a->id) {
33                 case FLOW_ACTION_DROP:
34                         rule->action = OCELOT_ACL_ACTION_DROP;
35                         break;
36                 case FLOW_ACTION_TRAP:
37                         rule->action = OCELOT_ACL_ACTION_TRAP;
38                         break;
39                 default:
40                         return -EOPNOTSUPP;
41                 }
42         }
43
44         return 0;
45 }
46
47 static int ocelot_flower_parse(struct flow_cls_offload *f,
48                                struct ocelot_ace_rule *ocelot_rule)
49 {
50         struct flow_rule *rule = flow_cls_offload_flow_rule(f);
51         struct flow_dissector *dissector = rule->match.dissector;
52
53         if (dissector->used_keys &
54             ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
55               BIT(FLOW_DISSECTOR_KEY_BASIC) |
56               BIT(FLOW_DISSECTOR_KEY_PORTS) |
57               BIT(FLOW_DISSECTOR_KEY_VLAN) |
58               BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
59               BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
60               BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
61                 return -EOPNOTSUPP;
62         }
63
64         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
65                 struct flow_match_control match;
66
67                 flow_rule_match_control(rule, &match);
68         }
69
70         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
71                 struct flow_match_eth_addrs match;
72                 u16 proto = ntohs(f->common.protocol);
73
74                 /* The hw support mac matches only for MAC_ETYPE key,
75                  * therefore if other matches(port, tcp flags, etc) are added
76                  * then just bail out
77                  */
78                 if ((dissector->used_keys &
79                     (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
80                      BIT(FLOW_DISSECTOR_KEY_BASIC) |
81                      BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
82                     (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
83                      BIT(FLOW_DISSECTOR_KEY_BASIC) |
84                      BIT(FLOW_DISSECTOR_KEY_CONTROL)))
85                         return -EOPNOTSUPP;
86
87                 if (proto == ETH_P_IP ||
88                     proto == ETH_P_IPV6 ||
89                     proto == ETH_P_ARP)
90                         return -EOPNOTSUPP;
91
92                 flow_rule_match_eth_addrs(rule, &match);
93                 ocelot_rule->type = OCELOT_ACE_TYPE_ETYPE;
94                 ether_addr_copy(ocelot_rule->frame.etype.dmac.value,
95                                 match.key->dst);
96                 ether_addr_copy(ocelot_rule->frame.etype.smac.value,
97                                 match.key->src);
98                 ether_addr_copy(ocelot_rule->frame.etype.dmac.mask,
99                                 match.mask->dst);
100                 ether_addr_copy(ocelot_rule->frame.etype.smac.mask,
101                                 match.mask->src);
102                 goto finished_key_parsing;
103         }
104
105         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
106                 struct flow_match_basic match;
107
108                 flow_rule_match_basic(rule, &match);
109                 if (ntohs(match.key->n_proto) == ETH_P_IP) {
110                         ocelot_rule->type = OCELOT_ACE_TYPE_IPV4;
111                         ocelot_rule->frame.ipv4.proto.value[0] =
112                                 match.key->ip_proto;
113                         ocelot_rule->frame.ipv4.proto.mask[0] =
114                                 match.mask->ip_proto;
115                 }
116                 if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
117                         ocelot_rule->type = OCELOT_ACE_TYPE_IPV6;
118                         ocelot_rule->frame.ipv6.proto.value[0] =
119                                 match.key->ip_proto;
120                         ocelot_rule->frame.ipv6.proto.mask[0] =
121                                 match.mask->ip_proto;
122                 }
123         }
124
125         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
126             ntohs(f->common.protocol) == ETH_P_IP) {
127                 struct flow_match_ipv4_addrs match;
128                 u8 *tmp;
129
130                 flow_rule_match_ipv4_addrs(rule, &match);
131                 tmp = &ocelot_rule->frame.ipv4.sip.value.addr[0];
132                 memcpy(tmp, &match.key->src, 4);
133
134                 tmp = &ocelot_rule->frame.ipv4.sip.mask.addr[0];
135                 memcpy(tmp, &match.mask->src, 4);
136
137                 tmp = &ocelot_rule->frame.ipv4.dip.value.addr[0];
138                 memcpy(tmp, &match.key->dst, 4);
139
140                 tmp = &ocelot_rule->frame.ipv4.dip.mask.addr[0];
141                 memcpy(tmp, &match.mask->dst, 4);
142         }
143
144         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
145             ntohs(f->common.protocol) == ETH_P_IPV6) {
146                 return -EOPNOTSUPP;
147         }
148
149         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
150                 struct flow_match_ports match;
151
152                 flow_rule_match_ports(rule, &match);
153                 ocelot_rule->frame.ipv4.sport.value = ntohs(match.key->src);
154                 ocelot_rule->frame.ipv4.sport.mask = ntohs(match.mask->src);
155                 ocelot_rule->frame.ipv4.dport.value = ntohs(match.key->dst);
156                 ocelot_rule->frame.ipv4.dport.mask = ntohs(match.mask->dst);
157         }
158
159         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
160                 struct flow_match_vlan match;
161
162                 flow_rule_match_vlan(rule, &match);
163                 ocelot_rule->type = OCELOT_ACE_TYPE_ANY;
164                 ocelot_rule->vlan.vid.value = match.key->vlan_id;
165                 ocelot_rule->vlan.vid.mask = match.mask->vlan_id;
166                 ocelot_rule->vlan.pcp.value[0] = match.key->vlan_priority;
167                 ocelot_rule->vlan.pcp.mask[0] = match.mask->vlan_priority;
168         }
169
170 finished_key_parsing:
171         ocelot_rule->prio = get_prio(f->common.prio);
172         ocelot_rule->id = f->cookie;
173         return ocelot_flower_parse_action(f, ocelot_rule);
174 }
175
176 static
177 struct ocelot_ace_rule *ocelot_ace_rule_create(struct flow_cls_offload *f,
178                                                struct ocelot_port_block *block)
179 {
180         struct ocelot_ace_rule *rule;
181
182         rule = kzalloc(sizeof(*rule), GFP_KERNEL);
183         if (!rule)
184                 return NULL;
185
186         rule->port = block->port;
187         rule->chip_port = block->port->chip_port;
188         return rule;
189 }
190
191 static int ocelot_flower_replace(struct flow_cls_offload *f,
192                                  struct ocelot_port_block *port_block)
193 {
194         struct ocelot_ace_rule *rule;
195         int ret;
196
197         rule = ocelot_ace_rule_create(f, port_block);
198         if (!rule)
199                 return -ENOMEM;
200
201         ret = ocelot_flower_parse(f, rule);
202         if (ret) {
203                 kfree(rule);
204                 return ret;
205         }
206
207         ret = ocelot_ace_rule_offload_add(rule);
208         if (ret)
209                 return ret;
210
211         port_block->port->tc.offload_cnt++;
212         return 0;
213 }
214
215 static int ocelot_flower_destroy(struct flow_cls_offload *f,
216                                  struct ocelot_port_block *port_block)
217 {
218         struct ocelot_ace_rule rule;
219         int ret;
220
221         rule.prio = get_prio(f->common.prio);
222         rule.port = port_block->port;
223         rule.id = f->cookie;
224
225         ret = ocelot_ace_rule_offload_del(&rule);
226         if (ret)
227                 return ret;
228
229         port_block->port->tc.offload_cnt--;
230         return 0;
231 }
232
233 static int ocelot_flower_stats_update(struct flow_cls_offload *f,
234                                       struct ocelot_port_block *port_block)
235 {
236         struct ocelot_ace_rule rule;
237         int ret;
238
239         rule.prio = get_prio(f->common.prio);
240         rule.port = port_block->port;
241         rule.id = f->cookie;
242         ret = ocelot_ace_rule_stats_update(&rule);
243         if (ret)
244                 return ret;
245
246         flow_stats_update(&f->stats, 0x0, rule.stats.pkts, 0x0);
247         return 0;
248 }
249
250 static int ocelot_setup_tc_cls_flower(struct flow_cls_offload *f,
251                                       struct ocelot_port_block *port_block)
252 {
253         switch (f->command) {
254         case FLOW_CLS_REPLACE:
255                 return ocelot_flower_replace(f, port_block);
256         case FLOW_CLS_DESTROY:
257                 return ocelot_flower_destroy(f, port_block);
258         case FLOW_CLS_STATS:
259                 return ocelot_flower_stats_update(f, port_block);
260         default:
261                 return -EOPNOTSUPP;
262         }
263 }
264
265 static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type,
266                                            void *type_data, void *cb_priv)
267 {
268         struct ocelot_port_block *port_block = cb_priv;
269
270         if (!tc_cls_can_offload_and_chain0(port_block->port->dev, type_data))
271                 return -EOPNOTSUPP;
272
273         switch (type) {
274         case TC_SETUP_CLSFLOWER:
275                 return ocelot_setup_tc_cls_flower(type_data, cb_priv);
276         case TC_SETUP_CLSMATCHALL:
277                 return 0;
278         default:
279                 return -EOPNOTSUPP;
280         }
281 }
282
283 static struct ocelot_port_block*
284 ocelot_port_block_create(struct ocelot_port *port)
285 {
286         struct ocelot_port_block *port_block;
287
288         port_block = kzalloc(sizeof(*port_block), GFP_KERNEL);
289         if (!port_block)
290                 return NULL;
291
292         port_block->port = port;
293
294         return port_block;
295 }
296
297 static void ocelot_port_block_destroy(struct ocelot_port_block *block)
298 {
299         kfree(block);
300 }
301
302 static void ocelot_tc_block_unbind(void *cb_priv)
303 {
304         struct ocelot_port_block *port_block = cb_priv;
305
306         ocelot_port_block_destroy(port_block);
307 }
308
309 int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port,
310                                       struct flow_block_offload *f)
311 {
312         struct ocelot_port_block *port_block;
313         struct flow_block_cb *block_cb;
314         int ret;
315
316         if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
317                 return -EOPNOTSUPP;
318
319         block_cb = flow_block_cb_lookup(f->block,
320                                         ocelot_setup_tc_block_cb_flower, port);
321         if (!block_cb) {
322                 port_block = ocelot_port_block_create(port);
323                 if (!port_block)
324                         return -ENOMEM;
325
326                 block_cb = flow_block_cb_alloc(ocelot_setup_tc_block_cb_flower,
327                                                port, port_block,
328                                                ocelot_tc_block_unbind);
329                 if (IS_ERR(block_cb)) {
330                         ret = PTR_ERR(block_cb);
331                         goto err_cb_register;
332                 }
333                 flow_block_cb_add(block_cb, f);
334                 list_add_tail(&block_cb->driver_list, f->driver_block_list);
335         } else {
336                 port_block = flow_block_cb_priv(block_cb);
337         }
338
339         flow_block_cb_incref(block_cb);
340         return 0;
341
342 err_cb_register:
343         ocelot_port_block_destroy(port_block);
344
345         return ret;
346 }
347
348 void ocelot_setup_tc_block_flower_unbind(struct ocelot_port *port,
349                                          struct flow_block_offload *f)
350 {
351         struct flow_block_cb *block_cb;
352
353         block_cb = flow_block_cb_lookup(f->block,
354                                         ocelot_setup_tc_block_cb_flower, port);
355         if (!block_cb)
356                 return;
357
358         if (!flow_block_cb_decref(block_cb)) {
359                 flow_block_cb_remove(block_cb, f);
360                 list_del(&block_cb->driver_list);
361         }
362 }