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>
9 #include "ocelot_ace.h"
11 struct ocelot_port_block
{
12 struct ocelot_acl_block
*block
;
13 struct ocelot_port_private
*priv
;
16 static int ocelot_flower_parse_action(struct flow_cls_offload
*f
,
17 struct ocelot_ace_rule
*rule
)
19 const struct flow_action_entry
*a
;
22 if (f
->rule
->action
.num_entries
!= 1)
25 flow_action_for_each(i
, a
, &f
->rule
->action
) {
27 case FLOW_ACTION_DROP
:
28 rule
->action
= OCELOT_ACL_ACTION_DROP
;
30 case FLOW_ACTION_TRAP
:
31 rule
->action
= OCELOT_ACL_ACTION_TRAP
;
41 static int ocelot_flower_parse(struct flow_cls_offload
*f
,
42 struct ocelot_ace_rule
*ocelot_rule
)
44 struct flow_rule
*rule
= flow_cls_offload_flow_rule(f
);
45 struct flow_dissector
*dissector
= rule
->match
.dissector
;
47 if (dissector
->used_keys
&
48 ~(BIT(FLOW_DISSECTOR_KEY_CONTROL
) |
49 BIT(FLOW_DISSECTOR_KEY_BASIC
) |
50 BIT(FLOW_DISSECTOR_KEY_PORTS
) |
51 BIT(FLOW_DISSECTOR_KEY_VLAN
) |
52 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS
) |
53 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS
) |
54 BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS
))) {
58 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_CONTROL
)) {
59 struct flow_match_control match
;
61 flow_rule_match_control(rule
, &match
);
64 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_ETH_ADDRS
)) {
65 struct flow_match_eth_addrs match
;
66 u16 proto
= ntohs(f
->common
.protocol
);
68 /* The hw support mac matches only for MAC_ETYPE key,
69 * therefore if other matches(port, tcp flags, etc) are added
72 if ((dissector
->used_keys
&
73 (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS
) |
74 BIT(FLOW_DISSECTOR_KEY_BASIC
) |
75 BIT(FLOW_DISSECTOR_KEY_CONTROL
))) !=
76 (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS
) |
77 BIT(FLOW_DISSECTOR_KEY_BASIC
) |
78 BIT(FLOW_DISSECTOR_KEY_CONTROL
)))
81 if (proto
== ETH_P_IP
||
82 proto
== ETH_P_IPV6
||
86 flow_rule_match_eth_addrs(rule
, &match
);
87 ocelot_rule
->type
= OCELOT_ACE_TYPE_ETYPE
;
88 ether_addr_copy(ocelot_rule
->frame
.etype
.dmac
.value
,
90 ether_addr_copy(ocelot_rule
->frame
.etype
.smac
.value
,
92 ether_addr_copy(ocelot_rule
->frame
.etype
.dmac
.mask
,
94 ether_addr_copy(ocelot_rule
->frame
.etype
.smac
.mask
,
96 goto finished_key_parsing
;
99 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_BASIC
)) {
100 struct flow_match_basic match
;
102 flow_rule_match_basic(rule
, &match
);
103 if (ntohs(match
.key
->n_proto
) == ETH_P_IP
) {
104 ocelot_rule
->type
= OCELOT_ACE_TYPE_IPV4
;
105 ocelot_rule
->frame
.ipv4
.proto
.value
[0] =
107 ocelot_rule
->frame
.ipv4
.proto
.mask
[0] =
108 match
.mask
->ip_proto
;
110 if (ntohs(match
.key
->n_proto
) == ETH_P_IPV6
) {
111 ocelot_rule
->type
= OCELOT_ACE_TYPE_IPV6
;
112 ocelot_rule
->frame
.ipv6
.proto
.value
[0] =
114 ocelot_rule
->frame
.ipv6
.proto
.mask
[0] =
115 match
.mask
->ip_proto
;
119 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_IPV4_ADDRS
) &&
120 ntohs(f
->common
.protocol
) == ETH_P_IP
) {
121 struct flow_match_ipv4_addrs match
;
124 flow_rule_match_ipv4_addrs(rule
, &match
);
125 tmp
= &ocelot_rule
->frame
.ipv4
.sip
.value
.addr
[0];
126 memcpy(tmp
, &match
.key
->src
, 4);
128 tmp
= &ocelot_rule
->frame
.ipv4
.sip
.mask
.addr
[0];
129 memcpy(tmp
, &match
.mask
->src
, 4);
131 tmp
= &ocelot_rule
->frame
.ipv4
.dip
.value
.addr
[0];
132 memcpy(tmp
, &match
.key
->dst
, 4);
134 tmp
= &ocelot_rule
->frame
.ipv4
.dip
.mask
.addr
[0];
135 memcpy(tmp
, &match
.mask
->dst
, 4);
138 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_IPV6_ADDRS
) &&
139 ntohs(f
->common
.protocol
) == ETH_P_IPV6
) {
143 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_PORTS
)) {
144 struct flow_match_ports match
;
146 flow_rule_match_ports(rule
, &match
);
147 ocelot_rule
->frame
.ipv4
.sport
.value
= ntohs(match
.key
->src
);
148 ocelot_rule
->frame
.ipv4
.sport
.mask
= ntohs(match
.mask
->src
);
149 ocelot_rule
->frame
.ipv4
.dport
.value
= ntohs(match
.key
->dst
);
150 ocelot_rule
->frame
.ipv4
.dport
.mask
= ntohs(match
.mask
->dst
);
153 if (flow_rule_match_key(rule
, FLOW_DISSECTOR_KEY_VLAN
)) {
154 struct flow_match_vlan match
;
156 flow_rule_match_vlan(rule
, &match
);
157 ocelot_rule
->type
= OCELOT_ACE_TYPE_ANY
;
158 ocelot_rule
->vlan
.vid
.value
= match
.key
->vlan_id
;
159 ocelot_rule
->vlan
.vid
.mask
= match
.mask
->vlan_id
;
160 ocelot_rule
->vlan
.pcp
.value
[0] = match
.key
->vlan_priority
;
161 ocelot_rule
->vlan
.pcp
.mask
[0] = match
.mask
->vlan_priority
;
164 finished_key_parsing
:
165 ocelot_rule
->prio
= f
->common
.prio
;
166 ocelot_rule
->id
= f
->cookie
;
167 return ocelot_flower_parse_action(f
, ocelot_rule
);
171 struct ocelot_ace_rule
*ocelot_ace_rule_create(struct flow_cls_offload
*f
,
172 struct ocelot_port_block
*block
)
174 struct ocelot_ace_rule
*rule
;
176 rule
= kzalloc(sizeof(*rule
), GFP_KERNEL
);
180 rule
->port
= &block
->priv
->port
;
181 rule
->chip_port
= block
->priv
->chip_port
;
185 static int ocelot_flower_replace(struct flow_cls_offload
*f
,
186 struct ocelot_port_block
*port_block
)
188 struct ocelot_ace_rule
*rule
;
191 rule
= ocelot_ace_rule_create(f
, port_block
);
195 ret
= ocelot_flower_parse(f
, rule
);
201 ret
= ocelot_ace_rule_offload_add(rule
);
205 port_block
->priv
->tc
.offload_cnt
++;
209 static int ocelot_flower_destroy(struct flow_cls_offload
*f
,
210 struct ocelot_port_block
*port_block
)
212 struct ocelot_ace_rule rule
;
215 rule
.prio
= f
->common
.prio
;
216 rule
.port
= &port_block
->priv
->port
;
219 ret
= ocelot_ace_rule_offload_del(&rule
);
223 port_block
->priv
->tc
.offload_cnt
--;
227 static int ocelot_flower_stats_update(struct flow_cls_offload
*f
,
228 struct ocelot_port_block
*port_block
)
230 struct ocelot_ace_rule rule
;
233 rule
.prio
= f
->common
.prio
;
234 rule
.port
= &port_block
->priv
->port
;
236 ret
= ocelot_ace_rule_stats_update(&rule
);
240 flow_stats_update(&f
->stats
, 0x0, rule
.stats
.pkts
, 0x0);
244 static int ocelot_setup_tc_cls_flower(struct flow_cls_offload
*f
,
245 struct ocelot_port_block
*port_block
)
247 switch (f
->command
) {
248 case FLOW_CLS_REPLACE
:
249 return ocelot_flower_replace(f
, port_block
);
250 case FLOW_CLS_DESTROY
:
251 return ocelot_flower_destroy(f
, port_block
);
253 return ocelot_flower_stats_update(f
, port_block
);
259 static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type
,
260 void *type_data
, void *cb_priv
)
262 struct ocelot_port_block
*port_block
= cb_priv
;
264 if (!tc_cls_can_offload_and_chain0(port_block
->priv
->dev
, type_data
))
268 case TC_SETUP_CLSFLOWER
:
269 return ocelot_setup_tc_cls_flower(type_data
, cb_priv
);
270 case TC_SETUP_CLSMATCHALL
:
277 static struct ocelot_port_block
*
278 ocelot_port_block_create(struct ocelot_port_private
*priv
)
280 struct ocelot_port_block
*port_block
;
282 port_block
= kzalloc(sizeof(*port_block
), GFP_KERNEL
);
286 port_block
->priv
= priv
;
291 static void ocelot_port_block_destroy(struct ocelot_port_block
*block
)
296 static void ocelot_tc_block_unbind(void *cb_priv
)
298 struct ocelot_port_block
*port_block
= cb_priv
;
300 ocelot_port_block_destroy(port_block
);
303 int ocelot_setup_tc_block_flower_bind(struct ocelot_port_private
*priv
,
304 struct flow_block_offload
*f
)
306 struct ocelot_port_block
*port_block
;
307 struct flow_block_cb
*block_cb
;
310 if (f
->binder_type
== FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS
)
313 block_cb
= flow_block_cb_lookup(f
->block
,
314 ocelot_setup_tc_block_cb_flower
, priv
);
316 port_block
= ocelot_port_block_create(priv
);
320 block_cb
= flow_block_cb_alloc(ocelot_setup_tc_block_cb_flower
,
322 ocelot_tc_block_unbind
);
323 if (IS_ERR(block_cb
)) {
324 ret
= PTR_ERR(block_cb
);
325 goto err_cb_register
;
327 flow_block_cb_add(block_cb
, f
);
328 list_add_tail(&block_cb
->driver_list
, f
->driver_block_list
);
330 port_block
= flow_block_cb_priv(block_cb
);
333 flow_block_cb_incref(block_cb
);
337 ocelot_port_block_destroy(port_block
);
342 void ocelot_setup_tc_block_flower_unbind(struct ocelot_port_private
*priv
,
343 struct flow_block_offload
*f
)
345 struct flow_block_cb
*block_cb
;
347 block_cb
= flow_block_cb_lookup(f
->block
,
348 ocelot_setup_tc_block_cb_flower
, priv
);
352 if (!flow_block_cb_decref(block_cb
)) {
353 flow_block_cb_remove(block_cb
, f
);
354 list_del(&block_cb
->driver_list
);