Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma
[linux-2.6-block.git] / drivers / net / ethernet / mscc / ocelot_flower.c
CommitLineData
fe3490e6
HV
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
9#include "ocelot_ace.h"
10
11struct ocelot_port_block {
12 struct ocelot_acl_block *block;
13 struct ocelot_port *port;
14};
15
16static u16 get_prio(u32 prio)
17{
18 /* prio starts from 0x1000 while the ids starts from 0 */
19 return prio >> 16;
20}
21
f9e30088 22static int ocelot_flower_parse_action(struct flow_cls_offload *f,
fe3490e6
HV
23 struct ocelot_ace_rule *rule)
24{
25 const struct flow_action_entry *a;
26 int i;
27
28 if (f->rule->action.num_entries != 1)
29 return -EOPNOTSUPP;
30
31 flow_action_for_each(i, a, &f->rule->action) {
32 switch (a->id) {
33 case FLOW_ACTION_DROP:
34 rule->action = OCELOT_ACL_ACTION_DROP;
35 break;
36 case FLOW_ACTION_TRAP:
37 rule->action = OCELOT_ACL_ACTION_TRAP;
38 break;
39 default:
40 return -EOPNOTSUPP;
41 }
42 }
43
44 return 0;
45}
46
f9e30088 47static int ocelot_flower_parse(struct flow_cls_offload *f,
fe3490e6
HV
48 struct ocelot_ace_rule *ocelot_rule)
49{
f9e30088 50 struct flow_rule *rule = flow_cls_offload_flow_rule(f);
fe3490e6
HV
51 struct flow_dissector *dissector = rule->match.dissector;
52
53 if (dissector->used_keys &
54 ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
55 BIT(FLOW_DISSECTOR_KEY_BASIC) |
56 BIT(FLOW_DISSECTOR_KEY_PORTS) |
57 BIT(FLOW_DISSECTOR_KEY_VLAN) |
58 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
59 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
60 BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
61 return -EOPNOTSUPP;
62 }
63
64 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
65 struct flow_match_control match;
66
67 flow_rule_match_control(rule, &match);
68 }
69
70 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
71 struct flow_match_eth_addrs match;
72 u16 proto = ntohs(f->common.protocol);
73
74 /* The hw support mac matches only for MAC_ETYPE key,
75 * therefore if other matches(port, tcp flags, etc) are added
76 * then just bail out
77 */
78 if ((dissector->used_keys &
79 (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
80 BIT(FLOW_DISSECTOR_KEY_BASIC) |
81 BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
82 (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
83 BIT(FLOW_DISSECTOR_KEY_BASIC) |
84 BIT(FLOW_DISSECTOR_KEY_CONTROL)))
85 return -EOPNOTSUPP;
86
87 if (proto == ETH_P_IP ||
88 proto == ETH_P_IPV6 ||
89 proto == ETH_P_ARP)
90 return -EOPNOTSUPP;
91
92 flow_rule_match_eth_addrs(rule, &match);
93 ocelot_rule->type = OCELOT_ACE_TYPE_ETYPE;
94 ether_addr_copy(ocelot_rule->frame.etype.dmac.value,
95 match.key->dst);
96 ether_addr_copy(ocelot_rule->frame.etype.smac.value,
97 match.key->src);
98 ether_addr_copy(ocelot_rule->frame.etype.dmac.mask,
99 match.mask->dst);
100 ether_addr_copy(ocelot_rule->frame.etype.smac.mask,
101 match.mask->src);
102 goto finished_key_parsing;
103 }
104
105 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
106 struct flow_match_basic match;
107
108 flow_rule_match_basic(rule, &match);
109 if (ntohs(match.key->n_proto) == ETH_P_IP) {
110 ocelot_rule->type = OCELOT_ACE_TYPE_IPV4;
111 ocelot_rule->frame.ipv4.proto.value[0] =
112 match.key->ip_proto;
113 ocelot_rule->frame.ipv4.proto.mask[0] =
114 match.mask->ip_proto;
115 }
116 if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
117 ocelot_rule->type = OCELOT_ACE_TYPE_IPV6;
118 ocelot_rule->frame.ipv6.proto.value[0] =
119 match.key->ip_proto;
120 ocelot_rule->frame.ipv6.proto.mask[0] =
121 match.mask->ip_proto;
122 }
123 }
124
125 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
126 ntohs(f->common.protocol) == ETH_P_IP) {
127 struct flow_match_ipv4_addrs match;
128 u8 *tmp;
129
130 flow_rule_match_ipv4_addrs(rule, &match);
131 tmp = &ocelot_rule->frame.ipv4.sip.value.addr[0];
132 memcpy(tmp, &match.key->src, 4);
133
134 tmp = &ocelot_rule->frame.ipv4.sip.mask.addr[0];
135 memcpy(tmp, &match.mask->src, 4);
136
137 tmp = &ocelot_rule->frame.ipv4.dip.value.addr[0];
138 memcpy(tmp, &match.key->dst, 4);
139
140 tmp = &ocelot_rule->frame.ipv4.dip.mask.addr[0];
141 memcpy(tmp, &match.mask->dst, 4);
142 }
143
144 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
145 ntohs(f->common.protocol) == ETH_P_IPV6) {
146 return -EOPNOTSUPP;
147 }
148
149 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
150 struct flow_match_ports match;
151
152 flow_rule_match_ports(rule, &match);
153 ocelot_rule->frame.ipv4.sport.value = ntohs(match.key->src);
154 ocelot_rule->frame.ipv4.sport.mask = ntohs(match.mask->src);
155 ocelot_rule->frame.ipv4.dport.value = ntohs(match.key->dst);
156 ocelot_rule->frame.ipv4.dport.mask = ntohs(match.mask->dst);
157 }
158
159 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
160 struct flow_match_vlan match;
161
162 flow_rule_match_vlan(rule, &match);
163 ocelot_rule->type = OCELOT_ACE_TYPE_ANY;
164 ocelot_rule->vlan.vid.value = match.key->vlan_id;
165 ocelot_rule->vlan.vid.mask = match.mask->vlan_id;
166 ocelot_rule->vlan.pcp.value[0] = match.key->vlan_priority;
167 ocelot_rule->vlan.pcp.mask[0] = match.mask->vlan_priority;
168 }
169
170finished_key_parsing:
171 ocelot_rule->prio = get_prio(f->common.prio);
172 ocelot_rule->id = f->cookie;
173 return ocelot_flower_parse_action(f, ocelot_rule);
174}
175
176static
f9e30088 177struct ocelot_ace_rule *ocelot_ace_rule_create(struct flow_cls_offload *f,
fe3490e6
HV
178 struct ocelot_port_block *block)
179{
180 struct ocelot_ace_rule *rule;
181
182 rule = kzalloc(sizeof(*rule), GFP_KERNEL);
183 if (!rule)
184 return NULL;
185
186 rule->port = block->port;
187 rule->chip_port = block->port->chip_port;
188 return rule;
189}
190
f9e30088 191static int ocelot_flower_replace(struct flow_cls_offload *f,
fe3490e6
HV
192 struct ocelot_port_block *port_block)
193{
194 struct ocelot_ace_rule *rule;
195 int ret;
196
197 rule = ocelot_ace_rule_create(f, port_block);
198 if (!rule)
199 return -ENOMEM;
200
201 ret = ocelot_flower_parse(f, rule);
202 if (ret) {
203 kfree(rule);
204 return ret;
205 }
206
207 ret = ocelot_ace_rule_offload_add(rule);
208 if (ret)
209 return ret;
210
211 port_block->port->tc.offload_cnt++;
212 return 0;
213}
214
f9e30088 215static int ocelot_flower_destroy(struct flow_cls_offload *f,
fe3490e6
HV
216 struct ocelot_port_block *port_block)
217{
218 struct ocelot_ace_rule rule;
219 int ret;
220
221 rule.prio = get_prio(f->common.prio);
222 rule.port = port_block->port;
223 rule.id = f->cookie;
224
225 ret = ocelot_ace_rule_offload_del(&rule);
226 if (ret)
227 return ret;
228
229 port_block->port->tc.offload_cnt--;
230 return 0;
231}
232
f9e30088 233static int ocelot_flower_stats_update(struct flow_cls_offload *f,
fe3490e6
HV
234 struct ocelot_port_block *port_block)
235{
236 struct ocelot_ace_rule rule;
237 int ret;
238
239 rule.prio = get_prio(f->common.prio);
240 rule.port = port_block->port;
241 rule.id = f->cookie;
242 ret = ocelot_ace_rule_stats_update(&rule);
243 if (ret)
244 return ret;
245
246 flow_stats_update(&f->stats, 0x0, rule.stats.pkts, 0x0);
247 return 0;
248}
249
f9e30088 250static int ocelot_setup_tc_cls_flower(struct flow_cls_offload *f,
fe3490e6
HV
251 struct ocelot_port_block *port_block)
252{
253 switch (f->command) {
f9e30088 254 case FLOW_CLS_REPLACE:
fe3490e6 255 return ocelot_flower_replace(f, port_block);
f9e30088 256 case FLOW_CLS_DESTROY:
fe3490e6 257 return ocelot_flower_destroy(f, port_block);
f9e30088 258 case FLOW_CLS_STATS:
fe3490e6
HV
259 return ocelot_flower_stats_update(f, port_block);
260 default:
261 return -EOPNOTSUPP;
262 }
263}
264
265static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type,
266 void *type_data, void *cb_priv)
267{
268 struct ocelot_port_block *port_block = cb_priv;
269
270 if (!tc_cls_can_offload_and_chain0(port_block->port->dev, type_data))
271 return -EOPNOTSUPP;
272
273 switch (type) {
274 case TC_SETUP_CLSFLOWER:
275 return ocelot_setup_tc_cls_flower(type_data, cb_priv);
276 case TC_SETUP_CLSMATCHALL:
277 return 0;
278 default:
279 return -EOPNOTSUPP;
280 }
281}
282
283static struct ocelot_port_block*
284ocelot_port_block_create(struct ocelot_port *port)
285{
286 struct ocelot_port_block *port_block;
287
288 port_block = kzalloc(sizeof(*port_block), GFP_KERNEL);
289 if (!port_block)
290 return NULL;
291
292 port_block->port = port;
293
294 return port_block;
295}
296
297static void ocelot_port_block_destroy(struct ocelot_port_block *block)
298{
299 kfree(block);
300}
301
955bcb6e
PNA
302static void ocelot_tc_block_unbind(void *cb_priv)
303{
304 struct ocelot_port_block *port_block = cb_priv;
305
306 ocelot_port_block_destroy(port_block);
307}
308
fe3490e6 309int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port,
955bcb6e 310 struct flow_block_offload *f)
fe3490e6
HV
311{
312 struct ocelot_port_block *port_block;
955bcb6e 313 struct flow_block_cb *block_cb;
fe3490e6
HV
314 int ret;
315
32f8c409 316 if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
fe3490e6
HV
317 return -EOPNOTSUPP;
318
14bfb13f
PNA
319 block_cb = flow_block_cb_lookup(f->block,
320 ocelot_setup_tc_block_cb_flower, port);
fe3490e6
HV
321 if (!block_cb) {
322 port_block = ocelot_port_block_create(port);
323 if (!port_block)
324 return -ENOMEM;
325
0c7294dd 326 block_cb = flow_block_cb_alloc(ocelot_setup_tc_block_cb_flower,
955bcb6e
PNA
327 port, port_block,
328 ocelot_tc_block_unbind);
fe3490e6
HV
329 if (IS_ERR(block_cb)) {
330 ret = PTR_ERR(block_cb);
331 goto err_cb_register;
332 }
955bcb6e
PNA
333 flow_block_cb_add(block_cb, f);
334 list_add_tail(&block_cb->driver_list, f->driver_block_list);
fe3490e6 335 } else {
955bcb6e 336 port_block = flow_block_cb_priv(block_cb);
fe3490e6
HV
337 }
338
955bcb6e 339 flow_block_cb_incref(block_cb);
fe3490e6
HV
340 return 0;
341
342err_cb_register:
343 ocelot_port_block_destroy(port_block);
344
345 return ret;
346}
347
348void ocelot_setup_tc_block_flower_unbind(struct ocelot_port *port,
955bcb6e 349 struct flow_block_offload *f)
fe3490e6 350{
955bcb6e 351 struct flow_block_cb *block_cb;
fe3490e6 352
14bfb13f
PNA
353 block_cb = flow_block_cb_lookup(f->block,
354 ocelot_setup_tc_block_cb_flower, port);
fe3490e6
HV
355 if (!block_cb)
356 return;
357
955bcb6e
PNA
358 if (!flow_block_cb_decref(block_cb)) {
359 flow_block_cb_remove(block_cb, f);
360 list_del(&block_cb->driver_list);
fe3490e6
HV
361 }
362}