spectrum_flower.c 18.0 KB
Newer Older
1 2
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
3 4 5 6

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
7
#include <net/net_namespace.h>
8 9 10 11
#include <net/flow_dissector.h>
#include <net/pkt_cls.h>
#include <net/tc_act/tc_gact.h>
#include <net/tc_act/tc_mirred.h>
12
#include <net/tc_act/tc_vlan.h>
13 14 15 16 17

#include "spectrum.h"
#include "core_acl_flex_keys.h"

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 34
	if (act->hw_stats == FLOW_ACTION_HW_STATS_ANY ||
	    act->hw_stats == FLOW_ACTION_HW_STATS_IMMEDIATE) {
35 36 37 38
		/* Count action is inserted first */
		err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack);
		if (err)
			return err;
39
	} else if (act->hw_stats != FLOW_ACTION_HW_STATS_DISABLED) {
40 41 42
		NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type");
		return -EOPNOTSUPP;
	}
43

44 45 46
	flow_action_for_each(i, act, flow_action) {
		switch (act->id) {
		case FLOW_ACTION_ACCEPT:
47
			err = mlxsw_sp_acl_rulei_act_terminate(rulei);
48 49
			if (err) {
				NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action");
50
				return err;
51
			}
52
			break;
53 54 55 56 57 58 59 60
		case FLOW_ACTION_DROP: {
			bool ingress;

			if (mlxsw_sp_acl_block_is_mixed_bound(block)) {
				NL_SET_ERR_MSG_MOD(extack, "Drop action is not supported when block is bound to ingress and egress");
				return -EOPNOTSUPP;
			}
			ingress = mlxsw_sp_acl_block_is_ingress_bound(block);
61 62
			err = mlxsw_sp_acl_rulei_act_drop(rulei, ingress,
							  act->cookie, extack);
63 64
			if (err) {
				NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action");
65
				return err;
66
			}
67 68 69 70 71 72 73 74 75 76

			/* Forbid block with this rulei to be bound
			 * to ingress/egress in future. Ingress rule is
			 * a blocker for egress and vice versa.
			 */
			if (ingress)
				rulei->egress_bind_blocker = 1;
			else
				rulei->ingress_bind_blocker = 1;
			}
77 78
			break;
		case FLOW_ACTION_TRAP:
79
			err = mlxsw_sp_acl_rulei_act_trap(rulei);
80 81
			if (err) {
				NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action");
82
				return err;
83
			}
84 85 86
			break;
		case FLOW_ACTION_GOTO: {
			u32 chain_index = act->chain_index;
87 88 89
			struct mlxsw_sp_acl_ruleset *ruleset;
			u16 group_id;

90
			ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block,
91 92 93 94 95 96
							      chain_index,
							      MLXSW_SP_ACL_PROFILE_FLOWER);
			if (IS_ERR(ruleset))
				return PTR_ERR(ruleset);

			group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
97
			err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id);
98 99
			if (err) {
				NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action");
100
				return err;
101
			}
102 103 104
			}
			break;
		case FLOW_ACTION_REDIRECT: {
105
			struct net_device *out_dev;
106 107
			struct mlxsw_sp_fid *fid;
			u16 fid_index;
108

109 110 111 112 113
			if (mlxsw_sp_acl_block_is_egress_bound(block)) {
				NL_SET_ERR_MSG_MOD(extack, "Redirect action is not supported on egress");
				return -EOPNOTSUPP;
			}

114 115 116 117 118
			/* Forbid block with this rulei to be bound
			 * to egress in future.
			 */
			rulei->egress_bind_blocker = 1;

119 120
			fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp);
			fid_index = mlxsw_sp_fid_index(fid);
121
			err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei,
122
							     fid_index, extack);
123 124 125
			if (err)
				return err;

126
			out_dev = act->dev;
127
			err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei,
128
							 out_dev, extack);
129 130
			if (err)
				return err;
