33a5e2a0a1adf8898bac3d804907fdaea8132f55
[linux-2.6-block.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_flower.c
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
3
4 #include <linux/kernel.h>
5 #include <linux/errno.h>
6 #include <linux/netdevice.h>
7 #include <net/net_namespace.h>
8 #include <net/flow_dissector.h>
9 #include <net/pkt_cls.h>
10 #include <net/tc_act/tc_gact.h>
11 #include <net/tc_act/tc_mirred.h>
12 #include <net/tc_act/tc_vlan.h>
13
14 #include "spectrum.h"
15 #include "core_acl_flex_keys.h"
16
17 static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
18                                          struct mlxsw_sp_acl_block *block,
19                                          struct mlxsw_sp_acl_rule_info *rulei,
20                                          struct flow_action *flow_action,
21                                          struct netlink_ext_ack *extack)
22 {
23         const struct flow_action_entry *act;
24         int mirror_act_count = 0;
25         int err, i;
26
27         if (!flow_action_has_entries(flow_action))
28                 return 0;
29         if (!flow_action_mixed_hw_stats_check(flow_action, extack))
30                 return -EOPNOTSUPP;
31
32         act = flow_action_first_entry_get(flow_action);
33         if (act->hw_stats == FLOW_ACTION_HW_STATS_ANY ||
34             act->hw_stats == FLOW_ACTION_HW_STATS_IMMEDIATE) {
35                 /* Count action is inserted first */
36                 err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack);
37                 if (err)
38                         return err;
39         } else if (act->hw_stats != FLOW_ACTION_HW_STATS_DISABLED) {
40                 NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type");
41                 return -EOPNOTSUPP;
42         }
43
44         flow_action_for_each(i, act, flow_action) {
45                 switch (act->id) {
46                 case FLOW_ACTION_ACCEPT:
47                         err = mlxsw_sp_acl_rulei_act_terminate(rulei);
48                         if (err) {
49                                 NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action");
50                                 return err;
51                         }
52                         break;
53                 case FLOW_ACTION_DROP: {
54                         bool ingress;
55
56                         if (mlxsw_sp_acl_block_is_mixed_bound(block)) {
57                                 NL_SET_ERR_MSG_MOD(extack, "Drop action is not supported when block is bound to ingress and egress");
58                                 return -EOPNOTSUPP;
59                         }
60                         ingress = mlxsw_sp_acl_block_is_ingress_bound(block);
61                         err = mlxsw_sp_acl_rulei_act_drop(rulei, ingress,
62                                                           act->cookie, extack);
63                         if (err) {
64                                 NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action");
65                                 return err;
66                         }
67
68                         /* Forbid block with this rulei to be bound
69                          * to ingress/egress in future. Ingress rule is
70                          * a blocker for egress and vice versa.
71                          */
72                         if (ingress)
73                                 rulei->egress_bind_blocker = 1;
74                         else
75                                 rulei->ingress_bind_blocker = 1;
76                         }
77                         break;
78                 case FLOW_ACTION_TRAP:
79                         err = mlxsw_sp_acl_rulei_act_trap(rulei);
80                         if (err) {
81                                 NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action");
82                                 return err;
83                         }
84                         break;
85                 case FLOW_ACTION_GOTO: {
86                         u32 chain_index = act->chain_index;
87                         struct mlxsw_sp_acl_ruleset *ruleset;
88                         u16 group_id;
89
90                         ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block,
91                                                               chain_index,
92                                                               MLXSW_SP_ACL_PROFILE_FLOWER);
93                         if (IS_ERR(ruleset))
94                                 return PTR_ERR(ruleset);
95
96                         group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
97                         err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id);
98                         if (err) {
99                                 NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action");
100                                 return err;
101                         }
102                         }
103                         break;
104                 case FLOW_ACTION_REDIRECT: {
105                         struct net_device *out_dev;
106                         struct mlxsw_sp_fid *fid;
107                         u16 fid_index;
108
109                         if (mlxsw_sp_acl_block_is_egress_bound(block)) {
110                                 NL_SET_ERR_MSG_MOD(extack, "Redirect action is not supported on egress");
111                                 return -EOPNOTSUPP;
112                         }
113
114                         /* Forbid block with this rulei to be bound
115                          * to egress in future.
116                          */
117                         rulei->egress_bind_blocker = 1;
118
119                         fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp);
120                         fid_index = mlxsw_sp_fid_index(fid);
121                         err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei,
122                                                              fid_index, extack);
123                         if (err)
124                                 return err;
125
126                         out_dev = act->dev;
127                         err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei,
128                                                          out_dev, extack);
129                         if (err)
130                                 return err;
131                         }
132                         break;
133                 case FLOW_ACTION_MIRRED: {
134                         struct net_device *out_dev = act->dev;
135
136                         if (mirror_act_count++) {
137                                 NL_SET_ERR_MSG_MOD(extack, "Multiple mirror actions per rule are not supported");
138                                 return -EOPNOTSUPP;
139                         }
140
141                         err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
142                                                             block, out_dev,
143                                                             extack);
144                         if (err)
145                                 return err;
146                         }
147                         break;
148                 case FLOW_ACTION_VLAN_MANGLE: {
149                         u16 proto = be16_to_cpu(act->vlan.proto);
150                         u8 prio = act->vlan.prio;
151                         u16 vid = act->vlan.vid;
152
153                         return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei,
154                                                            act->id, vid,
155                                                            proto, prio, extack);
156                         }
157                 case FLOW_ACTION_PRIORITY:
158                         return mlxsw_sp_acl_rulei_act_priority(mlxsw_sp, rulei,
159                                                                act->priority,
160                                                                extack);
161                 case FLOW_ACTION_MANGLE: {
162                         enum flow_action_mangle_base htype = act->mangle.htype;
163                         __be32 be_mask = (__force __be32) act->mangle.mask;
164                         __be32 be_val = (__force __be32) act->mangle.val;
165                         u32 offset = act->mangle.offset;
166                         u32 mask = be32_to_cpu(be_mask);
167                         u32 val = be32_to_cpu(be_val);
168
169                         err = mlxsw_sp_acl_rulei_act_mangle(mlxsw_sp, rulei,
170                                                             htype, offset,
171                                                             mask, val, extack);
172                         if (err)
173                                 return err;
174                         break;
175                         }
176                 default:
177                         NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
178                         dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
179                         return -EOPNOTSUPP;
180                 }
181         }
182         return 0;
183 }
184
185 static int mlxsw_sp_flower_parse_meta(struct mlxsw_sp_acl_rule_info *rulei,
186                                       struct flow_cls_offload *f,
187                                       struct mlxsw_sp_acl_block *block)
188 {
189         struct flow_rule *rule = flow_cls_offload_flow_rule(f);
190         struct mlxsw_sp_port *mlxsw_sp_port;
191         struct net_device *ingress_dev;
192         struct flow_match_meta match;
193
194         if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META))
195                 return 0;
196
197         flow_rule_match_meta(rule, &match);
198         if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
199                 NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported ingress ifindex mask");
200                 return -EINVAL;
201         }
202
203         ingress_dev = __dev_get_by_index(block->net,
204                                          match.key->ingress_ifindex);
205         if (!ingress_dev) {
206                 NL_SET_ERR_MSG_MOD(f->common.extack, "Can't find specified ingress port to match on");
207                 return -EINVAL;
208         }
209
210         if (!mlxsw_sp_port_dev_check(ingress_dev)) {
211                 NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on non-mlxsw ingress port");
212                 return -EINVAL;
213         }
214
215         mlxsw_sp_port = netdev_priv(ingress_dev);
216         if (mlxsw_sp_port->mlxsw_sp != block->mlxsw_sp) {
217                 NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on a port from different device");
218                 return -EINVAL;
219         }
220
221         mlxsw_sp_acl_rulei_keymask_u32(rulei,
222                                        MLXSW_AFK_ELEMENT_SRC_SYS_PORT,
223                                        mlxsw_sp_port->local_port,
224                                        0xFFFFFFFF);
225         return 0;
226 }
227
228 static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei,
229                                        struct flow_cls_offload *f)
230 {
231         struct flow_match_ipv4_addrs match;
232
233         flow_rule_match_ipv4_addrs(f->rule, &match);
234
235         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
236                                        (char *) &match.key->src,
237                                        (char *) &match.mask->src, 4);
238         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
239                                        (char *) &match.key->dst,
240                                        (char *) &match.mask->dst, 4);
241 }
242
243 static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei,
244                                        struct flow_cls_offload *f)
245 {
246         struct flow_match_ipv6_addrs match;
247
248         flow_rule_match_ipv6_addrs(f->rule, &match);
249
250         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
251                                        &match.key->src.s6_addr[0x0],
252                                        &match.mask->src.s6_addr[0x0], 4);
253         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
254                                        &match.key->src.s6_addr[0x4],
255                                        &match.mask->src.s6_addr[0x4], 4);
256         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
257                                        &match.key->src.s6_addr[0x8],
258                                        &match.mask->src.s6_addr[0x8], 4);
259         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
260                                        &match.key->src.s6_addr[0xC],
261                                        &match.mask->src.s6_addr[0xC], 4);
262         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
263                                        &match.key->dst.s6_addr[0x0],
264                                        &match.mask->dst.s6_addr[0x0], 4);
265         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
266                                        &match.key->dst.s6_addr[0x4],
267                                        &match.mask->dst.s6_addr[0x4], 4);
268         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
269                                        &match.key->dst.s6_addr[0x8],
270                                        &match.mask->dst.s6_addr[0x8], 4);
271         mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
272                                        &match.key->dst.s6_addr[0xC],
273                                        &match.mask->dst.s6_addr[0xC], 4);
274 }
275
276 static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
277                                        struct mlxsw_sp_acl_rule_info *rulei,
278                                        struct flow_cls_offload *f,
279                                        u8 ip_proto)
280 {
281         const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
282         struct flow_match_ports match;
283
284         if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS))
285                 return 0;
286
287         if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
288                 NL_SET_ERR_MSG_MOD(f->common.extack, "Only UDP and TCP keys are supported");
289                 dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n");
290                 return -EINVAL;
291         }
292
293         flow_rule_match_ports(rule, &match);
294         mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT,
295                                        ntohs(match.key->dst),
296                                        ntohs(match.mask->dst));
297         mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT,
298                                        ntohs(match.key->src),
299                                        ntohs(match.mask->src));
300         return 0;
301 }
302
303 static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
304                                      struct mlxsw_sp_acl_rule_info *rulei,
305                                      struct flow_cls_offload *f,
306                                      u8 ip_proto)
307 {
308         const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
309         struct flow_match_tcp match;
310
311         if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP))
312                 return 0;
313
314         if (ip_proto != IPPROTO_TCP) {
315                 NL_SET_ERR_MSG_MOD(f->common.extack, "TCP keys supported only for TCP");
316                 dev_err(mlxsw_sp->bus_info->dev, "TCP keys supported only for TCP\n");
317                 return -EINVAL;
318         }
319
320         flow_rule_match_tcp(rule, &match);
321
322         if (match.mask->flags & htons(0x0E00)) {
323                 NL_SET_ERR_MSG_MOD(f->common.extack, "TCP flags match not supported on reserved bits");
324                 dev_err(mlxsw_sp->bus_info->dev, "TCP flags match not supported on reserved bits\n");
325                 return -EINVAL;
326         }
327
328         mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS,
329                                        ntohs(match.key->flags),
330                                        ntohs(match.mask->flags));
331         return 0;
332 }
333
334 static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
335                                     struct mlxsw_sp_acl_rule_info *rulei,
336                                     struct flow_cls_offload *f,
337                                     u16 n_proto)
338 {
339         const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
340         struct flow_match_ip match;
341
342         if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP))
343                 return 0;
344
345         if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) {
346                 NL_SET_ERR_MSG_MOD(f->common.extack, "IP keys supported only for IPv4/6");
347                 dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n");
348                 return -EINVAL;
349         }
350
351         flow_rule_match_ip(rule, &match);
352
353         mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_,
354                                        match.key->ttl, match.mask->ttl);
355
356         mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN,
357                                        match.key->tos & 0x3,
358                                        match.mask->tos & 0x3);
359
360         mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP,
361                                        match.key->tos >> 2,
362                                        match.mask->tos >> 2);
363
364         return 0;
365 }
366
367 static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
368                                  struct mlxsw_sp_acl_block *block,
369                                  struct mlxsw_sp_acl_rule_info *rulei,
370                                  struct flow_cls_offload *f)
371 {
372         struct flow_rule *rule = flow_cls_offload_flow_rule(f);
373         struct flow_dissector *dissector = rule->match.dissector;
374         u16 n_proto_mask = 0;
375         u16 n_proto_key = 0;
376         u16 addr_type = 0;
377         u8 ip_proto = 0;
378         int err;
379
380         if (dissector->used_keys &
381             ~(BIT(FLOW_DISSECTOR_KEY_META) |
382               BIT(FLOW_DISSECTOR_KEY_CONTROL) |
383               BIT(FLOW_DISSECTOR_KEY_BASIC) |
384               BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
385               BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
386               BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
387               BIT(FLOW_DISSECTOR_KEY_PORTS) |
388               BIT(FLOW_DISSECTOR_KEY_TCP) |
389               BIT(FLOW_DISSECTOR_KEY_IP) |
390               BIT(FLOW_DISSECTOR_KEY_VLAN))) {
391                 dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n");
392                 NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
393                 return -EOPNOTSUPP;
394         }
395
396         mlxsw_sp_acl_rulei_priority(rulei, f->common.prio);
397
398         err = mlxsw_sp_flower_parse_meta(rulei, f, block);
399         if (err)
400                 return err;
401
402         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
403                 struct flow_match_control match;
404
405                 flow_rule_match_control(rule, &match);
406                 addr_type = match.key->addr_type;
407         }
408
409         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
410                 struct flow_match_basic match;
411
412                 flow_rule_match_basic(rule, &match);
413                 n_proto_key = ntohs(match.key->n_proto);
414                 n_proto_mask = ntohs(match.mask->n_proto);
415
416                 if (n_proto_key == ETH_P_ALL) {
417                         n_proto_key = 0;
418                         n_proto_mask = 0;
419                 }
420                 mlxsw_sp_acl_rulei_keymask_u32(rulei,
421                                                MLXSW_AFK_ELEMENT_ETHERTYPE,
422                                                n_proto_key, n_proto_mask);
423
424                 ip_proto = match.key->ip_proto;
425                 mlxsw_sp_acl_rulei_keymask_u32(rulei,
426                                                MLXSW_AFK_ELEMENT_IP_PROTO,
427                                                match.key->ip_proto,
428                                                match.mask->ip_proto);
429         }
430
431         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
432                 struct flow_match_eth_addrs match;
433
434                 flow_rule_match_eth_addrs(rule, &match);
435                 mlxsw_sp_acl_rulei_keymask_buf(rulei,
436                                                MLXSW_AFK_ELEMENT_DMAC_32_47,
437                                                match.key->dst,
438                                                match.mask->dst, 2);
439                 mlxsw_sp_acl_rulei_keymask_buf(rulei,
440                                                MLXSW_AFK_ELEMENT_DMAC_0_31,
441                                                match.key->dst + 2,
442                                                match.mask->dst + 2, 4);
443                 mlxsw_sp_acl_rulei_keymask_buf(rulei,
444                                                MLXSW_AFK_ELEMENT_SMAC_32_47,
445                                                match.key->src,
446                                                match.mask->src, 2);
447                 mlxsw_sp_acl_rulei_keymask_buf(rulei,
448                                                MLXSW_AFK_ELEMENT_SMAC_0_31,
449                                                match.key->src + 2,
450                                                match.mask->src + 2, 4);
451         }
452
453         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
454                 struct flow_match_vlan match;
455
456                 flow_rule_match_vlan(rule, &match);
457                 if (mlxsw_sp_acl_block_is_egress_bound(block)) {
458                         NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
459                         return -EOPNOTSUPP;
460                 }
461
462                 /* Forbid block with this rulei to be bound
463                  * to egress in future.
464                  */
465                 rulei->egress_bind_blocker = 1;
466
467                 if (match.mask->vlan_id != 0)
468                         mlxsw_sp_acl_rulei_keymask_u32(rulei,
469                                                        MLXSW_AFK_ELEMENT_VID,
470                                                        match.key->vlan_id,
471                                                        match.mask->vlan_id);
472                 if (match.mask->vlan_priority != 0)
473                         mlxsw_sp_acl_rulei_keymask_u32(rulei,
474                                                        MLXSW_AFK_ELEMENT_PCP,
475                                                        match.key->vlan_priority,
476                                                        match.mask->vlan_priority);
477         }
478
479         if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS)
480                 mlxsw_sp_flower_parse_ipv4(rulei, f);
481
482         if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS)
483                 mlxsw_sp_flower_parse_ipv6(rulei, f);
484
485         err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto);
486         if (err)
487                 return err;
488         err = mlxsw_sp_flower_parse_tcp(mlxsw_sp, rulei, f, ip_proto);
489         if (err)
490                 return err;
491
492         err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask);
493         if (err)
494                 return err;
495
496         return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei,
497                                              &f->rule->action,
498                                              f->common.extack);
499 }
500
501 int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
502                             struct mlxsw_sp_acl_block *block,
503                             struct flow_cls_offload *f)
504 {
505         struct mlxsw_sp_acl_rule_info *rulei;
506         struct mlxsw_sp_acl_ruleset *ruleset;
507         struct mlxsw_sp_acl_rule *rule;
508         int err;
509
510         ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
511                                            f->common.chain_index,
512                                            MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
513         if (IS_ERR(ruleset))
514                 return PTR_ERR(ruleset);
515
516         rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie, NULL,
517                                         f->common.extack);
518         if (IS_ERR(rule)) {
519                 err = PTR_ERR(rule);
520                 goto err_rule_create;
521         }
522
523         rulei = mlxsw_sp_acl_rule_rulei(rule);
524         err = mlxsw_sp_flower_parse(mlxsw_sp, block, rulei, f);
525         if (err)
526                 goto err_flower_parse;
527
528         err = mlxsw_sp_acl_rulei_commit(rulei);
529         if (err)
530                 goto err_rulei_commit;
531
532         err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
533         if (err)
534                 goto err_rule_add;
535
536         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
537         return 0;
538
539 err_rule_add:
540 err_rulei_commit:
541 err_flower_parse:
542         mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
543 err_rule_create:
544         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
545         return err;
546 }
547
548 void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
549                              struct mlxsw_sp_acl_block *block,
550                              struct flow_cls_offload *f)
551 {
552         struct mlxsw_sp_acl_ruleset *ruleset;
553         struct mlxsw_sp_acl_rule *rule;
554
555         ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
556                                            f->common.chain_index,
557                                            MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
558         if (IS_ERR(ruleset))
559                 return;
560
561         rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
562         if (rule) {
563                 mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
564                 mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
565         }
566
567         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
568 }
569
570 int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
571                           struct mlxsw_sp_acl_block *block,
572                           struct flow_cls_offload *f)
573 {
574         struct mlxsw_sp_acl_ruleset *ruleset;
575         struct mlxsw_sp_acl_rule *rule;
576         u64 packets;
577         u64 lastuse;
578         u64 bytes;
579         int err;
580
581         ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
582                                            f->common.chain_index,
583                                            MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
584         if (WARN_ON(IS_ERR(ruleset)))
585                 return -EINVAL;
586
587         rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
588         if (!rule)
589                 return -EINVAL;
590
591         err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
592                                           &lastuse);
593         if (err)
594                 goto err_rule_get_stats;
595
596         flow_stats_update(&f->stats, bytes, packets, lastuse);
597
598         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
599         return 0;
600
601 err_rule_get_stats:
602         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
603         return err;
604 }
605
606 int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
607                                  struct mlxsw_sp_acl_block *block,
608                                  struct flow_cls_offload *f)
609 {
610         struct mlxsw_sp_acl_ruleset *ruleset;
611         struct mlxsw_sp_acl_rule_info rulei;
612         int err;
613
614         memset(&rulei, 0, sizeof(rulei));
615         err = mlxsw_sp_flower_parse(mlxsw_sp, block, &rulei, f);
616         if (err)
617                 return err;
618         ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
619                                            f->common.chain_index,
620                                            MLXSW_SP_ACL_PROFILE_FLOWER,
621                                            &rulei.values.elusage);
622
623         /* keep the reference to the ruleset */
624         return PTR_ERR_OR_ZERO(ruleset);
625 }
626
627 void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
628                                    struct mlxsw_sp_acl_block *block,
629                                    struct flow_cls_offload *f)
630 {
631         struct mlxsw_sp_acl_ruleset *ruleset;
632
633         ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
634                                            f->common.chain_index,
635                                            MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
636         if (IS_ERR(ruleset))
637                 return;
638         /* put the reference to the ruleset kept in create */
639         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
640         mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
641 }