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 ¤t_packets, 102762306a36Sopenharmony_ci ¤t_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 ¤t_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