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