131 132 133 134
			}
			break;
		case FLOW_ACTION_MIRRED: {
			struct net_device *out_dev = act->dev;
135

136 137 138 139 140
			if (mirror_act_count++) {
				NL_SET_ERR_MSG_MOD(extack, "Multiple mirror actions per rule are not supported");
				return -EOPNOTSUPP;
			}

141
			err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
142 143
							    block, out_dev,
							    extack);
144 145
			if (err)
				return err;
146 147
			}
			break;
148
		case FLOW_ACTION_VLAN_MANGLE: {
149 150 151
			u16 proto = be16_to_cpu(act->vlan.proto);
			u8 prio = act->vlan.prio;
			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 158 159 160
		case FLOW_ACTION_PRIORITY:
			return mlxsw_sp_acl_rulei_act_priority(mlxsw_sp, rulei,
							       act->priority,
							       extack);
161
		default:
162
			NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
163 164 165 166 167 168 169
			dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
			return -EOPNOTSUPP;
		}
	}
	return 0;
}

170
static int mlxsw_sp_flower_parse_meta(struct mlxsw_sp_acl_rule_info *rulei,
171
				      struct flow_cls_offload *f,
172 173
				      struct mlxsw_sp_acl_block *block)
{
174
	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
	struct mlxsw_sp_port *mlxsw_sp_port;
	struct net_device *ingress_dev;
	struct flow_match_meta match;

	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META))
		return 0;

	flow_rule_match_meta(rule, &match);
	if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
		NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported ingress ifindex mask");
		return -EINVAL;
	}

	ingress_dev = __dev_get_by_index(block->net,
					 match.key->ingress_ifindex);
	if (!ingress_dev) {
		NL_SET_ERR_MSG_MOD(f->common.extack, "Can't find specified ingress port to match on");
		return -EINVAL;
	}

	if (!mlxsw_sp_port_dev_check(ingress_dev)) {
		NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on non-mlxsw ingress port");
		return -EINVAL;
	}

	mlxsw_sp_port = netdev_priv(ingress_dev);
	if (mlxsw_sp_port->mlxsw_sp != block->mlxsw_sp) {
		NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on a port from different device");
		return -EINVAL;
	}

	mlxsw_sp_acl_rulei_keymask_u32(rulei,
				       MLXSW_AFK_ELEMENT_SRC_SYS_PORT,
				       mlxsw_sp_port->local_port,
				       0xFFFFFFFF);
	return 0;
}

213
static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei,
214
				       struct flow_cls_offload *f)
215
{
216 217 218
	struct flow_match_ipv4_addrs match;

	flow_rule_match_ipv4_addrs(f->rule, &match);
219

220
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
221 222
				       (char *) &match.key->src,
				       (char *) &match.mask->src, 4);
223
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
224 225
				       (char *) &match.key->dst,
				       (char *) &match.mask->dst, 4);
226 227 228
}

static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei,
229
				       struct flow_cls_offload *f)
230
{
231 232 233
	struct flow_match_ipv6_addrs match;

	flow_rule_match_ipv6_addrs(f->rule, &match);
234 235

	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
236 237
				       &match.key->src.s6_addr[0x0],
				       &match.mask->src.s6_addr[0x0], 4);
238
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
239 240
				       &match.key->src.s6_addr[0x4],
				       &match.mask->src.s6_addr[0x4], 4);
241
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
242 243
				       &match.key->src.s6_addr[0x8],
				       &match.mask->src.s6_addr[0x8], 4);
244
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
245 246
				       &match.key->src.s6_addr[0xC],
				       &match.mask->src.s6_addr[0xC], 4);
247
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
248 249
				       &match.key->dst.s6_addr[0x0],
				       &match.mask->dst.s6_addr[0x0], 4);
250
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
251 252
				       &match.key->dst.s6_addr[0x4],
				       &match.mask->dst.s6_addr[0x4], 4);
