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