1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch driver
3 * Copyright (c) 2019 Microsemi Corporation
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"
11 /* Arbitrarily chosen constants for encoding the VCAP block and lookup number
12 * into the chain number. This is UAPI.
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))
24 static int ocelot_chain_to_block(int chain, bool ingress)
34 /* Backwards compatibility with older, single-chain tc-flower
35 * offload support in Ocelot
40 for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)
41 if (chain == VCAP_IS1_CHAIN(lookup))
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))
52 /* Caller must ensure this is a valid IS1 or IS2 chain first,
53 * by calling ocelot_chain_to_block.
55 static int ocelot_chain_to_lookup(int chain)
57 /* Backwards compatibility with older, single-chain tc-flower
58 * offload support in Ocelot
63 return (chain / VCAP_LOOKUP) % 10;
66 /* Caller must ensure this is a valid IS2 chain first,
67 * by calling ocelot_chain_to_block.
69 static int ocelot_chain_to_pag(int chain)
73 /* Backwards compatibility with older, single-chain tc-flower
74 * offload support in Ocelot
79 lookup = ocelot_chain_to_lookup(chain);
81 /* calculate PAG value as chain index relative to the first PAG */
82 return chain - VCAP_IS2_CHAIN(lookup, 0);
85 static bool ocelot_is_goto_target_valid(int goto_target, int chain,
90 /* Can't offload GOTO in VCAP ES0 */
92 return (goto_target < 0);
94 /* Non-optional GOTOs */
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));
103 if (chain == VCAP_IS1_CHAIN(0))
104 return (goto_target == VCAP_IS1_CHAIN(1));
106 if (chain == VCAP_IS1_CHAIN(1))
107 return (goto_target == VCAP_IS1_CHAIN(2));
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.
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))
121 /* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.
122 * We cannot change the PAG at this point.
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));
128 /* VCAP IS2 lookup 1 cannot jump anywhere */
132 static struct ocelot_vcap_filter *
133 ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
135 struct ocelot_vcap_filter *filter;
136 struct ocelot_vcap_block *block;
139 block_id = ocelot_chain_to_block(chain, true);
143 if (block_id == VCAP_IS2) {
144 block = &ocelot->block[VCAP_IS1];
146 list_for_each_entry(filter, &block->rules, list)
147 if (filter->type == OCELOT_VCAP_FILTER_PAG &&
148 filter->goto_target == chain)
152 list_for_each_entry(filter, &ocelot->dummy_rules, list)
153 if (filter->goto_target == chain)
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)
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;
171 if (!flow_action_basic_hw_stats_check(&f->rule->action,
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");
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);
186 filter->goto_target = -1;
187 filter->type = OCELOT_VCAP_FILTER_DUMMY;
189 flow_action_for_each(i, a, &f->rule->action) {
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");
197 if (filter->goto_target != -1) {
198 NL_SET_ERR_MSG_MOD(extack,
199 "Last action must be GOTO");
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;
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");
215 if (filter->goto_target != -1) {
216 NL_SET_ERR_MSG_MOD(extack,
217 "Last action must be GOTO");
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;
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");
233 if (filter->goto_target != -1) {
234 NL_SET_ERR_MSG_MOD(extack,
235 "Last action must be GOTO");
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;
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");
250 if (filter->goto_target != -1) {
251 NL_SET_ERR_MSG_MOD(extack,
252 "Last action must be GOTO");
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");
261 filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
262 filter->action.port_mask = BIT(egress_port);
263 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
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");
271 if (filter->goto_target != -1) {
272 NL_SET_ERR_MSG_MOD(extack,
273 "Last action must be GOTO");
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");
283 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
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");
291 if (filter->goto_target != -1) {
292 NL_SET_ERR_MSG_MOD(extack,
293 "Last action must be GOTO");
296 if (!ocelot_port->vlan_aware) {
297 NL_SET_ERR_MSG_MOD(extack,
298 "Can only modify VLAN under VLAN aware bridge");
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;
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");
313 if (filter->goto_target != -1) {
314 NL_SET_ERR_MSG_MOD(extack,
315 "Last action must be GOTO");
318 filter->action.qos_ena = true;
319 filter->action.qos_val = a->priority;
320 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
322 case FLOW_ACTION_GOTO:
323 filter->goto_target = a->chain_index;
325 if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
326 int pag = ocelot_chain_to_pag(filter->goto_target);
328 filter->action.pag_override_mask = 0xff;
329 filter->action.pag_val = pag;
330 filter->type = OCELOT_VCAP_FILTER_PAG;
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");
339 switch (ntohs(a->vlan.proto)) {
341 tpid = OCELOT_TAG_TPID_SEL_8021Q;
344 tpid = OCELOT_TAG_TPID_SEL_8021AD;
347 NL_SET_ERR_MSG_MOD(extack,
348 "Cannot push custom TPID");
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;
359 NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
364 if (filter->goto_target == -1) {
365 if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
367 allow_missing_goto_target = true;
369 NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
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");
383 static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
384 struct flow_cls_offload *f,
385 struct ocelot_vcap_filter *filter)
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;
395 flow_rule_match_meta(rule, &match);
397 if (!match.mask->ingress_ifindex)
400 if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
401 NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
405 dev = ocelot->ops->port_to_netdev(ocelot, port);
409 indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
411 NL_SET_ERR_MSG_MOD(extack,
412 "Can't find the ingress port to match on");
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");
422 if (ingress_port == port) {
423 NL_SET_ERR_MSG_MOD(extack,
424 "Ingress port is equal to the egress port");
428 filter->ingress_port.value = ingress_port;
429 filter->ingress_port.mask = GENMASK(key_length - 1, 0);
435 ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
436 struct flow_cls_offload *f,
437 struct ocelot_vcap_filter *filter)
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;
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))) {
458 /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
460 ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
465 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
466 struct flow_match_control match;
468 flow_rule_match_control(rule, &match);
471 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
472 struct flow_match_eth_addrs match;
474 if (filter->block_id == VCAP_ES0) {
475 NL_SET_ERR_MSG_MOD(extack,
476 "VCAP ES0 cannot match on MAC address");
480 /* The hw support mac matches only for MAC_ETYPE key,
481 * therefore if other matches(port, tcp flags, etc) are added
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)))
493 flow_rule_match_eth_addrs(rule, &match);
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");
502 filter->key_type = OCELOT_VCAP_KEY_ETYPE;
503 ether_addr_copy(filter->key.etype.dmac.value,
505 ether_addr_copy(filter->key.etype.smac.value,
507 ether_addr_copy(filter->key.etype.dmac.mask,
509 ether_addr_copy(filter->key.etype.smac.mask,
511 goto finished_key_parsing;
514 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
515 struct flow_match_basic match;
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");
525 filter->key_type = OCELOT_VCAP_KEY_IPV4;
526 filter->key.ipv4.proto.value[0] =
528 filter->key.ipv4.proto.mask[0] =
529 match.mask->ip_proto;
530 match_protocol = false;
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");
539 filter->key_type = OCELOT_VCAP_KEY_IPV6;
540 filter->key.ipv6.proto.value[0] =
542 filter->key.ipv6.proto.mask[0] =
543 match.mask->ip_proto;
544 match_protocol = false;
548 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
550 struct flow_match_ipv4_addrs match;
553 if (filter->block_id == VCAP_ES0) {
554 NL_SET_ERR_MSG_MOD(extack,
555 "VCAP ES0 cannot match on IP address");
559 flow_rule_match_ipv4_addrs(rule, &match);
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");
567 tmp = &filter->key.ipv4.sip.value.addr[0];
568 memcpy(tmp, &match.key->src, 4);
570 tmp = &filter->key.ipv4.sip.mask.addr[0];
571 memcpy(tmp, &match.mask->src, 4);
573 tmp = &filter->key.ipv4.dip.value.addr[0];
574 memcpy(tmp, &match.key->dst, 4);
576 tmp = &filter->key.ipv4.dip.mask.addr[0];
577 memcpy(tmp, &match.mask->dst, 4);
578 match_protocol = false;
581 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
582 proto == ETH_P_IPV6) {
586 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
587 struct flow_match_ports match;
589 if (filter->block_id == VCAP_ES0) {
590 NL_SET_ERR_MSG_MOD(extack,
591 "VCAP ES0 cannot match on L4 ports");
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;
603 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
604 struct flow_match_vlan match;
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;
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");
623 /* TODO: support SNAP, LLC etc */
624 if (proto < ETH_P_802_3_MIN)
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);
630 /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
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)
641 filter->prio = f->common.prio;
642 filter->id = f->cookie;
644 ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
648 return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
651 static struct ocelot_vcap_filter
652 *ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
653 struct flow_cls_offload *f)
655 struct ocelot_vcap_filter *filter;
657 filter = kzalloc(sizeof(*filter), GFP_KERNEL);
662 filter->ingress_port_mask = BIT(port);
664 const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
665 int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
667 filter->egress_port.value = port;
668 filter->egress_port.mask = GENMASK(key_length - 1, 0);
674 static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
675 struct ocelot_vcap_filter *filter)
677 list_add(&filter->list, &ocelot->dummy_rules);
682 static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
683 struct ocelot_vcap_filter *filter)
685 list_del(&filter->list);
691 int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
692 struct flow_cls_offload *f, bool ingress)
694 struct netlink_ext_ack *extack = f->common.extack;
695 struct ocelot_vcap_filter *filter;
696 int chain = f->common.chain_index;
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");
704 filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
708 ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
714 /* The non-optional GOTOs for the TCAM skeleton don't need
715 * to be actually offloaded.
717 if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
718 return ocelot_vcap_dummy_filter_add(ocelot, filter);
720 return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
722 EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
724 int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
725 struct flow_cls_offload *f, bool ingress)
727 struct ocelot_vcap_filter *filter;
728 struct ocelot_vcap_block *block;
731 block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
735 block = &ocelot->block[block_id];
737 filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
741 if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
742 return ocelot_vcap_dummy_filter_del(ocelot, filter);
744 return ocelot_vcap_filter_del(ocelot, filter);
746 EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
748 int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
749 struct flow_cls_offload *f, bool ingress)
751 struct ocelot_vcap_filter *filter;
752 struct ocelot_vcap_block *block;
755 block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
759 block = &ocelot->block[block_id];
761 filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
762 if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
765 ret = ocelot_vcap_filter_stats_update(ocelot, filter);
769 flow_stats_update(&f->stats, 0x0, filter->stats.pkts, 0, 0x0,
770 FLOW_ACTION_HW_STATS_IMMEDIATE);
773 EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);