253
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
254 255
				       &match.key->dst.s6_addr[0x8],
				       &match.mask->dst.s6_addr[0x8], 4);
256
	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
257 258
				       &match.key->dst.s6_addr[0xC],
				       &match.mask->dst.s6_addr[0xC], 4);
259 260 261 262
}

static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
				       struct mlxsw_sp_acl_rule_info *rulei,
263
				       struct flow_cls_offload *f,
264 265
				       u8 ip_proto)
{
266
	const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
267
	struct flow_match_ports match;
268

269
	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS))
270 271 272
		return 0;

	if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
273
		NL_SET_ERR_MSG_MOD(f->common.extack, "Only UDP and TCP keys are supported");
274 275 276 277
		dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n");
		return -EINVAL;
	}

278
	flow_rule_match_ports(rule, &match);
279
	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT,
280 281
				       ntohs(match.key->dst),
				       ntohs(match.mask->dst));
282
	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT,
283 284
				       ntohs(match.key->src),
				       ntohs(match.mask->src));
285 286 287
	return 0;
}

288 289
static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
				     struct mlxsw_sp_acl_rule_info *rulei,
290
				     struct flow_cls_offload *f,
291 292
				     u8 ip_proto)
{
293
	const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
294
	struct flow_match_tcp match;
295

296
	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP))
297 298 299
		return 0;

	if (ip_proto != IPPROTO_TCP) {
300
		NL_SET_ERR_MSG_MOD(f->common.extack, "TCP keys supported only for TCP");
301 302 303 304
		dev_err(mlxsw_sp->bus_info->dev, "TCP keys supported only for TCP\n");
		return -EINVAL;
	}

305 306
	flow_rule_match_tcp(rule, &match);

307 308 309 310 311 312
	if (match.mask->flags & htons(0x0E00)) {
		NL_SET_ERR_MSG_MOD(f->common.extack, "TCP flags match not supported on reserved bits");
		dev_err(mlxsw_sp->bus_info->dev, "TCP flags match not supported on reserved bits\n");
		return -EINVAL;
	}

313
	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS,
314 315
				       ntohs(match.key->flags),
				       ntohs(match.mask->flags));
316 317 318
	return 0;
}

319 320
static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
				    struct mlxsw_sp_acl_rule_info *rulei,
321
				    struct flow_cls_offload *f,
322 323
				    u16 n_proto)
{
324
	const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
325
	struct flow_match_ip match;
326

327
	if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP))
328 329 330
		return 0;

	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) {
331
		NL_SET_ERR_MSG_MOD(f->common.extack, "IP keys supported only for IPv4/6");
332 333 334 335
		dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n");
		return -EINVAL;
	}

336 337
	flow_rule_match_ip(rule, &match);

338
	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_,
339
				       match.key->ttl, match.mask->ttl);
340 341

	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN,
342 343
				       match.key->tos & 0x3,
				       match.mask->tos & 0x3);
344 345

	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP,
346 347
				       match.key->tos >> 2,
				       match.mask->tos >> 2);
348

349 350 351
	return 0;
}

352
static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
353
				 struct mlxsw_sp_acl_block *block,
354
				 struct mlxsw_sp_acl_rule_info *rulei,
355
				 struct flow_cls_offload *f)
356
{
357
	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
358
	struct flow_dissector *dissector = rule->match.dissector;
359 360
	u16 n_proto_mask = 0;
	u16 n_proto_key = 0;
361 362 363 364
	u16 addr_type = 0;
	u8 ip_proto = 0;
	int err;

365
	if (dissector->used_keys &
366 367
	    ~(BIT(FLOW_DISSECTOR_KEY_META) |
	      BIT(FLOW_DISSECTOR_KEY_CONTROL) |
368 369 370 371
	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
372
	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
373
	      BIT(FLOW_DISSECTOR_KEY_TCP) |
374
	      BIT(FLOW_DISSECTOR_KEY_IP) |
375
	      BIT(FLOW_DISSECTOR_KEY_VLAN))) {
376
		dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n");
377
		NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
378 379 380
		return -EOPNOTSUPP;
	}

381
	mlxsw_sp_acl_rulei_priority(rulei, f->common.prio);
382

383 384 385 386
	err = mlxsw_sp_flower_parse_meta(rulei, f, block);
	if (err)
		return err;

387 388 389 390 391
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
		struct flow_match_control match;

		flow_rule_match_control(rule, &match);
		addr_type = match.key->addr_type;
392 393
	}

