GNU Linux-libre 5.10.153-gnu1
[releases.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 #include <soc/mscc/ocelot_vcap.h>
9 #include "ocelot_vcap.h"
10
11 /* Arbitrarily chosen constants for encoding the VCAP block and lookup number
12  * into the chain number. This is UAPI.
13  */
14 #define VCAP_BLOCK                      10000
15 #define VCAP_LOOKUP                     1000
16 #define VCAP_IS1_NUM_LOOKUPS            3
17 #define VCAP_IS2_NUM_LOOKUPS            2
18 #define VCAP_IS2_NUM_PAG                256
19 #define VCAP_IS1_CHAIN(lookup)          \
20         (1 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP)
21 #define VCAP_IS2_CHAIN(lookup, pag)     \
22         (2 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP + (pag))
23
24 static int ocelot_chain_to_block(int chain, bool ingress)
25 {
26         int lookup, pag;
27
28         if (!ingress) {
29                 if (chain == 0)
30                         return VCAP_ES0;
31                 return -EOPNOTSUPP;
32         }
33
34         /* Backwards compatibility with older, single-chain tc-flower
35          * offload support in Ocelot
36          */
37         if (chain == 0)
38                 return VCAP_IS2;
39
40         for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)
41                 if (chain == VCAP_IS1_CHAIN(lookup))
42                         return VCAP_IS1;
43
44         for (lookup = 0; lookup < VCAP_IS2_NUM_LOOKUPS; lookup++)
45                 for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
46                         if (chain == VCAP_IS2_CHAIN(lookup, pag))
47                                 return VCAP_IS2;
48
49         return -EOPNOTSUPP;
50 }
51
52 /* Caller must ensure this is a valid IS1 or IS2 chain first,
53  * by calling ocelot_chain_to_block.
54  */
55 static int ocelot_chain_to_lookup(int chain)
56 {
57         /* Backwards compatibility with older, single-chain tc-flower
58          * offload support in Ocelot
59          */
60         if (chain == 0)
61                 return 0;
62
63         return (chain / VCAP_LOOKUP) % 10;
64 }
65
66 /* Caller must ensure this is a valid IS2 chain first,
67  * by calling ocelot_chain_to_block.
68  */
69 static int ocelot_chain_to_pag(int chain)
70 {
71         int lookup;
72
73         /* Backwards compatibility with older, single-chain tc-flower
74          * offload support in Ocelot
75          */
76         if (chain == 0)
77                 return 0;
78
79         lookup = ocelot_chain_to_lookup(chain);
80
81         /* calculate PAG value as chain index relative to the first PAG */
82         return chain - VCAP_IS2_CHAIN(lookup, 0);
83 }
84
85 static bool ocelot_is_goto_target_valid(int goto_target, int chain,
86                                         bool ingress)
87 {
88         int pag;
89
90         /* Can't offload GOTO in VCAP ES0 */
91         if (!ingress)
92                 return (goto_target < 0);
93
94         /* Non-optional GOTOs */
95         if (chain == 0)
96                 /* VCAP IS1 can be skipped, either partially or completely */
97                 return (goto_target == VCAP_IS1_CHAIN(0) ||
98                         goto_target == VCAP_IS1_CHAIN(1) ||
99                         goto_target == VCAP_IS1_CHAIN(2) ||
100                         goto_target == VCAP_IS2_CHAIN(0, 0) ||
101                         goto_target == VCAP_IS2_CHAIN(1, 0));
102
103         if (chain == VCAP_IS1_CHAIN(0))
104                 return (goto_target == VCAP_IS1_CHAIN(1));
105
106         if (chain == VCAP_IS1_CHAIN(1))
107                 return (goto_target == VCAP_IS1_CHAIN(2));
108
109         /* Lookup 2 of VCAP IS1 can really support non-optional GOTOs,
110          * using a Policy Association Group (PAG) value, which is an 8-bit
111          * value encoding a VCAP IS2 target chain.
112          */
113         if (chain == VCAP_IS1_CHAIN(2)) {
114                 for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
115                         if (goto_target == VCAP_IS2_CHAIN(0, pag))
116                                 return true;
117
118                 return false;
119         }
120
121         /* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.
122          * We cannot change the PAG at this point.
123          */
124         for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
125                 if (chain == VCAP_IS2_CHAIN(0, pag))
126                         return (goto_target == VCAP_IS2_CHAIN(1, pag));
127
128         /* VCAP IS2 lookup 1 cannot jump anywhere */
129         return false;
130 }
131
132 static struct ocelot_vcap_filter *
133 ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
134 {
135         struct ocelot_vcap_filter *filter;
136         struct ocelot_vcap_block *block;
137         int block_id;
138
139         block_id = ocelot_chain_to_block(chain, true);
140         if (block_id < 0)
141                 return NULL;
142
143         if (block_id == VCAP_IS2) {
144                 block = &ocelot->block[VCAP_IS1];
145
146                 list_for_each_entry(filter, &block->rules, list)
147                         if (filter->type == OCELOT_VCAP_FILTER_PAG &&
148                             filter->goto_target == chain)
149                                 return filter;
150         }
151
152         list_for_each_entry(filter, &ocelot->dummy_rules, list)
153                 if (filter->goto_target == chain)
154                         return filter;
155
156         return NULL;
157 }
158
159 static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
160                                       bool ingress, struct flow_cls_offload *f,
161                                       struct ocelot_vcap_filter *filter)
162 {
163         struct ocelot_port *ocelot_port = ocelot->ports[port];
164         struct netlink_ext_ack *extack = f->common.extack;
165         bool allow_missing_goto_target = false;
166         const struct flow_action_entry *a;
167         enum ocelot_tag_tpid_sel tpid;
168         int i, chain, egress_port;
169         u64 rate;
170
171         if (!flow_action_basic_hw_stats_check(&f->rule->action,
172                                               f->common.extack))
173                 return -EOPNOTSUPP;
174
175         chain = f->common.chain_index;
176         filter->block_id = ocelot_chain_to_block(chain, ingress);
177         if (filter->block_id < 0) {
178                 NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");
179                 return -EOPNOTSUPP;
180         }
181         if (filter->block_id == VCAP_IS1 || filter->block_id == VCAP_IS2)
182                 filter->lookup = ocelot_chain_to_lookup(chain);
183         if (filter->block_id == VCAP_IS2)
184                 filter->pag = ocelot_chain_to_pag(chain);
185
186         filter->goto_target = -1;
187         filter->type = OCELOT_VCAP_FILTER_DUMMY;
188
189         flow_action_for_each(i, a, &f->rule->action) {
190                 switch (a->id) {
191                 case FLOW_ACTION_DROP:
192                         if (filter->block_id != VCAP_IS2) {
193                                 NL_SET_ERR_MSG_MOD(extack,
194                                                    "Drop action can only be offloaded to VCAP IS2");
195                                 return -EOPNOTSUPP;
196                         }
197                         if (filter->goto_target != -1) {
198                                 NL_SET_ERR_MSG_MOD(extack,
199                                                    "Last action must be GOTO");
200                                 return -EOPNOTSUPP;
201                         }
202                         filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
203                         filter->action.port_mask = 0;
204                         filter->action.police_ena = true;
205                         filter->action.pol_ix = OCELOT_POLICER_DISCARD;
206                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
207                         break;
208                 case FLOW_ACTION_TRAP:
209                         if (filter->block_id != VCAP_IS2 ||
210                             filter->lookup != 0) {
211                                 NL_SET_ERR_MSG_MOD(extack,
212                                                    "Trap action can only be offloaded to VCAP IS2 lookup 0");
213                                 return -EOPNOTSUPP;
214                         }
215                         if (filter->goto_target != -1) {
216                                 NL_SET_ERR_MSG_MOD(extack,
217                                                    "Last action must be GOTO");
218                                 return -EOPNOTSUPP;
219                         }
220                         filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
221                         filter->action.port_mask = 0;
222                         filter->action.cpu_copy_ena = true;
223                         filter->action.cpu_qu_num = 0;
224                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
225                         break;
226                 case FLOW_ACTION_POLICE:
227                         if (filter->block_id != VCAP_IS2 ||
228                             filter->lookup != 0) {
229                                 NL_SET_ERR_MSG_MOD(extack,
230                                                    "Police action can only be offloaded to VCAP IS2 lookup 0");
231                                 return -EOPNOTSUPP;
232                         }
233                         if (filter->goto_target != -1) {
234                                 NL_SET_ERR_MSG_MOD(extack,
235                                                    "Last action must be GOTO");
236                                 return -EOPNOTSUPP;
237                         }
238                         filter->action.police_ena = true;
239                         rate = a->police.rate_bytes_ps;
240                         filter->action.pol.rate = div_u64(rate, 1000) * 8;
241                         filter->action.pol.burst = a->police.burst;
242                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
243                         break;
244                 case FLOW_ACTION_REDIRECT:
245                         if (filter->block_id != VCAP_IS2) {
246                                 NL_SET_ERR_MSG_MOD(extack,
247                                                    "Redirect action can only be offloaded to VCAP IS2");
248                                 return -EOPNOTSUPP;
249                         }
250                         if (filter->goto_target != -1) {
251                                 NL_SET_ERR_MSG_MOD(extack,
252                                                    "Last action must be GOTO");
253                                 return -EOPNOTSUPP;
254                         }
255                         egress_port = ocelot->ops->netdev_to_port(a->dev);
256                         if (egress_port < 0) {
257                                 NL_SET_ERR_MSG_MOD(extack,
258                                                    "Destination not an ocelot port");
259                                 return -EOPNOTSUPP;
260                         }
261                         filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
262                         filter->action.port_mask = BIT(egress_port);
263                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
264                         break;
265                 case FLOW_ACTION_VLAN_POP:
266                         if (filter->block_id != VCAP_IS1) {
267                                 NL_SET_ERR_MSG_MOD(extack,
268                                                    "VLAN pop action can only be offloaded to VCAP IS1");
269                                 return -EOPNOTSUPP;
270                         }
271                         if (filter->goto_target != -1) {
272                                 NL_SET_ERR_MSG_MOD(extack,
273                                                    "Last action must be GOTO");
274                                 return -EOPNOTSUPP;
275                         }
276                         filter->action.vlan_pop_cnt_ena = true;
277                         filter->action.vlan_pop_cnt++;
278                         if (filter->action.vlan_pop_cnt > 2) {
279                                 NL_SET_ERR_MSG_MOD(extack,
280                                                    "Cannot pop more than 2 VLAN headers");
281                                 return -EOPNOTSUPP;
282                         }
283                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
284                         break;
285                 case FLOW_ACTION_VLAN_MANGLE:
286                         if (filter->block_id != VCAP_IS1) {
287                                 NL_SET_ERR_MSG_MOD(extack,
288                                                    "VLAN modify action can only be offloaded to VCAP IS1");
289                                 return -EOPNOTSUPP;
290                         }
291                         if (filter->goto_target != -1) {
292                                 NL_SET_ERR_MSG_MOD(extack,
293                                                    "Last action must be GOTO");
294                                 return -EOPNOTSUPP;
295                         }
296                         if (!ocelot_port->vlan_aware) {
297                                 NL_SET_ERR_MSG_MOD(extack,
298                                                    "Can only modify VLAN under VLAN aware bridge");
299                                 return -EOPNOTSUPP;
300                         }
301                         filter->action.vid_replace_ena = true;
302                         filter->action.pcp_dei_ena = true;
303                         filter->action.vid = a->vlan.vid;
304                         filter->action.pcp = a->vlan.prio;
305                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
306                         break;
307                 case FLOW_ACTION_PRIORITY:
308                         if (filter->block_id != VCAP_IS1) {
309                                 NL_SET_ERR_MSG_MOD(extack,
310                                                    "Priority action can only be offloaded to VCAP IS1");
311                                 return -EOPNOTSUPP;
312                         }
313                         if (filter->goto_target != -1) {
314                                 NL_SET_ERR_MSG_MOD(extack,
315                                                    "Last action must be GOTO");
316                                 return -EOPNOTSUPP;
317                         }
318                         filter->action.qos_ena = true;
319                         filter->action.qos_val = a->priority;
320                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
321                         break;
322                 case FLOW_ACTION_GOTO:
323                         filter->goto_target = a->chain_index;
324
325                         if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
326                                 int pag = ocelot_chain_to_pag(filter->goto_target);
327
328                                 filter->action.pag_override_mask = 0xff;
329                                 filter->action.pag_val = pag;
330                                 filter->type = OCELOT_VCAP_FILTER_PAG;
331                         }
332                         break;
333                 case FLOW_ACTION_VLAN_PUSH:
334                         if (filter->block_id != VCAP_ES0) {
335                                 NL_SET_ERR_MSG_MOD(extack,
336                                                    "VLAN push action can only be offloaded to VCAP ES0");
337                                 return -EOPNOTSUPP;
338                         }
339                         switch (ntohs(a->vlan.proto)) {
340                         case ETH_P_8021Q:
341                                 tpid = OCELOT_TAG_TPID_SEL_8021Q;
342                                 break;
343                         case ETH_P_8021AD:
344                                 tpid = OCELOT_TAG_TPID_SEL_8021AD;
345                                 break;
346                         default:
347                                 NL_SET_ERR_MSG_MOD(extack,
348                                                    "Cannot push custom TPID");
349                                 return -EOPNOTSUPP;
350                         }
351                         filter->action.tag_a_tpid_sel = tpid;
352                         filter->action.push_outer_tag = OCELOT_ES0_TAG;
353                         filter->action.tag_a_vid_sel = 1;
354                         filter->action.vid_a_val = a->vlan.vid;
355                         filter->action.pcp_a_val = a->vlan.prio;
356                         filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
357                         break;
358                 default:
359                         NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
360                         return -EOPNOTSUPP;
361                 }
362         }
363
364         if (filter->goto_target == -1) {
365                 if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
366                     chain == 0) {
367                         allow_missing_goto_target = true;
368                 } else {
369                         NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
370                         return -EOPNOTSUPP;
371                 }
372         }
373
374         if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&
375             !allow_missing_goto_target) {
376                 NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");
377                 return -EOPNOTSUPP;
378         }
379
380         return 0;
381 }
382
383 static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
384                                      struct flow_cls_offload *f,
385                                      struct ocelot_vcap_filter *filter)
386 {
387         struct flow_rule *rule = flow_cls_offload_flow_rule(f);
388         const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
389         int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;
390         struct netlink_ext_ack *extack = f->common.extack;
391         struct net_device *dev, *indev;
392         struct flow_match_meta match;
393         int ingress_port;
394
395         flow_rule_match_meta(rule, &match);
396
397         if (!match.mask->ingress_ifindex)
398                 return 0;
399
400         if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
401                 NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
402                 return -EOPNOTSUPP;
403         }
404
405         dev = ocelot->ops->port_to_netdev(ocelot, port);
406         if (!dev)
407                 return -EINVAL;
408
409         indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
410         if (!indev) {
411                 NL_SET_ERR_MSG_MOD(extack,
412                                    "Can't find the ingress port to match on");
413                 return -ENOENT;
414         }
415
416         ingress_port = ocelot->ops->netdev_to_port(indev);
417         if (ingress_port < 0) {
418                 NL_SET_ERR_MSG_MOD(extack,
419                                    "Can only offload an ocelot ingress port");
420                 return -EOPNOTSUPP;
421         }
422         if (ingress_port == port) {
423                 NL_SET_ERR_MSG_MOD(extack,
424                                    "Ingress port is equal to the egress port");
425                 return -EINVAL;
426         }
427
428         filter->ingress_port.value = ingress_port;
429         filter->ingress_port.mask = GENMASK(key_length - 1, 0);
430
431         return 0;
432 }
433
434 static int
435 ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
436                         struct flow_cls_offload *f,
437                         struct ocelot_vcap_filter *filter)
438 {
439         struct flow_rule *rule = flow_cls_offload_flow_rule(f);
440         struct flow_dissector *dissector = rule->match.dissector;
441         struct netlink_ext_ack *extack = f->common.extack;
442         u16 proto = ntohs(f->common.protocol);
443         bool match_protocol = true;
444         int ret;
445
446         if (dissector->used_keys &
447             ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
448               BIT(FLOW_DISSECTOR_KEY_BASIC) |
449               BIT(FLOW_DISSECTOR_KEY_META) |
450               BIT(FLOW_DISSECTOR_KEY_PORTS) |
451               BIT(FLOW_DISSECTOR_KEY_VLAN) |
452               BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
453               BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
454               BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
455                 return -EOPNOTSUPP;
456         }
457
458         /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
459         if (!ingress) {
460                 ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
461                 if (ret)
462                         return ret;
463         }
464
465         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
466                 struct flow_match_control match;
467
468                 flow_rule_match_control(rule, &match);
469         }
470
471         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
472                 struct flow_match_eth_addrs match;
473
474                 if (filter->block_id == VCAP_ES0) {
475                         NL_SET_ERR_MSG_MOD(extack,
476                                            "VCAP ES0 cannot match on MAC address");
477                         return -EOPNOTSUPP;
478                 }
479
480                 /* The hw support mac matches only for MAC_ETYPE key,
481                  * therefore if other matches(port, tcp flags, etc) are added
482                  * then just bail out
483                  */
484                 if ((dissector->used_keys &
485                     (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
486                      BIT(FLOW_DISSECTOR_KEY_BASIC) |
487                      BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
488                     (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
489                      BIT(FLOW_DISSECTOR_KEY_BASIC) |
490                      BIT(FLOW_DISSECTOR_KEY_CONTROL)))
491                         return -EOPNOTSUPP;
492
493                 flow_rule_match_eth_addrs(rule, &match);
494
495                 if (filter->block_id == VCAP_IS1 &&
496                     !is_zero_ether_addr(match.mask->dst)) {
497                         NL_SET_ERR_MSG_MOD(extack,
498                                            "Key type S1_NORMAL cannot match on destination MAC");
499                         return -EOPNOTSUPP;
500                 }
501
502                 filter->key_type = OCELOT_VCAP_KEY_ETYPE;
503                 ether_addr_copy(filter->key.etype.dmac.value,
504                                 match.key->dst);
505                 ether_addr_copy(filter->key.etype.smac.value,
506                                 match.key->src);
507                 ether_addr_copy(filter->key.etype.dmac.mask,
508                                 match.mask->dst);
509                 ether_addr_copy(filter->key.etype.smac.mask,
510                                 match.mask->src);
511                 goto finished_key_parsing;
512         }
513
514         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
515                 struct flow_match_basic match;
516
517                 flow_rule_match_basic(rule, &match);
518                 if (ntohs(match.key->n_proto) == ETH_P_IP) {
519                         if (filter->block_id == VCAP_ES0) {
520                                 NL_SET_ERR_MSG_MOD(extack,
521                                                    "VCAP ES0 cannot match on IP protocol");
522                                 return -EOPNOTSUPP;
523                         }
524
525                         filter->key_type = OCELOT_VCAP_KEY_IPV4;
526                         filter->key.ipv4.proto.value[0] =
527                                 match.key->ip_proto;
528                         filter->key.ipv4.proto.mask[0] =
529                                 match.mask->ip_proto;
530                         match_protocol = false;
531                 }
532                 if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
533                         if (filter->block_id == VCAP_ES0) {
534                                 NL_SET_ERR_MSG_MOD(extack,
535                                                    "VCAP ES0 cannot match on IP protocol");
536                                 return -EOPNOTSUPP;
537                         }
538
539                         filter->key_type = OCELOT_VCAP_KEY_IPV6;
540                         filter->key.ipv6.proto.value[0] =
541                                 match.key->ip_proto;
542                         filter->key.ipv6.proto.mask[0] =
543                                 match.mask->ip_proto;
544                         match_protocol = false;
545                 }
546         }
547
548         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
549             proto == ETH_P_IP) {
550                 struct flow_match_ipv4_addrs match;
551                 u8 *tmp;
552
553                 if (filter->block_id == VCAP_ES0) {
554                         NL_SET_ERR_MSG_MOD(extack,
555                                            "VCAP ES0 cannot match on IP address");
556                         return -EOPNOTSUPP;
557                 }
558
559                 flow_rule_match_ipv4_addrs(rule, &match);
560
561                 if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
562                         NL_SET_ERR_MSG_MOD(extack,
563                                            "Key type S1_NORMAL cannot match on destination IP");
564                         return -EOPNOTSUPP;
565                 }
566
567                 tmp = &filter->key.ipv4.sip.value.addr[0];
568                 memcpy(tmp, &match.key->src, 4);
569
570                 tmp = &filter->key.ipv4.sip.mask.addr[0];
571                 memcpy(tmp, &match.mask->src, 4);
572
573                 tmp = &filter->key.ipv4.dip.value.addr[0];
574                 memcpy(tmp, &match.key->dst, 4);
575
576                 tmp = &filter->key.ipv4.dip.mask.addr[0];
577                 memcpy(tmp, &match.mask->dst, 4);
578                 match_protocol = false;
579         }
580
581         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
582             proto == ETH_P_IPV6) {
583                 return -EOPNOTSUPP;
584         }
585
586         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
587                 struct flow_match_ports match;
588
589                 if (filter->block_id == VCAP_ES0) {
590                         NL_SET_ERR_MSG_MOD(extack,
591                                            "VCAP ES0 cannot match on L4 ports");
592                         return -EOPNOTSUPP;
593                 }
594
595                 flow_rule_match_ports(rule, &match);
596                 filter->key.ipv4.sport.value = ntohs(match.key->src);
597                 filter->key.ipv4.sport.mask = ntohs(match.mask->src);
598                 filter->key.ipv4.dport.value = ntohs(match.key->dst);
599                 filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
600                 match_protocol = false;
601         }
602
603         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
604                 struct flow_match_vlan match;
605
606                 flow_rule_match_vlan(rule, &match);
607                 filter->key_type = OCELOT_VCAP_KEY_ANY;
608                 filter->vlan.vid.value = match.key->vlan_id;
609                 filter->vlan.vid.mask = match.mask->vlan_id;
610                 filter->vlan.pcp.value[0] = match.key->vlan_priority;
611                 filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
612                 match_protocol = false;
613         }
614
615 finished_key_parsing:
616         if (match_protocol && proto != ETH_P_ALL) {
617                 if (filter->block_id == VCAP_ES0) {
618                         NL_SET_ERR_MSG_MOD(extack,
619                                            "VCAP ES0 cannot match on L2 proto");
620                         return -EOPNOTSUPP;
621                 }
622
623                 /* TODO: support SNAP, LLC etc */
624                 if (proto < ETH_P_802_3_MIN)
625                         return -EOPNOTSUPP;
626                 filter->key_type = OCELOT_VCAP_KEY_ETYPE;
627                 *(__be16 *)filter->key.etype.etype.value = htons(proto);
628                 *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
629         }
630         /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
631
632         return 0;
633 }
634
635 static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
636                                struct flow_cls_offload *f,
637                                struct ocelot_vcap_filter *filter)
638 {
639         int ret;
640
641         filter->prio = f->common.prio;
642         filter->id = f->cookie;
643
644         ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
645         if (ret)
646                 return ret;
647
648         return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
649 }
650
651 static struct ocelot_vcap_filter
652 *ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
653                            struct flow_cls_offload *f)
654 {
655         struct ocelot_vcap_filter *filter;
656
657         filter = kzalloc(sizeof(*filter), GFP_KERNEL);
658         if (!filter)
659                 return NULL;
660
661         if (ingress) {
662                 filter->ingress_port_mask = BIT(port);
663         } else {
664                 const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
665                 int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
666
667                 filter->egress_port.value = port;
668                 filter->egress_port.mask = GENMASK(key_length - 1, 0);
669         }
670
671         return filter;
672 }
673
674 static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
675                                         struct ocelot_vcap_filter *filter)
676 {
677         list_add(&filter->list, &ocelot->dummy_rules);
678
679         return 0;
680 }
681
682 static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
683                                         struct ocelot_vcap_filter *filter)
684 {
685         list_del(&filter->list);
686         kfree(filter);
687
688         return 0;
689 }
690
691 int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
692                               struct flow_cls_offload *f, bool ingress)
693 {
694         struct netlink_ext_ack *extack = f->common.extack;
695         struct ocelot_vcap_filter *filter;
696         int chain = f->common.chain_index;
697         int ret;
698
699         if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {
700                 NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");
701                 return -EOPNOTSUPP;
702         }
703
704         filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
705         if (!filter)
706                 return -ENOMEM;
707
708         ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
709         if (ret) {
710                 kfree(filter);
711                 return ret;
712         }
713
714         /* The non-optional GOTOs for the TCAM skeleton don't need
715          * to be actually offloaded.
716          */
717         if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
718                 return ocelot_vcap_dummy_filter_add(ocelot, filter);
719
720         return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
721 }
722 EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
723
724 int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
725                               struct flow_cls_offload *f, bool ingress)
726 {
727         struct ocelot_vcap_filter *filter;
728         struct ocelot_vcap_block *block;
729         int block_id;
730
731         block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
732         if (block_id < 0)
733                 return 0;
734
735         block = &ocelot->block[block_id];
736
737         filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
738         if (!filter)
739                 return 0;
740
741         if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
742                 return ocelot_vcap_dummy_filter_del(ocelot, filter);
743
744         return ocelot_vcap_filter_del(ocelot, filter);
745 }
746 EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
747
748 int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
749                             struct flow_cls_offload *f, bool ingress)
750 {
751         struct ocelot_vcap_filter *filter;
752         struct ocelot_vcap_block *block;
753         int block_id, ret;
754
755         block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
756         if (block_id < 0)
757                 return 0;
758
759         block = &ocelot->block[block_id];
760
761         filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
762         if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
763                 return 0;
764
765         ret = ocelot_vcap_filter_stats_update(ocelot, filter);
766         if (ret)
767                 return ret;
768
769         flow_stats_update(&f->stats, 0x0, filter->stats.pkts, 0, 0x0,
770                           FLOW_ACTION_HW_STATS_IMMEDIATE);
771         return 0;
772 }
773 EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);