// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/rhashtable.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
#include <net/net_namespace.h>
#include <net/tc_act/tc_vlan.h>
#include "reg.h"
#include "core.h"
#include "resources.h"
#include "spectrum.h"
#include "core_acl_flex_keys.h"
#include "core_acl_flex_actions.h"
#include "spectrum_acl_tcam.h"
struct mlxsw_sp_acl {
struct mlxsw_sp *mlxsw_sp;
struct mlxsw_afk *afk;
struct mlxsw_sp_fid *dummy_fid;
struct rhashtable ruleset_ht;
struct list_head rules;
struct mutex rules_lock; /* Protects rules list */
struct {
struct delayed_work dw;
unsigned long interval; /* ms */
#define MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS 1000
} rule_activity_update;
struct mlxsw_sp_acl_tcam tcam;
};
struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
{
return acl->afk;
}
struct mlxsw_sp_acl_ruleset_ht_key {
struct mlxsw_sp_flow_block *block;
u32 chain_index;
const struct mlxsw_sp_acl_profile_ops *ops;
};
struct mlxsw_sp_acl_ruleset {
struct rhash_head ht_node; /* Member of acl HT */
struct mlxsw_sp_acl_ruleset_ht_key ht_key;
struct rhashtable rule_ht;
unsigned int ref_count;
unsigned int min_prio;
unsigned int max_prio;
unsigned long priv[];
/* priv has to be always the last item */
};
struct mlxsw_sp_acl_rule {
struct rhash_head ht_node; /* Member of rule HT */
struct list_head list;
unsigned long cookie; /* HT key */
struct mlxsw_sp_acl_ruleset *ruleset;
struct mlxsw_sp_acl_rule_info *rulei;
u64 last_used;
u64 last_packets;
u64 last_bytes;
unsigned long priv[];
/* priv has to be always the last item */
};
static const struct rhashtable_params mlxsw_sp_acl_ruleset_ht_params = {
.key_len = sizeof(struct mlxsw_sp_acl_ruleset_ht_key),
.key_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_key),
.head_offset = offsetof(struct mlxsw_sp_acl_ruleset, ht_node),
.automatic_shrinking = true,
};
static const struct rhashtable_params mlxsw_sp_acl_rule_ht_params = {
.key_len = sizeof(unsigned long),
.key_offset = offsetof(struct mlxsw_sp_acl_rule, cookie),
.head_offset = offsetof(struct mlxsw_sp_acl_rule, ht_node),
.automatic_shrinking = true,
};
struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp)
{
return mlxsw_sp->acl->dummy_fid;
}
static bool
mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset)
{
/* We hold a reference on ruleset ourselves */
return ruleset->ref_count == 2;
}
int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_flow_block *block,
struct mlxsw_sp_flow_block_binding *binding)
{
struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
return ops->ruleset_bind(mlxsw_sp, ruleset->priv,
binding->mlxsw_sp_port, binding->ingress);
}
void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_flow_block *block,
struct mlxsw_sp_flow_block_binding *binding)
{
struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
ops->ruleset_unbind(mlxsw_sp, ruleset->priv,
binding->mlxsw_sp_port, binding->ingress);
}
static int
mlxsw_sp_acl_ruleset_block_bind(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_ruleset *ruleset,
struct mlxsw_sp_flow_block *block)
{
struct mlxsw_sp_flow_block_binding *binding;
int err;
block->ruleset_zero = ruleset;
list_for_each_entry(binding, &block->binding_list, list) {
err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
if (err)
goto rollback;
}
return 0;
rollback:
list_for_each_entry_continue_reverse(binding, &block->binding_list,
list)
mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
block->ruleset_zero = NULL;
return err;
}
static void
mlxsw_sp_acl_ruleset_block_unbind(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_ruleset *ruleset,
struct mlxsw_sp_flow_block *block)
{
struct mlxsw_sp_flow_block_binding *binding;
list_for_each_entry(binding, &block->binding_list, list)
mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block,