394 395 396 397 398 399
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
		struct flow_match_basic match;

		flow_rule_match_basic(rule, &match);
		n_proto_key = ntohs(match.key->n_proto);
		n_proto_mask = ntohs(match.mask->n_proto);
400 401 402 403 404

		if (n_proto_key == ETH_P_ALL) {
			n_proto_key = 0;
			n_proto_mask = 0;
		}
405 406
		mlxsw_sp_acl_rulei_keymask_u32(rulei,
					       MLXSW_AFK_ELEMENT_ETHERTYPE,
407 408
					       n_proto_key, n_proto_mask);

409
		ip_proto = match.key->ip_proto;
410 411
		mlxsw_sp_acl_rulei_keymask_u32(rulei,
					       MLXSW_AFK_ELEMENT_IP_PROTO,
412 413
					       match.key->ip_proto,
					       match.mask->ip_proto);
414 415
	}

416 417
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
		struct flow_match_eth_addrs match;
418

419
		flow_rule_match_eth_addrs(rule, &match);
420
		mlxsw_sp_acl_rulei_keymask_buf(rulei,
421
					       MLXSW_AFK_ELEMENT_DMAC_32_47,
422 423
					       match.key->dst,
					       match.mask->dst, 2);
424 425
		mlxsw_sp_acl_rulei_keymask_buf(rulei,
					       MLXSW_AFK_ELEMENT_DMAC_0_31,
426 427
					       match.key->dst + 2,
					       match.mask->dst + 2, 4);
428 429
		mlxsw_sp_acl_rulei_keymask_buf(rulei,
					       MLXSW_AFK_ELEMENT_SMAC_32_47,
430 431
					       match.key->src,
					       match.mask->src, 2);
432
		mlxsw_sp_acl_rulei_keymask_buf(rulei,
433
					       MLXSW_AFK_ELEMENT_SMAC_0_31,
434 435
					       match.key->src + 2,
					       match.mask->src + 2, 4);
436 437
	}

438 439
	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
		struct flow_match_vlan match;
440

441
		flow_rule_match_vlan(rule, &match);
442 443 444 445
		if (mlxsw_sp_acl_block_is_egress_bound(block)) {
			NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
			return -EOPNOTSUPP;
		}
446 447 448 449 450 451

		/* Forbid block with this rulei to be bound
		 * to egress in future.
		 */
		rulei->egress_bind_blocker = 1;

452
		if (match.mask->vlan_id != 0)
453 454
			mlxsw_sp_acl_rulei_keymask_u32(rulei,
						       MLXSW_AFK_ELEMENT_VID,
455 456 457
						       match.key->vlan_id,
						       match.mask->vlan_id);
		if (match.mask->vlan_priority != 0)
458 459
			mlxsw_sp_acl_rulei_keymask_u32(rulei,
						       MLXSW_AFK_ELEMENT_PCP,
460 461
						       match.key->vlan_priority,
						       match.mask->vlan_priority);
462 463
	}

464 465 466 467 468 469 470
	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS)
		mlxsw_sp_flower_parse_ipv4(rulei, f);

	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS)
		mlxsw_sp_flower_parse_ipv6(rulei, f);

	err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto);
471 472 473
	if (err)
		return err;
	err = mlxsw_sp_flower_parse_tcp(mlxsw_sp, rulei, f, ip_proto);
474 475 476
	if (err)
		return err;

477 478 479 480
	err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask);
	if (err)
		return err;

