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 return (chain / VCAP_LOOKUP) % 10;
60 /* Caller must ensure this is a valid IS2 chain first,
61 * by calling ocelot_chain_to_block.
63 static int ocelot_chain_to_pag(int chain)
65 int lookup = ocelot_chain_to_lookup(chain);
67 /* calculate PAG value as chain index relative to the first PAG */
68 return chain - VCAP_IS2_CHAIN(lookup, 0);
71 static bool ocelot_is_goto_target_valid(int goto_target, int chain,
76 /* Can't offload GOTO in VCAP ES0 */
78 return (goto_target < 0);
80 /* Non-optional GOTOs */
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));
89 if (chain == VCAP_IS1_CHAIN(0))
90 return (goto_target == VCAP_IS1_CHAIN(1));
92 if (chain == VCAP_IS1_CHAIN(1))
93 return (goto_target == VCAP_IS1_CHAIN(2));
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.
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))
107 /* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.
108 * We cannot change the PAG at this point.
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));
114 /* VCAP IS2 lookup 1 cannot jump anywhere */
118 static struct ocelot_vcap_filter *
119 ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
121 struct ocelot_vcap_filter *filter;
122 struct ocelot_vcap_block *block;
125 block_id = ocelot_chain_to_block(chain, true);
129 if (block_id == VCAP_IS2) {
130 block = &ocelot->block[VCAP_IS1];
132 list_for_each_entry(filter, &block->rules, list)
133 if (filter->type == OCELOT_VCAP_FILTER_PAG &&
134 filter->goto_target == chain)
138 list_for_each_entry(filter, &ocelot->dummy_rules, list)
139 if (filter->goto_target == chain)
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)
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;
157 if (!flow_action_basic_hw_stats_check(&f->rule->action,
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");
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);
172 filter->goto_target = -1;
173 filter->type = OCELOT_VCAP_FILTER_DUMMY;
175 flow_action_for_each(i, a, &f->rule->action) {
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");
183 if (filter->goto_target != -1) {
184 NL_SET_ERR_MSG_MOD(extack,
185 "Last action must be GOTO");
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;
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");
200 if (filter->goto_target != -1) {
201 NL_SET_ERR_MSG_MOD(extack,
202 "Last action must be GOTO");
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;
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");
218 if (filter->goto_target != -1) {
219 NL_SET_ERR_MSG_MOD(extack,
220 "Last action must be GOTO");
223 if (a->police.rate_pkt_ps) {
224 NL_SET_ERR_MSG_MOD(extack,
225 "QoS offload not support packets per second");
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;
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");
240 if (filter->goto_target != -1) {
241 NL_SET_ERR_MSG_MOD(extack,
242 "Last action must be GOTO");
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");
251 filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
252 filter->action.port_mask = BIT(egress_port);
253 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
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");
261 if (filter->goto_target != -1) {
262 NL_SET_ERR_MSG_MOD(extack,
263 "Last action must be GOTO");
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");
273 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
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");
281 if (filter->goto_target != -1) {
282 NL_SET_ERR_MSG_MOD(extack,
283 "Last action must be GOTO");
286 if (!ocelot_port->vlan_aware) {
287 NL_SET_ERR_MSG_MOD(extack,
288 "Can only modify VLAN under VLAN aware bridge");
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;
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");
303 if (filter->goto_target != -1) {
304 NL_SET_ERR_MSG_MOD(extack,
305 "Last action must be GOTO");
308 filter->action.qos_ena = true;
309 filter->action.qos_val = a->priority;
310 filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
312 case FLOW_ACTION_GOTO:
313 filter->goto_target = a->chain_index;
315 if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
316 int pag = ocelot_chain_to_pag(filter->goto_target);
318 filter->action.pag_override_mask = 0xff;
319 filter->action.pag_val = pag;
320 filter->type = OCELOT_VCAP_FILTER_PAG;
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");
329 switch (ntohs(a->vlan.proto)) {
331 tpid = OCELOT_TAG_TPID_SEL_8021Q;
334 tpid = OCELOT_TAG_TPID_SEL_8021AD;
337 NL_SET_ERR_MSG_MOD(extack,
338 "Cannot push custom TPID");
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;
349 NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
354 if (filter->goto_target == -1) {
355 if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
357 allow_missing_goto_target = true;
359 NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
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");
373 static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
374 struct flow_cls_offload *f,
375 struct ocelot_vcap_filter *filter)
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;
385 flow_rule_match_meta(rule, &match);
387 if (!match.mask->ingress_ifindex)
390 if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
391 NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
395 dev = ocelot->ops->port_to_netdev(ocelot, port);
399 indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
401 NL_SET_ERR_MSG_MOD(extack,
402 "Can't find the ingress port to match on");
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");
412 if (ingress_port == port) {
413 NL_SET_ERR_MSG_MOD(extack,
414 "Ingress port is equal to the egress port");
418 filter->ingress_port.value = ingress_port;
419 filter->ingress_port.mask = GENMASK(key_length - 1, 0);
425 ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
426 struct flow_cls_offload *f,
427 struct ocelot_vcap_filter *filter)
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;
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))) {
448 /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
450 ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
455 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
456 struct flow_match_control match;
458 flow_rule_match_control(rule, &match);
461 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
462 struct flow_match_eth_addrs match;
464 if (filter->block_id == VCAP_ES0) {
465 NL_SET_ERR_MSG_MOD(extack,
466 "VCAP ES0 cannot match on MAC address");
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");
477 /* The hw support mac matches only for MAC_ETYPE key,
478 * therefore if other matches(port, tcp flags, etc) are added
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)))
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,
494 ether_addr_copy(filter->key.etype.smac.value,
496 ether_addr_copy(filter->key.etype.dmac.mask,
498 ether_addr_copy(filter->key.etype.smac.mask,
500 goto finished_key_parsing;
503 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
504 struct flow_match_basic match;
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");
514 filter->key_type = OCELOT_VCAP_KEY_IPV4;
515 filter->key.ipv4.proto.value[0] =
517 filter->key.ipv4.proto.mask[0] =
518 match.mask->ip_proto;
519 match_protocol = false;
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");
528 filter->key_type = OCELOT_VCAP_KEY_IPV6;
529 filter->key.ipv6.proto.value[0] =
531 filter->key.ipv6.proto.mask[0] =
532 match.mask->ip_proto;
533 match_protocol = false;
537 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
539 struct flow_match_ipv4_addrs match;
542 if (filter->block_id == VCAP_ES0) {
543 NL_SET_ERR_MSG_MOD(extack,
544 "VCAP ES0 cannot match on IP address");
548 flow_rule_match_ipv4_addrs(rule, &match);
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");
556 tmp = &filter->key.ipv4.sip.value.addr[0];
557 memcpy(tmp, &match.key->src, 4);
559 tmp = &filter->key.ipv4.sip.mask.addr[0];
560 memcpy(tmp, &match.mask->src, 4);
562 tmp = &filter->key.ipv4.dip.value.addr[0];
563 memcpy(tmp, &match.key->dst, 4);
565 tmp = &filter->key.ipv4.dip.mask.addr[0];
566 memcpy(tmp, &match.mask->dst, 4);
567 match_protocol = false;
570 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
571 proto == ETH_P_IPV6) {
575 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
576 struct flow_match_ports match;
578 if (filter->block_id == VCAP_ES0) {
579 NL_SET_ERR_MSG_MOD(extack,
580 "VCAP ES0 cannot match on L4 ports");
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;
592 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
593 struct flow_match_vlan match;
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;
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");
612 /* TODO: support SNAP, LLC etc */
613 if (proto < ETH_P_802_3_MIN)
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);
619 /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
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)
630 filter->prio = f->common.prio;
631 filter->id.cookie = f->cookie;
632 filter->id.tc_offload = true;
634 ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
638 return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
641 static struct ocelot_vcap_filter
642 *ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
643 struct flow_cls_offload *f)
645 struct ocelot_vcap_filter *filter;
647 filter = kzalloc(sizeof(*filter), GFP_KERNEL);
652 filter->ingress_port_mask = BIT(port);
654 const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
655 int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
657 filter->egress_port.value = port;
658 filter->egress_port.mask = GENMASK(key_length - 1, 0);
664 static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
665 struct ocelot_vcap_filter *filter)
667 list_add(&filter->list, &ocelot->dummy_rules);
672 static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
673 struct ocelot_vcap_filter *filter)
675 list_del(&filter->list);
681 int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
682 struct flow_cls_offload *f, bool ingress)
684 struct netlink_ext_ack *extack = f->common.extack;
685 struct ocelot_vcap_filter *filter;
686 int chain = f->common.chain_index;
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");
694 filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
698 ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
704 /* The non-optional GOTOs for the TCAM skeleton don't need
705 * to be actually offloaded.
707 if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
708 return ocelot_vcap_dummy_filter_add(ocelot, filter);
710 return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
712 EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
714 int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
715 struct flow_cls_offload *f, bool ingress)
717 struct ocelot_vcap_filter *filter;
718 struct ocelot_vcap_block *block;
721 block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
725 block = &ocelot->block[block_id];
727 filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);
731 if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
732 return ocelot_vcap_dummy_filter_del(ocelot, filter);
734 return ocelot_vcap_filter_del(ocelot, filter);
736 EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
738 int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
739 struct flow_cls_offload *f, bool ingress)
741 struct ocelot_vcap_filter *filter;
742 struct ocelot_vcap_block *block;
745 block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
749 block = &ocelot->block[block_id];
751 filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);
752 if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
755 ret = ocelot_vcap_filter_stats_update(ocelot, filter);
759 flow_stats_update(&f->stats, 0x0, filter->stats.pkts, 0, 0x0,
760 FLOW_ACTION_HW_STATS_IMMEDIATE);
763 EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);