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 tcf_exts *exts,
21 					 struct netlink_ext_ack *extack)
22 {
23 	const struct tc_action *a;
24 	int err, i;
25 
26 	if (!tcf_exts_has_actions(exts))
27 		return 0;
28 
29 	/* Count action is inserted first */
30 	err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack);
31 	if (err)
32 		return err;
33 
34 	tcf_exts_for_each_action(i, a, exts) {
35 		if (is_tcf_gact_ok(a)) {
36 			err = mlxsw_sp_acl_rulei_act_terminate(rulei);
37 			if (err) {
38 				NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action");
39 				return err;
40 			}
41 		} else if (is_tcf_gact_shot(a)) {
42 			err = mlxsw_sp_acl_rulei_act_drop(rulei);
43 			if (err) {
44 				NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action");
45 				return err;
46 			}
47 		} else if (is_tcf_gact_trap(a)) {
48 			err = mlxsw_sp_acl_rulei_act_trap(rulei);
49 			if (err) {
50 				NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action");
51 				return err;
52 			}
53 		} else if (is_tcf_gact_goto_chain(a)) {
54 			u32 chain_index = tcf_gact_goto_chain_index(a);
55 			struct mlxsw_sp_acl_ruleset *ruleset;
56 			u16 group_id;
57 
58 			ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block,
59 							      chain_index,
60 							      MLXSW_SP_ACL_PROFILE_FLOWER);
61 			if (IS_ERR(ruleset))
62 				return PTR_ERR(ruleset);
63 
64 			group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
65 			err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id);
66 			if (err) {
67 				NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action");
68 				return err;
69 			}
70 		} else if (is_tcf_mirred_egress_redirect(a)) {
71 			struct net_device *out_dev;
72 			struct mlxsw_sp_fid *fid;
73 			u16 fid_index;
74 
75 			fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp);
76 			fid_index = mlxsw_sp_fid_index(fid);
77 			err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei,
78 							     fid_index, extack);
79 			if (err)
80 				return err;
81 
82 			out_dev = tcf_mirred_dev(a);
83 			err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei,
84 							 out_dev, extack);
85 			if (err)
86 				return err;
87 		} else if (is_tcf_mirred_egress_mirror(a)) {
88 			struct net_device *out_dev = tcf_mirred_dev(a);
89 
90 			err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
91 							    block, out_dev,
92 							    extack);
93 			if (err)
94 				return err;
95 		} else if (is_tcf_vlan(a)) {
96 			u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
97 			u32 action = tcf_vlan_action(a);
98 			u8 prio = tcf_vlan_push_prio(a);
99 			u16 vid = tcf_vlan_push_vid(a);
100 
101 			return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei,
102 							   action, vid,
103 							   proto, prio, extack);
104 		} else {
105 			NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
106 			dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
107 			return -EOPNOTSUPP;
108 		}
109 	}
110 	return 0;
111 }
112 
113 static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei,
114 				       struct tc_cls_flower_offload *f)
115 {
116 	struct flow_dissector_key_ipv4_addrs *key =
117 		skb_flow_dissector_target(f->dissector,
118 					  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
119 					  f->key);
120 	struct flow_dissector_key_ipv4_addrs *mask =
121 		skb_flow_dissector_target(f->dissector,
122 					  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
123 					  f->mask);
124 
125 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
126 				       (char *) &key->src,
127 				       (char *) &mask->src, 4);
128 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
129 				       (char *) &key->dst,
130 				       (char *) &mask->dst, 4);
131 }
132 
133 static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei,
134 				       struct tc_cls_flower_offload *f)
135 {
136 	struct flow_dissector_key_ipv6_addrs *key =
137 		skb_flow_dissector_target(f->dissector,
138 					  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
139 					  f->key);
140 	struct flow_dissector_key_ipv6_addrs *mask =
141 		skb_flow_dissector_target(f->dissector,
142 					  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
143 					  f->mask);
144 
145 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
146 				       &key->src.s6_addr[0x0],
147 				       &mask->src.s6_addr[0x0], 4);
148 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
149 				       &key->src.s6_addr[0x4],
150 				       &mask->src.s6_addr[0x4], 4);
151 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
152 				       &key->src.s6_addr[0x8],
153 				       &mask->src.s6_addr[0x8], 4);
154 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
155 				       &key->src.s6_addr[0xC],
156 				       &mask->src.s6_addr[0xC], 4);
157 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
158 				       &key->dst.s6_addr[0x0],
159 				       &mask->dst.s6_addr[0x0], 4);
160 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
161 				       &key->dst.s6_addr[0x4],
162 				       &mask->dst.s6_addr[0x4], 4);
163 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
164 				       &key->dst.s6_addr[0x8],
165 				       &mask->dst.s6_addr[0x8], 4);
166 	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
167 				       &key->dst.s6_addr[0xC],
168 				       &mask->dst.s6_addr[0xC], 4);
169 }
170 
171 static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
172 				       struct mlxsw_sp_acl_rule_info *rulei,
173 				       struct tc_cls_flower_offload *f,
174 				       u8 ip_proto)
175 {
176 	struct flow_dissector_key_ports *key, *mask;
177 
178 	if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS))
179 		return 0;
180 
181 	if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
182 		NL_SET_ERR_MSG_MOD(f->common.extack, "Only UDP and TCP keys are supported");
183 		dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n");
184 		return -EINVAL;
185 	}
186 
187 	key = skb_flow_dissector_target(f->dissector,
188 					FLOW_DISSECTOR_KEY_PORTS,
189 					f->key);
190 	mask = skb_flow_dissector_target(f->dissector,
191 					 FLOW_DISSECTOR_KEY_PORTS,
192 					 f->mask);
193 	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT,
194 				       ntohs(key->dst), ntohs(mask->dst));
195 	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT,
196 				       ntohs(key->src), ntohs(mask->src));
197 	return 0;
198 }
199 
200 static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
201 				     struct mlxsw_sp_acl_rule_info *rulei,
202 				     struct tc_cls_flower_offload *f,
203 				     u8 ip_proto)
204 {
205 	struct flow_dissector_key_tcp *key, *mask;
206 
207 	if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP))
208 		return 0;
209 
210 	if (ip_proto != IPPROTO_TCP) {
211 		NL_SET_ERR_MSG_MOD(f->common.extack, "TCP keys supported only for TCP");
212 		dev_err(mlxsw_sp->bus_info->dev, "TCP keys supported only for TCP\n");
213 		return -EINVAL;
214 	}
215 
216 	key = skb_flow_dissector_target(f->dissector,
217 					FLOW_DISSECTOR_KEY_TCP,
218 					f->key);
219 	mask = skb_flow_dissector_target(f->dissector,
220 					 FLOW_DISSECTOR_KEY_TCP,
221 					 f->mask);
222 	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS,
223 				       ntohs(key->flags), ntohs(mask->flags));
224 	return 0;
225 }
226 
227 static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
228 				    struct mlxsw_sp_acl_rule_info *rulei,
229 				    struct tc_cls_flower_offload *f,
230 				    u16 n_proto)
231 {
232 	struct flow_dissector_key_ip *key, *mask;
233 
234 	if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP))
235 		return 0;
236 
237 	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) {
238 		NL_SET_ERR_MSG_MOD(f->common.extack, "IP keys supported only for IPv4/6");
239 		dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n");
240 		return -EINVAL;
241 	}
242 
243 	key = skb_flow_dissector_target(f->dissector,
244 					FLOW_DISSECTOR_KEY_IP,
245 					f->key);
246 	mask = skb_flow_dissector_target(f->dissector,
247 					 FLOW_DISSECTOR_KEY_IP,
248 					 f->mask);
249 	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_,
250 				       key->ttl, mask->ttl);
251 
252 	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN,
253 				       key->tos & 0x3, mask->tos & 0x3);
254 
255 	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP,
256 				       key->tos >> 6, mask->tos >> 6);
257 
258 	return 0;
259 }
260 
261 static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
262 				 struct mlxsw_sp_acl_block *block,
263 				 struct mlxsw_sp_acl_rule_info *rulei,
264 				 struct tc_cls_flower_offload *f)
265 {
266 	u16 n_proto_mask = 0;
267 	u16 n_proto_key = 0;
268 	u16 addr_type = 0;
269 	u8 ip_proto = 0;
270 	int err;
271 
272 	if (f->dissector->used_keys &
273 	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
274 	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
275 	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
276 	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
277 	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
278 	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
279 	      BIT(FLOW_DISSECTOR_KEY_TCP) |
280 	      BIT(FLOW_DISSECTOR_KEY_IP) |
281 	      BIT(FLOW_DISSECTOR_KEY_VLAN))) {
282 		dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n");
283 		NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
284 		return -EOPNOTSUPP;
285 	}
286 
287 	mlxsw_sp_acl_rulei_priority(rulei, f->common.prio);
288 
289 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
290 		struct flow_dissector_key_control *key =
291 			skb_flow_dissector_target(f->dissector,
292 						  FLOW_DISSECTOR_KEY_CONTROL,
293 						  f->key);
294 		addr_type = key->addr_type;
295 	}
296 
297 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
298 		struct flow_dissector_key_basic *key =
299 			skb_flow_dissector_target(f->dissector,
300 						  FLOW_DISSECTOR_KEY_BASIC,
301 						  f->key);
302 		struct flow_dissector_key_basic *mask =
303 			skb_flow_dissector_target(f->dissector,
304 						  FLOW_DISSECTOR_KEY_BASIC,
305 						  f->mask);
306 		n_proto_key = ntohs(key->n_proto);
307 		n_proto_mask = ntohs(mask->n_proto);
308 
309 		if (n_proto_key == ETH_P_ALL) {
310 			n_proto_key = 0;
311 			n_proto_mask = 0;
312 		}
313 		mlxsw_sp_acl_rulei_keymask_u32(rulei,
314 					       MLXSW_AFK_ELEMENT_ETHERTYPE,
315 					       n_proto_key, n_proto_mask);
316 
317 		ip_proto = key->ip_proto;
318 		mlxsw_sp_acl_rulei_keymask_u32(rulei,
319 					       MLXSW_AFK_ELEMENT_IP_PROTO,
320 					       key->ip_proto, mask->ip_proto);
321 	}
322 
323 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
324 		struct flow_dissector_key_eth_addrs *key =
325 			skb_flow_dissector_target(f->dissector,
326 						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
327 						  f->key);
328 		struct flow_dissector_key_eth_addrs *mask =
329 			skb_flow_dissector_target(f->dissector,
330 						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
331 						  f->mask);
332 
333 		mlxsw_sp_acl_rulei_keymask_buf(rulei,
334 					       MLXSW_AFK_ELEMENT_DMAC_32_47,
335 					       key->dst, mask->dst, 2);
336 		mlxsw_sp_acl_rulei_keymask_buf(rulei,
337 					       MLXSW_AFK_ELEMENT_DMAC_0_31,
338 					       key->dst + 2, mask->dst + 2, 4);
339 		mlxsw_sp_acl_rulei_keymask_buf(rulei,
340 					       MLXSW_AFK_ELEMENT_SMAC_32_47,
341 					       key->src, mask->src, 2);
342 		mlxsw_sp_acl_rulei_keymask_buf(rulei,
343 					       MLXSW_AFK_ELEMENT_SMAC_0_31,
344 					       key->src + 2, mask->src + 2, 4);
345 	}
346 
347 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
348 		struct flow_dissector_key_vlan *key =
349 			skb_flow_dissector_target(f->dissector,
350 						  FLOW_DISSECTOR_KEY_VLAN,
351 						  f->key);
352 		struct flow_dissector_key_vlan *mask =
353 			skb_flow_dissector_target(f->dissector,
354 						  FLOW_DISSECTOR_KEY_VLAN,
355 						  f->mask);
356 
357 		if (mlxsw_sp_acl_block_is_egress_bound(block)) {
358 			NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
359 			return -EOPNOTSUPP;
360 		}
361 		if (mask->vlan_id != 0)
362 			mlxsw_sp_acl_rulei_keymask_u32(rulei,
363 						       MLXSW_AFK_ELEMENT_VID,
364 						       key->vlan_id,
365 						       mask->vlan_id);
366 		if (mask->vlan_priority != 0)
367 			mlxsw_sp_acl_rulei_keymask_u32(rulei,
368 						       MLXSW_AFK_ELEMENT_PCP,
369 						       key->vlan_priority,
370 						       mask->vlan_priority);
371 	}
372 
373 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS)
374 		mlxsw_sp_flower_parse_ipv4(rulei, f);
375 
376 	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS)
377 		mlxsw_sp_flower_parse_ipv6(rulei, f);
378 
379 	err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto);
380 	if (err)
381 		return err;
382 	err = mlxsw_sp_flower_parse_tcp(mlxsw_sp, rulei, f, ip_proto);
383 	if (err)
384 		return err;
385 
386 	err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask);
387 	if (err)
388 		return err;
389 
390 	return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei, f->exts,
391 					     f->common.extack);
392 }
393 
394 int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
395 			    struct mlxsw_sp_acl_block *block,
396 			    struct tc_cls_flower_offload *f)
397 {
398 	struct mlxsw_sp_acl_rule_info *rulei;
399 	struct mlxsw_sp_acl_ruleset *ruleset;
400 	struct mlxsw_sp_acl_rule *rule;
401 	int err;
402 
403 	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
404 					   f->common.chain_index,
405 					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
406 	if (IS_ERR(ruleset))
407 		return PTR_ERR(ruleset);
408 
409 	rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie, NULL,
410 					f->common.extack);
411 	if (IS_ERR(rule)) {
412 		err = PTR_ERR(rule);
413 		goto err_rule_create;
414 	}
415 
416 	rulei = mlxsw_sp_acl_rule_rulei(rule);
417 	err = mlxsw_sp_flower_parse(mlxsw_sp, block, rulei, f);
418 	if (err)
419 		goto err_flower_parse;
420 
421 	err = mlxsw_sp_acl_rulei_commit(rulei);
422 	if (err)
423 		goto err_rulei_commit;
424 
425 	err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
426 	if (err)
427 		goto err_rule_add;
428 
429 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
430 	return 0;
431 
432 err_rule_add:
433 err_rulei_commit:
434 err_flower_parse:
435 	mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
436 err_rule_create:
437 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
438 	return err;
439 }
440 
441 void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
442 			     struct mlxsw_sp_acl_block *block,
443 			     struct tc_cls_flower_offload *f)
444 {
445 	struct mlxsw_sp_acl_ruleset *ruleset;
446 	struct mlxsw_sp_acl_rule *rule;
447 
448 	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
449 					   f->common.chain_index,
450 					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
451 	if (IS_ERR(ruleset))
452 		return;
453 
454 	rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
455 	if (rule) {
456 		mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
457 		mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
458 	}
459 
460 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
461 }
462 
463 int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
464 			  struct mlxsw_sp_acl_block *block,
465 			  struct tc_cls_flower_offload *f)
466 {
467 	struct mlxsw_sp_acl_ruleset *ruleset;
468 	struct mlxsw_sp_acl_rule *rule;
469 	u64 packets;
470 	u64 lastuse;
471 	u64 bytes;
472 	int err;
473 
474 	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
475 					   f->common.chain_index,
476 					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
477 	if (WARN_ON(IS_ERR(ruleset)))
478 		return -EINVAL;
479 
480 	rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
481 	if (!rule)
482 		return -EINVAL;
483 
484 	err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
485 					  &lastuse);
486 	if (err)
487 		goto err_rule_get_stats;
488 
489 	tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
490 
491 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
492 	return 0;
493 
494 err_rule_get_stats:
495 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
496 	return err;
497 }
498 
499 int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
500 				 struct mlxsw_sp_acl_block *block,
501 				 struct tc_cls_flower_offload *f)
502 {
503 	struct mlxsw_sp_acl_ruleset *ruleset;
504 	struct mlxsw_sp_acl_rule_info rulei;
505 	int err;
506 
507 	memset(&rulei, 0, sizeof(rulei));
508 	err = mlxsw_sp_flower_parse(mlxsw_sp, block, &rulei, f);
509 	if (err)
510 		return err;
511 	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
512 					   f->common.chain_index,
513 					   MLXSW_SP_ACL_PROFILE_FLOWER,
514 					   &rulei.values.elusage);
515 
516 	/* keep the reference to the ruleset */
517 	return PTR_ERR_OR_ZERO(ruleset);
518 }
519 
520 void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
521 				   struct mlxsw_sp_acl_block *block,
522 				   struct tc_cls_flower_offload *f)
523 {
524 	struct mlxsw_sp_acl_ruleset *ruleset;
525 
526 	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
527 					   f->common.chain_index,
528 					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
529 	if (IS_ERR(ruleset))
530 		return;
531 	/* put the reference to the ruleset kept in create */
532 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
533 	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
534 }
535