481 482
	return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei,
					     &f->rule->action,
483
					     f->common.extack);
484 485
}

486 487
int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
			    struct mlxsw_sp_acl_block *block,
488
			    struct flow_cls_offload *f)
489 490 491 492 493 494
{
	struct mlxsw_sp_acl_rule_info *rulei;
	struct mlxsw_sp_acl_ruleset *ruleset;
	struct mlxsw_sp_acl_rule *rule;
	int err;

495
	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
496
					   f->common.chain_index,
497
					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
498 499 500
	if (IS_ERR(ruleset))
		return PTR_ERR(ruleset);

501
	rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie, NULL,
502
					f->common.extack);
503 504 505 506 507 508
	if (IS_ERR(rule)) {
		err = PTR_ERR(rule);
		goto err_rule_create;
	}

	rulei = mlxsw_sp_acl_rule_rulei(rule);
509
	err = mlxsw_sp_flower_parse(mlxsw_sp, block, rulei, f);
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
	if (err)
		goto err_flower_parse;

	err = mlxsw_sp_acl_rulei_commit(rulei);
	if (err)
		goto err_rulei_commit;

	err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
	if (err)
		goto err_rule_add;

	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
	return 0;

err_rule_add:
err_rulei_commit:
err_flower_parse:
	mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
err_rule_create:
	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
	return err;
}

533 534
void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
			     struct mlxsw_sp_acl_block *block,
535
			     struct flow_cls_offload *f)
536 537 538 539
{
	struct mlxsw_sp_acl_ruleset *ruleset;
	struct mlxsw_sp_acl_rule *rule;

540 541
	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
					   f->common.chain_index,
542
					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
543
	if (IS_ERR(ruleset))
544 545 546
		return;

	rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
547
	if (rule) {
548 549 550 551 552 553
		mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
		mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
	}

	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
}
554

555 556
int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
			  struct mlxsw_sp_acl_block *block,
557
			  struct flow_cls_offload *f)
558 559 560 561 562 563 564 565
{
	struct mlxsw_sp_acl_ruleset *ruleset;
	struct mlxsw_sp_acl_rule *rule;
	u64 packets;
	u64 lastuse;
	u64 bytes;
	int err;

566 567
	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
					   f->common.chain_index,
568
					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
569 570 571 572 573 574 575
	if (WARN_ON(IS_ERR(ruleset)))
		return -EINVAL;

	rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
	if (!rule)
		return -EINVAL;

576
	err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
577 578 579 580
					  &lastuse);
	if (err)
		goto err_rule_get_stats;

581
	flow_stats_update(&f->stats, bytes, packets, lastuse);
582 583 584 585 586 587 588 589

	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
	return 0;

err_rule_get_stats:
	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
	return err;
}
590 591 592

int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
				 struct mlxsw_sp_acl_block *block,
593
				 struct flow_cls_offload *f)
594 595 596 597 598 599 600 601 602 603 604 605 606
{
	struct mlxsw_sp_acl_ruleset *ruleset;
	struct mlxsw_sp_acl_rule_info rulei;
	int err;

	memset(&rulei, 0, sizeof(rulei));
	err = mlxsw_sp_flower_parse(mlxsw_sp, block, &rulei, f);
	if (err)
		return err;
	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
					   f->common.chain_index,
					   MLXSW_SP_ACL_PROFILE_FLOWER,
					   &rulei.values.elusage);
607

608
	/* keep the reference to the ruleset */
609
	return PTR_ERR_OR_ZERO(ruleset);
610 611 612 613
}

void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
				   struct mlxsw_sp_acl_block *block,
614
				   struct flow_cls_offload *f)
615 616 617 618 619 620 621 622 623 624 625 626
{
	struct mlxsw_sp_acl_ruleset *ruleset;

	ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
					   f->common.chain_index,
					   MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
	if (IS_ERR(ruleset))
		return;
	/* put the reference to the ruleset kept in create */
	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
	mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
}