162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/slab.h>
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include <linux/list.h>
862306a36Sopenharmony_ci#include <linux/string.h>
962306a36Sopenharmony_ci#include <linux/rhashtable.h>
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <net/net_namespace.h>
1362306a36Sopenharmony_ci#include <net/tc_act/tc_vlan.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "reg.h"
1662306a36Sopenharmony_ci#include "core.h"
1762306a36Sopenharmony_ci#include "resources.h"
1862306a36Sopenharmony_ci#include "spectrum.h"
1962306a36Sopenharmony_ci#include "core_acl_flex_keys.h"
2062306a36Sopenharmony_ci#include "core_acl_flex_actions.h"
2162306a36Sopenharmony_ci#include "spectrum_acl_tcam.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct mlxsw_sp_acl {
2462306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
2562306a36Sopenharmony_ci	struct mlxsw_afk *afk;
2662306a36Sopenharmony_ci	struct mlxsw_sp_fid *dummy_fid;
2762306a36Sopenharmony_ci	struct rhashtable ruleset_ht;
2862306a36Sopenharmony_ci	struct list_head rules;
2962306a36Sopenharmony_ci	struct mutex rules_lock; /* Protects rules list */
3062306a36Sopenharmony_ci	struct {
3162306a36Sopenharmony_ci		struct delayed_work dw;
3262306a36Sopenharmony_ci		unsigned long interval;	/* ms */
3362306a36Sopenharmony_ci#define MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS 1000
3462306a36Sopenharmony_ci	} rule_activity_update;
3562306a36Sopenharmony_ci	struct mlxsw_sp_acl_tcam tcam;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return acl->afk;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct mlxsw_sp_acl_tcam *mlxsw_sp_acl_to_tcam(struct mlxsw_sp_acl *acl)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return &acl->tcam;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct mlxsw_sp_acl_ruleset_ht_key {
4962306a36Sopenharmony_ci	struct mlxsw_sp_flow_block *block;
5062306a36Sopenharmony_ci	u32 chain_index;
5162306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct mlxsw_sp_acl_ruleset {
5562306a36Sopenharmony_ci	struct rhash_head ht_node; /* Member of acl HT */
5662306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset_ht_key ht_key;
5762306a36Sopenharmony_ci	struct rhashtable rule_ht;
5862306a36Sopenharmony_ci	unsigned int ref_count;
5962306a36Sopenharmony_ci	unsigned int min_prio;
6062306a36Sopenharmony_ci	unsigned int max_prio;
6162306a36Sopenharmony_ci	unsigned long priv[];
6262306a36Sopenharmony_ci	/* priv has to be always the last item */
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct mlxsw_sp_acl_rule {
6662306a36Sopenharmony_ci	struct rhash_head ht_node; /* Member of rule HT */
6762306a36Sopenharmony_ci	struct list_head list;
6862306a36Sopenharmony_ci	unsigned long cookie; /* HT key */
6962306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset;
7062306a36Sopenharmony_ci	struct mlxsw_sp_acl_rule_info *rulei;
7162306a36Sopenharmony_ci	u64 last_used;
7262306a36Sopenharmony_ci	u64 last_packets;
7362306a36Sopenharmony_ci	u64 last_bytes;
7462306a36Sopenharmony_ci	u64 last_drops;
7562306a36Sopenharmony_ci	unsigned long priv[];
7662306a36Sopenharmony_ci	/* priv has to be always the last item */
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_acl_ruleset_ht_params = {
8062306a36Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_acl_ruleset_ht_key),
8162306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_key),
8262306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_node),
8362306a36Sopenharmony_ci	.automatic_shrinking = true,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_acl_rule_ht_params = {
8762306a36Sopenharmony_ci	.key_len = sizeof(unsigned long),
8862306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_acl_rule, cookie),
8962306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_acl_rule, ht_node),
9062306a36Sopenharmony_ci	.automatic_shrinking = true,
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	return mlxsw_sp->acl->dummy_fid;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic bool
9962306a36Sopenharmony_cimlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	/* We hold a reference on ruleset ourselves */
10262306a36Sopenharmony_ci	return ruleset->ref_count == 2;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciint mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
10662306a36Sopenharmony_ci			      struct mlxsw_sp_flow_block *block,
10762306a36Sopenharmony_ci			      struct mlxsw_sp_flow_block_binding *binding)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
11062306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return ops->ruleset_bind(mlxsw_sp, ruleset->priv,
11362306a36Sopenharmony_ci				 binding->mlxsw_sp_port, binding->ingress);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_civoid mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
11762306a36Sopenharmony_ci				 struct mlxsw_sp_flow_block *block,
11862306a36Sopenharmony_ci				 struct mlxsw_sp_flow_block_binding *binding)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
12162306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	ops->ruleset_unbind(mlxsw_sp, ruleset->priv,
12462306a36Sopenharmony_ci			    binding->mlxsw_sp_port, binding->ingress);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int
12862306a36Sopenharmony_cimlxsw_sp_acl_ruleset_block_bind(struct mlxsw_sp *mlxsw_sp,
12962306a36Sopenharmony_ci				struct mlxsw_sp_acl_ruleset *ruleset,
13062306a36Sopenharmony_ci				struct mlxsw_sp_flow_block *block)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct mlxsw_sp_flow_block_binding *binding;
13362306a36Sopenharmony_ci	int err;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	block->ruleset_zero = ruleset;
13662306a36Sopenharmony_ci	list_for_each_entry(binding, &block->binding_list, list) {
13762306a36Sopenharmony_ci		err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
13862306a36Sopenharmony_ci		if (err)
13962306a36Sopenharmony_ci			goto rollback;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cirollback:
14462306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(binding, &block->binding_list,
14562306a36Sopenharmony_ci					     list)
14662306a36Sopenharmony_ci		mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
14762306a36Sopenharmony_ci	block->ruleset_zero = NULL;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return err;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void
15362306a36Sopenharmony_cimlxsw_sp_acl_ruleset_block_unbind(struct mlxsw_sp *mlxsw_sp,
15462306a36Sopenharmony_ci				  struct mlxsw_sp_acl_ruleset *ruleset,
15562306a36Sopenharmony_ci				  struct mlxsw_sp_flow_block *block)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct mlxsw_sp_flow_block_binding *binding;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	list_for_each_entry(binding, &block->binding_list, list)
16062306a36Sopenharmony_ci		mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
16162306a36Sopenharmony_ci	block->ruleset_zero = NULL;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic struct mlxsw_sp_acl_ruleset *
16562306a36Sopenharmony_cimlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
16662306a36Sopenharmony_ci			    struct mlxsw_sp_flow_block *block, u32 chain_index,
16762306a36Sopenharmony_ci			    const struct mlxsw_sp_acl_profile_ops *ops,
16862306a36Sopenharmony_ci			    struct mlxsw_afk_element_usage *tmplt_elusage)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
17162306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset;
17262306a36Sopenharmony_ci	size_t alloc_size;
17362306a36Sopenharmony_ci	int err;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	alloc_size = sizeof(*ruleset) + ops->ruleset_priv_size;
17662306a36Sopenharmony_ci	ruleset = kzalloc(alloc_size, GFP_KERNEL);
17762306a36Sopenharmony_ci	if (!ruleset)
17862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
17962306a36Sopenharmony_ci	ruleset->ref_count = 1;
18062306a36Sopenharmony_ci	ruleset->ht_key.block = block;
18162306a36Sopenharmony_ci	ruleset->ht_key.chain_index = chain_index;
18262306a36Sopenharmony_ci	ruleset->ht_key.ops = ops;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	err = rhashtable_init(&ruleset->rule_ht, &mlxsw_sp_acl_rule_ht_params);
18562306a36Sopenharmony_ci	if (err)
18662306a36Sopenharmony_ci		goto err_rhashtable_init;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	err = ops->ruleset_add(mlxsw_sp, &acl->tcam, ruleset->priv,
18962306a36Sopenharmony_ci			       tmplt_elusage, &ruleset->min_prio,
19062306a36Sopenharmony_ci			       &ruleset->max_prio);
19162306a36Sopenharmony_ci	if (err)
19262306a36Sopenharmony_ci		goto err_ops_ruleset_add;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node,
19562306a36Sopenharmony_ci				     mlxsw_sp_acl_ruleset_ht_params);
19662306a36Sopenharmony_ci	if (err)
19762306a36Sopenharmony_ci		goto err_ht_insert;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return ruleset;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cierr_ht_insert:
20262306a36Sopenharmony_ci	ops->ruleset_del(mlxsw_sp, ruleset->priv);
20362306a36Sopenharmony_cierr_ops_ruleset_add:
20462306a36Sopenharmony_ci	rhashtable_destroy(&ruleset->rule_ht);
20562306a36Sopenharmony_cierr_rhashtable_init:
20662306a36Sopenharmony_ci	kfree(ruleset);
20762306a36Sopenharmony_ci	return ERR_PTR(err);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp,
21162306a36Sopenharmony_ci					 struct mlxsw_sp_acl_ruleset *ruleset)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
21462306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node,
21762306a36Sopenharmony_ci			       mlxsw_sp_acl_ruleset_ht_params);
21862306a36Sopenharmony_ci	ops->ruleset_del(mlxsw_sp, ruleset->priv);
21962306a36Sopenharmony_ci	rhashtable_destroy(&ruleset->rule_ht);
22062306a36Sopenharmony_ci	kfree(ruleset);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void mlxsw_sp_acl_ruleset_ref_inc(struct mlxsw_sp_acl_ruleset *ruleset)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	ruleset->ref_count++;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp,
22962306a36Sopenharmony_ci					 struct mlxsw_sp_acl_ruleset *ruleset)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	if (--ruleset->ref_count)
23262306a36Sopenharmony_ci		return;
23362306a36Sopenharmony_ci	mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic struct mlxsw_sp_acl_ruleset *
23762306a36Sopenharmony_ci__mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl,
23862306a36Sopenharmony_ci			      struct mlxsw_sp_flow_block *block, u32 chain_index,
23962306a36Sopenharmony_ci			      const struct mlxsw_sp_acl_profile_ops *ops)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset_ht_key ht_key;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	memset(&ht_key, 0, sizeof(ht_key));
24462306a36Sopenharmony_ci	ht_key.block = block;
24562306a36Sopenharmony_ci	ht_key.chain_index = chain_index;
24662306a36Sopenharmony_ci	ht_key.ops = ops;
24762306a36Sopenharmony_ci	return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key,
24862306a36Sopenharmony_ci				      mlxsw_sp_acl_ruleset_ht_params);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistruct mlxsw_sp_acl_ruleset *
25262306a36Sopenharmony_cimlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp,
25362306a36Sopenharmony_ci			    struct mlxsw_sp_flow_block *block, u32 chain_index,
25462306a36Sopenharmony_ci			    enum mlxsw_sp_acl_profile profile)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops;
25762306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
25862306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	ops = mlxsw_sp_acl_tcam_profile_ops(mlxsw_sp, profile);
26162306a36Sopenharmony_ci	if (!ops)
26262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
26362306a36Sopenharmony_ci	ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, block, chain_index, ops);
26462306a36Sopenharmony_ci	if (!ruleset)
26562306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
26662306a36Sopenharmony_ci	return ruleset;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistruct mlxsw_sp_acl_ruleset *
27062306a36Sopenharmony_cimlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp,
27162306a36Sopenharmony_ci			 struct mlxsw_sp_flow_block *block, u32 chain_index,
27262306a36Sopenharmony_ci			 enum mlxsw_sp_acl_profile profile,
27362306a36Sopenharmony_ci			 struct mlxsw_afk_element_usage *tmplt_elusage)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops;
27662306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
27762306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ops = mlxsw_sp_acl_tcam_profile_ops(mlxsw_sp, profile);
28062306a36Sopenharmony_ci	if (!ops)
28162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, block, chain_index, ops);
28462306a36Sopenharmony_ci	if (ruleset) {
28562306a36Sopenharmony_ci		mlxsw_sp_acl_ruleset_ref_inc(ruleset);
28662306a36Sopenharmony_ci		return ruleset;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci	return mlxsw_sp_acl_ruleset_create(mlxsw_sp, block, chain_index, ops,
28962306a36Sopenharmony_ci					   tmplt_elusage);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_civoid mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
29362306a36Sopenharmony_ci			      struct mlxsw_sp_acl_ruleset *ruleset)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ciu16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return ops->ruleset_group_id(ruleset->priv);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_civoid mlxsw_sp_acl_ruleset_prio_get(struct mlxsw_sp_acl_ruleset *ruleset,
30662306a36Sopenharmony_ci				   unsigned int *p_min_prio,
30762306a36Sopenharmony_ci				   unsigned int *p_max_prio)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	*p_min_prio = ruleset->min_prio;
31062306a36Sopenharmony_ci	*p_max_prio = ruleset->max_prio;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistruct mlxsw_sp_acl_rule_info *
31462306a36Sopenharmony_cimlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl,
31562306a36Sopenharmony_ci			  struct mlxsw_afa_block *afa_block)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct mlxsw_sp_acl_rule_info *rulei;
31862306a36Sopenharmony_ci	int err;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
32162306a36Sopenharmony_ci	if (!rulei)
32262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (afa_block) {
32562306a36Sopenharmony_ci		rulei->act_block = afa_block;
32662306a36Sopenharmony_ci		return rulei;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	rulei->act_block = mlxsw_afa_block_create(acl->mlxsw_sp->afa);
33062306a36Sopenharmony_ci	if (IS_ERR(rulei->act_block)) {
33162306a36Sopenharmony_ci		err = PTR_ERR(rulei->act_block);
33262306a36Sopenharmony_ci		goto err_afa_block_create;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci	rulei->action_created = 1;
33562306a36Sopenharmony_ci	return rulei;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cierr_afa_block_create:
33862306a36Sopenharmony_ci	kfree(rulei);
33962306a36Sopenharmony_ci	return ERR_PTR(err);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_civoid mlxsw_sp_acl_rulei_destroy(struct mlxsw_sp *mlxsw_sp,
34362306a36Sopenharmony_ci				struct mlxsw_sp_acl_rule_info *rulei)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	if (rulei->action_created)
34662306a36Sopenharmony_ci		mlxsw_afa_block_destroy(rulei->act_block);
34762306a36Sopenharmony_ci	if (rulei->src_port_range_reg_valid)
34862306a36Sopenharmony_ci		mlxsw_sp_port_range_reg_put(mlxsw_sp,
34962306a36Sopenharmony_ci					    rulei->src_port_range_reg_index);
35062306a36Sopenharmony_ci	if (rulei->dst_port_range_reg_valid)
35162306a36Sopenharmony_ci		mlxsw_sp_port_range_reg_put(mlxsw_sp,
35262306a36Sopenharmony_ci					    rulei->dst_port_range_reg_index);
35362306a36Sopenharmony_ci	kfree(rulei);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	return mlxsw_afa_block_commit(rulei->act_block);
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei,
36262306a36Sopenharmony_ci				 unsigned int priority)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	rulei->priority = priority;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_civoid mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei,
36862306a36Sopenharmony_ci				    enum mlxsw_afk_element element,
36962306a36Sopenharmony_ci				    u32 key_value, u32 mask_value)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	mlxsw_afk_values_add_u32(&rulei->values, element,
37262306a36Sopenharmony_ci				 key_value, mask_value);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_civoid mlxsw_sp_acl_rulei_keymask_buf(struct mlxsw_sp_acl_rule_info *rulei,
37662306a36Sopenharmony_ci				    enum mlxsw_afk_element element,
37762306a36Sopenharmony_ci				    const char *key_value,
37862306a36Sopenharmony_ci				    const char *mask_value, unsigned int len)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	mlxsw_afk_values_add_buf(&rulei->values, element,
38162306a36Sopenharmony_ci				 key_value, mask_value, len);
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_continue(struct mlxsw_sp_acl_rule_info *rulei)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	return mlxsw_afa_block_continue(rulei->act_block);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_jump(struct mlxsw_sp_acl_rule_info *rulei,
39062306a36Sopenharmony_ci				u16 group_id)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	return mlxsw_afa_block_jump(rulei->act_block, group_id);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_terminate(struct mlxsw_sp_acl_rule_info *rulei)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	return mlxsw_afa_block_terminate(rulei->act_block);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei,
40162306a36Sopenharmony_ci				bool ingress,
40262306a36Sopenharmony_ci				const struct flow_action_cookie *fa_cookie,
40362306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	return mlxsw_afa_block_append_drop(rulei->act_block, ingress,
40662306a36Sopenharmony_ci					   fa_cookie, extack);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return mlxsw_afa_block_append_trap(rulei->act_block,
41262306a36Sopenharmony_ci					   MLXSW_TRAP_ID_ACL0);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
41662306a36Sopenharmony_ci			       struct mlxsw_sp_acl_rule_info *rulei,
41762306a36Sopenharmony_ci			       struct net_device *out_dev,
41862306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
42162306a36Sopenharmony_ci	u16 local_port;
42262306a36Sopenharmony_ci	bool in_port;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (out_dev) {
42562306a36Sopenharmony_ci		if (!mlxsw_sp_port_dev_check(out_dev)) {
42662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid output device");
42762306a36Sopenharmony_ci			return -EINVAL;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci		mlxsw_sp_port = netdev_priv(out_dev);
43062306a36Sopenharmony_ci		if (mlxsw_sp_port->mlxsw_sp != mlxsw_sp) {
43162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid output device");
43262306a36Sopenharmony_ci			return -EINVAL;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci		local_port = mlxsw_sp_port->local_port;
43562306a36Sopenharmony_ci		in_port = false;
43662306a36Sopenharmony_ci	} else {
43762306a36Sopenharmony_ci		/* If out_dev is NULL, the caller wants to
43862306a36Sopenharmony_ci		 * set forward to ingress port.
43962306a36Sopenharmony_ci		 */
44062306a36Sopenharmony_ci		local_port = 0;
44162306a36Sopenharmony_ci		in_port = true;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	return mlxsw_afa_block_append_fwd(rulei->act_block,
44462306a36Sopenharmony_ci					  local_port, in_port, extack);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
44862306a36Sopenharmony_ci				  struct mlxsw_sp_acl_rule_info *rulei,
44962306a36Sopenharmony_ci				  struct mlxsw_sp_flow_block *block,
45062306a36Sopenharmony_ci				  struct net_device *out_dev,
45162306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct mlxsw_sp_flow_block_binding *binding;
45462306a36Sopenharmony_ci	struct mlxsw_sp_port *in_port;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (!list_is_singular(&block->binding_list)) {
45762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Only a single mirror source is allowed");
45862306a36Sopenharmony_ci		return -EOPNOTSUPP;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	binding = list_first_entry(&block->binding_list,
46162306a36Sopenharmony_ci				   struct mlxsw_sp_flow_block_binding, list);
46262306a36Sopenharmony_ci	in_port = binding->mlxsw_sp_port;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return mlxsw_afa_block_append_mirror(rulei->act_block,
46562306a36Sopenharmony_ci					     in_port->local_port,
46662306a36Sopenharmony_ci					     out_dev,
46762306a36Sopenharmony_ci					     binding->ingress,
46862306a36Sopenharmony_ci					     extack);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp,
47262306a36Sopenharmony_ci				struct mlxsw_sp_acl_rule_info *rulei,
47362306a36Sopenharmony_ci				u32 action, u16 vid, u16 proto, u8 prio,
47462306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	u8 ethertype;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (action == FLOW_ACTION_VLAN_MANGLE) {
47962306a36Sopenharmony_ci		switch (proto) {
48062306a36Sopenharmony_ci		case ETH_P_8021Q:
48162306a36Sopenharmony_ci			ethertype = 0;
48262306a36Sopenharmony_ci			break;
48362306a36Sopenharmony_ci		case ETH_P_8021AD:
48462306a36Sopenharmony_ci			ethertype = 1;
48562306a36Sopenharmony_ci			break;
48662306a36Sopenharmony_ci		default:
48762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported VLAN protocol");
48862306a36Sopenharmony_ci			dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN protocol %#04x\n",
48962306a36Sopenharmony_ci				proto);
49062306a36Sopenharmony_ci			return -EINVAL;
49162306a36Sopenharmony_ci		}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		return mlxsw_afa_block_append_vlan_modify(rulei->act_block,
49462306a36Sopenharmony_ci							  vid, prio, ethertype,
49562306a36Sopenharmony_ci							  extack);
49662306a36Sopenharmony_ci	} else {
49762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unsupported VLAN action");
49862306a36Sopenharmony_ci		dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN action\n");
49962306a36Sopenharmony_ci		return -EINVAL;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_priority(struct mlxsw_sp *mlxsw_sp,
50462306a36Sopenharmony_ci				    struct mlxsw_sp_acl_rule_info *rulei,
50562306a36Sopenharmony_ci				    u32 prio, struct netlink_ext_ack *extack)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	/* Even though both Linux and Spectrum switches support 16 priorities,
50862306a36Sopenharmony_ci	 * spectrum_qdisc only processes the first eight priomap elements, and
50962306a36Sopenharmony_ci	 * the DCB and PFC features are tied to 8 priorities as well. Therefore
51062306a36Sopenharmony_ci	 * bounce attempts to prioritize packets to higher priorities.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	if (prio >= IEEE_8021QAZ_MAX_TCS) {
51362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Only priorities 0..7 are supported");
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	return mlxsw_afa_block_append_qos_switch_prio(rulei->act_block, prio,
51762306a36Sopenharmony_ci						      extack);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistruct mlxsw_sp_acl_mangle_action {
52162306a36Sopenharmony_ci	enum flow_action_mangle_base htype;
52262306a36Sopenharmony_ci	/* Offset is u32-aligned. */
52362306a36Sopenharmony_ci	u32 offset;
52462306a36Sopenharmony_ci	/* Mask bits are unset for the modified field. */
52562306a36Sopenharmony_ci	u32 mask;
52662306a36Sopenharmony_ci	/* Shift required to extract the set value. */
52762306a36Sopenharmony_ci	u32 shift;
52862306a36Sopenharmony_ci	enum mlxsw_sp_acl_mangle_field field;
52962306a36Sopenharmony_ci};
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci#define MLXSW_SP_ACL_MANGLE_ACTION(_htype, _offset, _mask, _shift, _field) \
53262306a36Sopenharmony_ci	{								\
53362306a36Sopenharmony_ci		.htype = _htype,					\
53462306a36Sopenharmony_ci		.offset = _offset,					\
53562306a36Sopenharmony_ci		.mask = _mask,						\
53662306a36Sopenharmony_ci		.shift = _shift,					\
53762306a36Sopenharmony_ci		.field = MLXSW_SP_ACL_MANGLE_FIELD_##_field,		\
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci#define MLXSW_SP_ACL_MANGLE_ACTION_IP4(_offset, _mask, _shift, _field) \
54162306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_IP4,       \
54262306a36Sopenharmony_ci				   _offset, _mask, _shift, _field)
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci#define MLXSW_SP_ACL_MANGLE_ACTION_IP6(_offset, _mask, _shift, _field) \
54562306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_IP6,       \
54662306a36Sopenharmony_ci				   _offset, _mask, _shift, _field)
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci#define MLXSW_SP_ACL_MANGLE_ACTION_TCP(_offset, _mask, _shift, _field) \
54962306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_TCP, _offset, _mask, _shift, _field)
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci#define MLXSW_SP_ACL_MANGLE_ACTION_UDP(_offset, _mask, _shift, _field) \
55262306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION(FLOW_ACT_MANGLE_HDR_TYPE_UDP, _offset, _mask, _shift, _field)
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic struct mlxsw_sp_acl_mangle_action mlxsw_sp_acl_mangle_actions[] = {
55562306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff00ffff, 16, IP_DSFIELD),
55662306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xff03ffff, 18, IP_DSCP),
55762306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP4(0, 0xfffcffff, 16, IP_ECN),
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf00fffff, 20, IP_DSFIELD),
56062306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xf03fffff, 22, IP_DSCP),
56162306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(0, 0xffcfffff, 20, IP_ECN),
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0x0000ffff, 16, IP_SPORT),
56462306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_TCP(0, 0xffff0000, 0,  IP_DPORT),
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0x0000ffff, 16, IP_SPORT),
56762306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0xffff0000, 0,  IP_DPORT),
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP4(12, 0x00000000, 0, IP4_SIP),
57062306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP4(16, 0x00000000, 0, IP4_DIP),
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(8, 0x00000000, 0, IP6_SIP_1),
57362306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(12, 0x00000000, 0, IP6_SIP_2),
57462306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(16, 0x00000000, 0, IP6_SIP_3),
57562306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(20, 0x00000000, 0, IP6_SIP_4),
57662306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(24, 0x00000000, 0, IP6_DIP_1),
57762306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(28, 0x00000000, 0, IP6_DIP_2),
57862306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(32, 0x00000000, 0, IP6_DIP_3),
57962306a36Sopenharmony_ci	MLXSW_SP_ACL_MANGLE_ACTION_IP6(36, 0x00000000, 0, IP6_DIP_4),
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int
58362306a36Sopenharmony_cimlxsw_sp_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
58462306a36Sopenharmony_ci				    struct mlxsw_sp_acl_rule_info *rulei,
58562306a36Sopenharmony_ci				    struct mlxsw_sp_acl_mangle_action *mact,
58662306a36Sopenharmony_ci				    u32 val, struct netlink_ext_ack *extack)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	switch (mact->field) {
58962306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD:
59062306a36Sopenharmony_ci		return mlxsw_afa_block_append_qos_dsfield(rulei->act_block,
59162306a36Sopenharmony_ci							  val, extack);
59262306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP:
59362306a36Sopenharmony_ci		return mlxsw_afa_block_append_qos_dscp(rulei->act_block,
59462306a36Sopenharmony_ci						       val, extack);
59562306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN:
59662306a36Sopenharmony_ci		return mlxsw_afa_block_append_qos_ecn(rulei->act_block,
59762306a36Sopenharmony_ci						      val, extack);
59862306a36Sopenharmony_ci	default:
59962306a36Sopenharmony_ci		return -EOPNOTSUPP;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic int mlxsw_sp1_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
60462306a36Sopenharmony_ci						struct mlxsw_sp_acl_rule_info *rulei,
60562306a36Sopenharmony_ci						struct mlxsw_sp_acl_mangle_action *mact,
60662306a36Sopenharmony_ci						u32 val, struct netlink_ext_ack *extack)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	int err;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack);
61162306a36Sopenharmony_ci	if (err != -EOPNOTSUPP)
61262306a36Sopenharmony_ci		return err;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field");
61562306a36Sopenharmony_ci	return err;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int
61962306a36Sopenharmony_cimlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(struct mlxsw_sp_acl_rule_info *rulei,
62062306a36Sopenharmony_ci					    enum mlxsw_sp_acl_mangle_field field,
62162306a36Sopenharmony_ci					    u32 val, struct netlink_ext_ack *extack)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	if (!rulei->ipv6_valid) {
62462306a36Sopenharmony_ci		rulei->ipv6.prev_val = val;
62562306a36Sopenharmony_ci		rulei->ipv6_valid = true;
62662306a36Sopenharmony_ci		rulei->ipv6.prev_field = field;
62762306a36Sopenharmony_ci		return 0;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field order");
63162306a36Sopenharmony_ci	return -EOPNOTSUPP;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
63562306a36Sopenharmony_ci						struct mlxsw_sp_acl_rule_info *rulei,
63662306a36Sopenharmony_ci						struct mlxsw_sp_acl_mangle_action *mact,
63762306a36Sopenharmony_ci						u32 val, struct netlink_ext_ack *extack)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	int err;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	err = mlxsw_sp_acl_rulei_act_mangle_field(mlxsw_sp, rulei, mact, val, extack);
64262306a36Sopenharmony_ci	if (err != -EOPNOTSUPP)
64362306a36Sopenharmony_ci		return err;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	switch (mact->field) {
64662306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT:
64762306a36Sopenharmony_ci		return mlxsw_afa_block_append_l4port(rulei->act_block, false, val, extack);
64862306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT:
64962306a36Sopenharmony_ci		return mlxsw_afa_block_append_l4port(rulei->act_block, true, val, extack);
65062306a36Sopenharmony_ci	/* IPv4 fields */
65162306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP4_SIP:
65262306a36Sopenharmony_ci		return mlxsw_afa_block_append_ip(rulei->act_block, false,
65362306a36Sopenharmony_ci						 true, val, 0, extack);
65462306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP4_DIP:
65562306a36Sopenharmony_ci		return mlxsw_afa_block_append_ip(rulei->act_block, true,
65662306a36Sopenharmony_ci						 true, val, 0, extack);
65762306a36Sopenharmony_ci	/* IPv6 fields */
65862306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1:
65962306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3:
66062306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1:
66162306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3:
66262306a36Sopenharmony_ci		return mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(rulei,
66362306a36Sopenharmony_ci								   mact->field,
66462306a36Sopenharmony_ci								   val, extack);
66562306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_2:
66662306a36Sopenharmony_ci		if (rulei->ipv6_valid &&
66762306a36Sopenharmony_ci		    rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1) {
66862306a36Sopenharmony_ci			rulei->ipv6_valid = false;
66962306a36Sopenharmony_ci			return mlxsw_afa_block_append_ip(rulei->act_block,
67062306a36Sopenharmony_ci							 false, false, val,
67162306a36Sopenharmony_ci							 rulei->ipv6.prev_val,
67262306a36Sopenharmony_ci							 extack);
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_4:
67662306a36Sopenharmony_ci		if (rulei->ipv6_valid &&
67762306a36Sopenharmony_ci		    rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3) {
67862306a36Sopenharmony_ci			rulei->ipv6_valid = false;
67962306a36Sopenharmony_ci			return mlxsw_afa_block_append_ip(rulei->act_block,
68062306a36Sopenharmony_ci							 false, true, val,
68162306a36Sopenharmony_ci							 rulei->ipv6.prev_val,
68262306a36Sopenharmony_ci							 extack);
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_2:
68662306a36Sopenharmony_ci		if (rulei->ipv6_valid &&
68762306a36Sopenharmony_ci		    rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1) {
68862306a36Sopenharmony_ci			rulei->ipv6_valid = false;
68962306a36Sopenharmony_ci			return mlxsw_afa_block_append_ip(rulei->act_block,
69062306a36Sopenharmony_ci							 true, false, val,
69162306a36Sopenharmony_ci							 rulei->ipv6.prev_val,
69262306a36Sopenharmony_ci							 extack);
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci		break;
69562306a36Sopenharmony_ci	case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_4:
69662306a36Sopenharmony_ci		if (rulei->ipv6_valid &&
69762306a36Sopenharmony_ci		    rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3) {
69862306a36Sopenharmony_ci			rulei->ipv6_valid = false;
69962306a36Sopenharmony_ci			return mlxsw_afa_block_append_ip(rulei->act_block,
70062306a36Sopenharmony_ci							 true, true, val,
70162306a36Sopenharmony_ci							 rulei->ipv6.prev_val,
70262306a36Sopenharmony_ci							 extack);
70362306a36Sopenharmony_ci		}
70462306a36Sopenharmony_ci		break;
70562306a36Sopenharmony_ci	default:
70662306a36Sopenharmony_ci		break;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field");
71062306a36Sopenharmony_ci	return err;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_mangle(struct mlxsw_sp *mlxsw_sp,
71462306a36Sopenharmony_ci				  struct mlxsw_sp_acl_rule_info *rulei,
71562306a36Sopenharmony_ci				  enum flow_action_mangle_base htype,
71662306a36Sopenharmony_ci				  u32 offset, u32 mask, u32 val,
71762306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	const struct mlxsw_sp_acl_rulei_ops *acl_rulei_ops = mlxsw_sp->acl_rulei_ops;
72062306a36Sopenharmony_ci	struct mlxsw_sp_acl_mangle_action *mact;
72162306a36Sopenharmony_ci	size_t i;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mlxsw_sp_acl_mangle_actions); ++i) {
72462306a36Sopenharmony_ci		mact = &mlxsw_sp_acl_mangle_actions[i];
72562306a36Sopenharmony_ci		if (mact->htype == htype &&
72662306a36Sopenharmony_ci		    mact->offset == offset &&
72762306a36Sopenharmony_ci		    mact->mask == mask) {
72862306a36Sopenharmony_ci			val >>= mact->shift;
72962306a36Sopenharmony_ci			return acl_rulei_ops->act_mangle_field(mlxsw_sp,
73062306a36Sopenharmony_ci							       rulei, mact,
73162306a36Sopenharmony_ci							       val, extack);
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "Unknown mangle field");
73662306a36Sopenharmony_ci	return -EINVAL;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_police(struct mlxsw_sp *mlxsw_sp,
74062306a36Sopenharmony_ci				  struct mlxsw_sp_acl_rule_info *rulei,
74162306a36Sopenharmony_ci				  u32 index, u64 rate_bytes_ps,
74262306a36Sopenharmony_ci				  u32 burst, struct netlink_ext_ack *extack)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	int err;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	err = mlxsw_afa_block_append_police(rulei->act_block, index,
74762306a36Sopenharmony_ci					    rate_bytes_ps, burst,
74862306a36Sopenharmony_ci					    &rulei->policer_index, extack);
74962306a36Sopenharmony_ci	if (err)
75062306a36Sopenharmony_ci		return err;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	rulei->policer_index_valid = true;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp,
75862306a36Sopenharmony_ci				 struct mlxsw_sp_acl_rule_info *rulei,
75962306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	int err;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	err = mlxsw_afa_block_append_counter(rulei->act_block,
76462306a36Sopenharmony_ci					     &rulei->counter_index, extack);
76562306a36Sopenharmony_ci	if (err)
76662306a36Sopenharmony_ci		return err;
76762306a36Sopenharmony_ci	rulei->counter_valid = true;
76862306a36Sopenharmony_ci	return 0;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
77262306a36Sopenharmony_ci				   struct mlxsw_sp_acl_rule_info *rulei,
77362306a36Sopenharmony_ci				   u16 fid, struct netlink_ext_ack *extack)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	return mlxsw_afa_block_append_fid_set(rulei->act_block, fid, extack);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_ignore(struct mlxsw_sp *mlxsw_sp,
77962306a36Sopenharmony_ci				  struct mlxsw_sp_acl_rule_info *rulei,
78062306a36Sopenharmony_ci				  bool disable_learning, bool disable_security)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	return mlxsw_afa_block_append_ignore(rulei->act_block,
78362306a36Sopenharmony_ci					     disable_learning,
78462306a36Sopenharmony_ci					     disable_security);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ciint mlxsw_sp_acl_rulei_act_sample(struct mlxsw_sp *mlxsw_sp,
78862306a36Sopenharmony_ci				  struct mlxsw_sp_acl_rule_info *rulei,
78962306a36Sopenharmony_ci				  struct mlxsw_sp_flow_block *block,
79062306a36Sopenharmony_ci				  struct psample_group *psample_group, u32 rate,
79162306a36Sopenharmony_ci				  u32 trunc_size, bool truncate,
79262306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct mlxsw_sp_flow_block_binding *binding;
79562306a36Sopenharmony_ci	struct mlxsw_sp_port *mlxsw_sp_port;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (!list_is_singular(&block->binding_list)) {
79862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Only a single sampling source is allowed");
79962306a36Sopenharmony_ci		return -EOPNOTSUPP;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci	binding = list_first_entry(&block->binding_list,
80262306a36Sopenharmony_ci				   struct mlxsw_sp_flow_block_binding, list);
80362306a36Sopenharmony_ci	mlxsw_sp_port = binding->mlxsw_sp_port;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return mlxsw_afa_block_append_sampler(rulei->act_block,
80662306a36Sopenharmony_ci					      mlxsw_sp_port->local_port,
80762306a36Sopenharmony_ci					      psample_group, rate, trunc_size,
80862306a36Sopenharmony_ci					      truncate, binding->ingress,
80962306a36Sopenharmony_ci					      extack);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistruct mlxsw_sp_acl_rule *
81362306a36Sopenharmony_cimlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
81462306a36Sopenharmony_ci			 struct mlxsw_sp_acl_ruleset *ruleset,
81562306a36Sopenharmony_ci			 unsigned long cookie,
81662306a36Sopenharmony_ci			 struct mlxsw_afa_block *afa_block,
81762306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
82062306a36Sopenharmony_ci	struct mlxsw_sp_acl_rule *rule;
82162306a36Sopenharmony_ci	int err;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	mlxsw_sp_acl_ruleset_ref_inc(ruleset);
82462306a36Sopenharmony_ci	rule = kzalloc(sizeof(*rule) + ops->rule_priv_size,
82562306a36Sopenharmony_ci		       GFP_KERNEL);
82662306a36Sopenharmony_ci	if (!rule) {
82762306a36Sopenharmony_ci		err = -ENOMEM;
82862306a36Sopenharmony_ci		goto err_alloc;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci	rule->cookie = cookie;
83162306a36Sopenharmony_ci	rule->ruleset = ruleset;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	rule->rulei = mlxsw_sp_acl_rulei_create(mlxsw_sp->acl, afa_block);
83462306a36Sopenharmony_ci	if (IS_ERR(rule->rulei)) {
83562306a36Sopenharmony_ci		err = PTR_ERR(rule->rulei);
83662306a36Sopenharmony_ci		goto err_rulei_create;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return rule;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cierr_rulei_create:
84262306a36Sopenharmony_ci	kfree(rule);
84362306a36Sopenharmony_cierr_alloc:
84462306a36Sopenharmony_ci	mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
84562306a36Sopenharmony_ci	return ERR_PTR(err);
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_civoid mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp,
84962306a36Sopenharmony_ci			       struct mlxsw_sp_acl_rule *rule)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	mlxsw_sp_acl_rulei_destroy(mlxsw_sp, rule->rulei);
85462306a36Sopenharmony_ci	kfree(rule);
85562306a36Sopenharmony_ci	mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ciint mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
85962306a36Sopenharmony_ci			  struct mlxsw_sp_acl_rule *rule)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
86262306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
86362306a36Sopenharmony_ci	struct mlxsw_sp_flow_block *block = ruleset->ht_key.block;
86462306a36Sopenharmony_ci	int err;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei);
86762306a36Sopenharmony_ci	if (err)
86862306a36Sopenharmony_ci		return err;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	err = rhashtable_insert_fast(&ruleset->rule_ht, &rule->ht_node,
87162306a36Sopenharmony_ci				     mlxsw_sp_acl_rule_ht_params);
87262306a36Sopenharmony_ci	if (err)
87362306a36Sopenharmony_ci		goto err_rhashtable_insert;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!ruleset->ht_key.chain_index &&
87662306a36Sopenharmony_ci	    mlxsw_sp_acl_ruleset_is_singular(ruleset)) {
87762306a36Sopenharmony_ci		/* We only need ruleset with chain index 0, the implicit
87862306a36Sopenharmony_ci		 * one, to be directly bound to device. The rest of the
87962306a36Sopenharmony_ci		 * rulesets are bound by "Goto action set".
88062306a36Sopenharmony_ci		 */
88162306a36Sopenharmony_ci		err = mlxsw_sp_acl_ruleset_block_bind(mlxsw_sp, ruleset, block);
88262306a36Sopenharmony_ci		if (err)
88362306a36Sopenharmony_ci			goto err_ruleset_block_bind;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->acl->rules_lock);
88762306a36Sopenharmony_ci	list_add_tail(&rule->list, &mlxsw_sp->acl->rules);
88862306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->acl->rules_lock);
88962306a36Sopenharmony_ci	block->rule_count++;
89062306a36Sopenharmony_ci	block->ingress_blocker_rule_count += rule->rulei->ingress_bind_blocker;
89162306a36Sopenharmony_ci	block->egress_blocker_rule_count += rule->rulei->egress_bind_blocker;
89262306a36Sopenharmony_ci	return 0;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cierr_ruleset_block_bind:
89562306a36Sopenharmony_ci	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
89662306a36Sopenharmony_ci			       mlxsw_sp_acl_rule_ht_params);
89762306a36Sopenharmony_cierr_rhashtable_insert:
89862306a36Sopenharmony_ci	ops->rule_del(mlxsw_sp, rule->priv);
89962306a36Sopenharmony_ci	return err;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_civoid mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
90362306a36Sopenharmony_ci			   struct mlxsw_sp_acl_rule *rule)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
90662306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
90762306a36Sopenharmony_ci	struct mlxsw_sp_flow_block *block = ruleset->ht_key.block;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	block->egress_blocker_rule_count -= rule->rulei->egress_bind_blocker;
91062306a36Sopenharmony_ci	block->ingress_blocker_rule_count -= rule->rulei->ingress_bind_blocker;
91162306a36Sopenharmony_ci	block->rule_count--;
91262306a36Sopenharmony_ci	mutex_lock(&mlxsw_sp->acl->rules_lock);
91362306a36Sopenharmony_ci	list_del(&rule->list);
91462306a36Sopenharmony_ci	mutex_unlock(&mlxsw_sp->acl->rules_lock);
91562306a36Sopenharmony_ci	if (!ruleset->ht_key.chain_index &&
91662306a36Sopenharmony_ci	    mlxsw_sp_acl_ruleset_is_singular(ruleset))
91762306a36Sopenharmony_ci		mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset, block);
91862306a36Sopenharmony_ci	rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
91962306a36Sopenharmony_ci			       mlxsw_sp_acl_rule_ht_params);
92062306a36Sopenharmony_ci	ops->rule_del(mlxsw_sp, rule->priv);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ciint mlxsw_sp_acl_rule_action_replace(struct mlxsw_sp *mlxsw_sp,
92462306a36Sopenharmony_ci				     struct mlxsw_sp_acl_rule *rule,
92562306a36Sopenharmony_ci				     struct mlxsw_afa_block *afa_block)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
92862306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
92962306a36Sopenharmony_ci	struct mlxsw_sp_acl_rule_info *rulei;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	rulei = mlxsw_sp_acl_rule_rulei(rule);
93262306a36Sopenharmony_ci	rulei->act_block = afa_block;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	return ops->rule_action_replace(mlxsw_sp, rule->priv, rule->rulei);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistruct mlxsw_sp_acl_rule *
93862306a36Sopenharmony_cimlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp,
93962306a36Sopenharmony_ci			 struct mlxsw_sp_acl_ruleset *ruleset,
94062306a36Sopenharmony_ci			 unsigned long cookie)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
94362306a36Sopenharmony_ci				       mlxsw_sp_acl_rule_ht_params);
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistruct mlxsw_sp_acl_rule_info *
94762306a36Sopenharmony_cimlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	return rule->rulei;
95062306a36Sopenharmony_ci}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_cistatic int mlxsw_sp_acl_rule_activity_update(struct mlxsw_sp *mlxsw_sp,
95362306a36Sopenharmony_ci					     struct mlxsw_sp_acl_rule *rule)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
95662306a36Sopenharmony_ci	const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
95762306a36Sopenharmony_ci	bool active;
95862306a36Sopenharmony_ci	int err;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	err = ops->rule_activity_get(mlxsw_sp, rule->priv, &active);
96162306a36Sopenharmony_ci	if (err)
96262306a36Sopenharmony_ci		return err;
96362306a36Sopenharmony_ci	if (active)
96462306a36Sopenharmony_ci		rule->last_used = jiffies;
96562306a36Sopenharmony_ci	return 0;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int mlxsw_sp_acl_rules_activity_update(struct mlxsw_sp_acl *acl)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct mlxsw_sp_acl_rule *rule;
97162306a36Sopenharmony_ci	int err;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	mutex_lock(&acl->rules_lock);
97462306a36Sopenharmony_ci	list_for_each_entry(rule, &acl->rules, list) {
97562306a36Sopenharmony_ci		err = mlxsw_sp_acl_rule_activity_update(acl->mlxsw_sp,
97662306a36Sopenharmony_ci							rule);
97762306a36Sopenharmony_ci		if (err)
97862306a36Sopenharmony_ci			goto err_rule_update;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci	mutex_unlock(&acl->rules_lock);
98162306a36Sopenharmony_ci	return 0;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cierr_rule_update:
98462306a36Sopenharmony_ci	mutex_unlock(&acl->rules_lock);
98562306a36Sopenharmony_ci	return err;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic void mlxsw_sp_acl_rule_activity_work_schedule(struct mlxsw_sp_acl *acl)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	unsigned long interval = acl->rule_activity_update.interval;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	mlxsw_core_schedule_dw(&acl->rule_activity_update.dw,
99362306a36Sopenharmony_ci			       msecs_to_jiffies(interval));
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic void mlxsw_sp_acl_rule_activity_update_work(struct work_struct *work)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl = container_of(work, struct mlxsw_sp_acl,
99962306a36Sopenharmony_ci						rule_activity_update.dw.work);
100062306a36Sopenharmony_ci	int err;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	err = mlxsw_sp_acl_rules_activity_update(acl);
100362306a36Sopenharmony_ci	if (err)
100462306a36Sopenharmony_ci		dev_err(acl->mlxsw_sp->bus_info->dev, "Could not update acl activity");
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	mlxsw_sp_acl_rule_activity_work_schedule(acl);
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ciint mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp,
101062306a36Sopenharmony_ci				struct mlxsw_sp_acl_rule *rule,
101162306a36Sopenharmony_ci				u64 *packets, u64 *bytes, u64 *drops,
101262306a36Sopenharmony_ci				u64 *last_use,
101362306a36Sopenharmony_ci				enum flow_action_hw_stats *used_hw_stats)
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	enum mlxsw_sp_policer_type type = MLXSW_SP_POLICER_TYPE_SINGLE_RATE;
101762306a36Sopenharmony_ci	struct mlxsw_sp_acl_rule_info *rulei;
101862306a36Sopenharmony_ci	u64 current_packets = 0;
101962306a36Sopenharmony_ci	u64 current_bytes = 0;
102062306a36Sopenharmony_ci	u64 current_drops = 0;
102162306a36Sopenharmony_ci	int err;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	rulei = mlxsw_sp_acl_rule_rulei(rule);
102462306a36Sopenharmony_ci	if (rulei->counter_valid) {
102562306a36Sopenharmony_ci		err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index,
102662306a36Sopenharmony_ci						&current_packets,
102762306a36Sopenharmony_ci						&current_bytes);
102862306a36Sopenharmony_ci		if (err)
102962306a36Sopenharmony_ci			return err;
103062306a36Sopenharmony_ci		*used_hw_stats = FLOW_ACTION_HW_STATS_IMMEDIATE;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci	if (rulei->policer_index_valid) {
103362306a36Sopenharmony_ci		err = mlxsw_sp_policer_drops_counter_get(mlxsw_sp, type,
103462306a36Sopenharmony_ci							 rulei->policer_index,
103562306a36Sopenharmony_ci							 &current_drops);
103662306a36Sopenharmony_ci		if (err)
103762306a36Sopenharmony_ci			return err;
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci	*packets = current_packets - rule->last_packets;
104062306a36Sopenharmony_ci	*bytes = current_bytes - rule->last_bytes;
104162306a36Sopenharmony_ci	*drops = current_drops - rule->last_drops;
104262306a36Sopenharmony_ci	*last_use = rule->last_used;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	rule->last_bytes = current_bytes;
104562306a36Sopenharmony_ci	rule->last_packets = current_packets;
104662306a36Sopenharmony_ci	rule->last_drops = current_drops;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	return 0;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ciint mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct mlxsw_sp_fid *fid;
105462306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl;
105562306a36Sopenharmony_ci	size_t alloc_size;
105662306a36Sopenharmony_ci	int err;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	alloc_size = sizeof(*acl) + mlxsw_sp_acl_tcam_priv_size(mlxsw_sp);
105962306a36Sopenharmony_ci	acl = kzalloc(alloc_size, GFP_KERNEL);
106062306a36Sopenharmony_ci	if (!acl)
106162306a36Sopenharmony_ci		return -ENOMEM;
106262306a36Sopenharmony_ci	mlxsw_sp->acl = acl;
106362306a36Sopenharmony_ci	acl->mlxsw_sp = mlxsw_sp;
106462306a36Sopenharmony_ci	acl->afk = mlxsw_afk_create(MLXSW_CORE_RES_GET(mlxsw_sp->core,
106562306a36Sopenharmony_ci						       ACL_FLEX_KEYS),
106662306a36Sopenharmony_ci				    mlxsw_sp->afk_ops);
106762306a36Sopenharmony_ci	if (!acl->afk) {
106862306a36Sopenharmony_ci		err = -ENOMEM;
106962306a36Sopenharmony_ci		goto err_afk_create;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	err = rhashtable_init(&acl->ruleset_ht,
107362306a36Sopenharmony_ci			      &mlxsw_sp_acl_ruleset_ht_params);
107462306a36Sopenharmony_ci	if (err)
107562306a36Sopenharmony_ci		goto err_rhashtable_init;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	fid = mlxsw_sp_fid_dummy_get(mlxsw_sp);
107862306a36Sopenharmony_ci	if (IS_ERR(fid)) {
107962306a36Sopenharmony_ci		err = PTR_ERR(fid);
108062306a36Sopenharmony_ci		goto err_fid_get;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci	acl->dummy_fid = fid;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	INIT_LIST_HEAD(&acl->rules);
108562306a36Sopenharmony_ci	mutex_init(&acl->rules_lock);
108662306a36Sopenharmony_ci	err = mlxsw_sp_acl_tcam_init(mlxsw_sp, &acl->tcam);
108762306a36Sopenharmony_ci	if (err)
108862306a36Sopenharmony_ci		goto err_acl_ops_init;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	/* Create the delayed work for the rule activity_update */
109162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&acl->rule_activity_update.dw,
109262306a36Sopenharmony_ci			  mlxsw_sp_acl_rule_activity_update_work);
109362306a36Sopenharmony_ci	acl->rule_activity_update.interval = MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS;
109462306a36Sopenharmony_ci	mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0);
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cierr_acl_ops_init:
109862306a36Sopenharmony_ci	mutex_destroy(&acl->rules_lock);
109962306a36Sopenharmony_ci	mlxsw_sp_fid_put(fid);
110062306a36Sopenharmony_cierr_fid_get:
110162306a36Sopenharmony_ci	rhashtable_destroy(&acl->ruleset_ht);
110262306a36Sopenharmony_cierr_rhashtable_init:
110362306a36Sopenharmony_ci	mlxsw_afk_destroy(acl->afk);
110462306a36Sopenharmony_cierr_afk_create:
110562306a36Sopenharmony_ci	kfree(acl);
110662306a36Sopenharmony_ci	return err;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_civoid mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw);
111462306a36Sopenharmony_ci	mlxsw_sp_acl_tcam_fini(mlxsw_sp, &acl->tcam);
111562306a36Sopenharmony_ci	mutex_destroy(&acl->rules_lock);
111662306a36Sopenharmony_ci	WARN_ON(!list_empty(&acl->rules));
111762306a36Sopenharmony_ci	mlxsw_sp_fid_put(acl->dummy_fid);
111862306a36Sopenharmony_ci	rhashtable_destroy(&acl->ruleset_ht);
111962306a36Sopenharmony_ci	mlxsw_afk_destroy(acl->afk);
112062306a36Sopenharmony_ci	kfree(acl);
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistruct mlxsw_sp_acl_rulei_ops mlxsw_sp1_acl_rulei_ops = {
112462306a36Sopenharmony_ci	.act_mangle_field = mlxsw_sp1_acl_rulei_act_mangle_field,
112562306a36Sopenharmony_ci};
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cistruct mlxsw_sp_acl_rulei_ops mlxsw_sp2_acl_rulei_ops = {
112862306a36Sopenharmony_ci	.act_mangle_field = mlxsw_sp2_acl_rulei_act_mangle_field,
112962306a36Sopenharmony_ci};
1130