Commit f465261a authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

mlxsw: spectrum_acl: Implement common eRP core

When rules are inserted into the A-TCAM they are associated with a mask,
which is part of the lookup key: { masked key, mask ID, region ID }.

These masks are called rule patterns (RP) and the aggregation of several
masks into one (to be introduced in follow-up patch sets) is called an
extended RP (eRP).

When a packet undergoes a lookup in an ACL region it is masked by the
current set of eRPs used by the region, looking for an exact match.
Eventually, the rule with the highest priority is picked.

These eRPs are stored in several global banks to allow for lookup to
occur using several eRPs simultaneously.

At first, an ACL region will only require a single mask - upon the
insertion of the first rule. In this case, the region can use the
"master RP" which is composed by OR-ing all the masks used by the
region. This mask is a property of the region and thus there is no need
to use the above mentioned banks.

At some point, a second mask will be needed. In this case, the region
will need to allocate an eRP table from the above mentioned banks and
insert its masks there.

>From now on, upon lookup, the eRP table used by the region will be
fetched from the eRP banks - using {eRP bank, Index within the bank} -
and the eRPs present in the table will be used to mask the packet. Note
that masks with consecutive indexes are inserted into consecutive banks.

When rules are deleted and a region only needs a single mask once again
it can free its eRP table and use the master RP.

The above logic is implemented in the eRP core and represented using the
following state machine:

    +------------+   create mask - as master RP   +---------------+
    |            +-------------------------------->               |
    |  no masks  |                                |  single mask  |
    |            <--------------------------------+               |
    +------------+          delete mask           +-----+--^------+
                                                        |  |
                                                        |  |
                                  create mask -         |  |  delete mask -
    create mask                   transition to use eRP |  |  transition to
     +--------+                   table                 |  |  use master RP
     |        |                                         |  |
     |        |                                         |  |
+----v--------+----+         create mask           +----v--+-----+
|                  <-------------------------------+             |
|  multiple masks  |                               |  two masks  |
|                  +------------------------------->             |
+------------------+      delete mask - if two     +-------------+
                          remaining

The code that actually configures rules in the A-TCAM will interface
with the eRP core by getting or putting an eRP based on the required
mask used by the rule.
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Reviewed-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 489142ec
......@@ -78,6 +78,7 @@ config MLXSW_SPECTRUM
depends on IPV6 || IPV6=n
depends on NET_IPGRE || NET_IPGRE=n
depends on IPV6_GRE || IPV6_GRE=n
select GENERIC_ALLOCATOR
select PARMAN
select MLXFW
default m
......
......@@ -18,7 +18,7 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \
spectrum1_kvdl.o spectrum2_kvdl.o \
spectrum_kvdl.o \
spectrum_acl_tcam.o spectrum_acl_ctcam.o \
spectrum_acl_atcam.o \
spectrum_acl_atcam.o spectrum_acl_erp.o \
spectrum1_acl_tcam.o spectrum2_acl_tcam.o \
spectrum_acl.o \
spectrum_flower.o spectrum_cnt.o \
......
/*
* drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
* Copyright (c) 2018 Mellanox Technologies. All rights reserved.
* Copyright (c) 2018 Ido Schimmel <idosch@mellanox.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/bitmap.h>
#include <linux/errno.h>
#include <linux/genalloc.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/rhashtable.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include "core.h"
#include "reg.h"
#include "spectrum.h"
#include "spectrum_acl_tcam.h"
/* gen_pool_alloc() returns 0 when allocation fails, so use an offset */
#define MLXSW_SP_ACL_ERP_GENALLOC_OFFSET 0x100
#define MLXSW_SP_ACL_ERP_MAX_PER_REGION 16
struct mlxsw_sp_acl_erp_core {
unsigned int erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX + 1];
struct gen_pool *erp_tables;
struct mlxsw_sp *mlxsw_sp;
unsigned int num_erp_banks;
};
struct mlxsw_sp_acl_erp_key {
char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN];
};
struct mlxsw_sp_acl_erp {
struct mlxsw_sp_acl_erp_key key;
u8 id;
u8 index;
refcount_t refcnt;
DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
struct list_head list;
struct rhash_head ht_node;
struct mlxsw_sp_acl_erp_table *erp_table;
};
struct mlxsw_sp_acl_erp_master_mask {
DECLARE_BITMAP(bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
unsigned int count[MLXSW_SP_ACL_TCAM_MASK_LEN];
};
struct mlxsw_sp_acl_erp_table {
struct mlxsw_sp_acl_erp_master_mask master_mask;
DECLARE_BITMAP(erp_id_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
DECLARE_BITMAP(erp_index_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
struct list_head atcam_erps_list;
struct rhashtable erp_ht;
struct mlxsw_sp_acl_erp_core *erp_core;
struct mlxsw_sp_acl_atcam_region *aregion;
const struct mlxsw_sp_acl_erp_table_ops *ops;
unsigned long base_index;
unsigned int num_atcam_erps;
unsigned int num_max_atcam_erps;
};
static const struct rhashtable_params mlxsw_sp_acl_erp_ht_params = {
.key_len = sizeof(struct mlxsw_sp_acl_erp_key),
.key_offset = offsetof(struct mlxsw_sp_acl_erp, key),
.head_offset = offsetof(struct mlxsw_sp_acl_erp, ht_node),
};
struct mlxsw_sp_acl_erp_table_ops {
struct mlxsw_sp_acl_erp *
(*erp_create)(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key);
void (*erp_destroy)(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp);
};
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key);
static void
mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp);
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key);
static void
mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp);
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key);
static void
mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp);
static void
mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp);
static const struct mlxsw_sp_acl_erp_table_ops erp_multiple_masks_ops = {
.erp_create = mlxsw_sp_acl_erp_mask_create,
.erp_destroy = mlxsw_sp_acl_erp_mask_destroy,
};
static const struct mlxsw_sp_acl_erp_table_ops erp_two_masks_ops = {
.erp_create = mlxsw_sp_acl_erp_mask_create,
.erp_destroy = mlxsw_sp_acl_erp_second_mask_destroy,
};
static const struct mlxsw_sp_acl_erp_table_ops erp_single_mask_ops = {
.erp_create = mlxsw_sp_acl_erp_second_mask_create,
.erp_destroy = mlxsw_sp_acl_erp_first_mask_destroy,
};
static const struct mlxsw_sp_acl_erp_table_ops erp_no_mask_ops = {
.erp_create = mlxsw_sp_acl_erp_first_mask_create,
.erp_destroy = mlxsw_sp_acl_erp_no_mask_destroy,
};
u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp)
{
return erp->id;
}
static unsigned int
mlxsw_sp_acl_erp_table_entry_size(const struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion;
struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
return erp_core->erpt_entries_size[aregion->type];
}
static int mlxsw_sp_acl_erp_id_get(struct mlxsw_sp_acl_erp_table *erp_table,
u8 *p_id)
{
u8 id;
id = find_first_zero_bit(erp_table->erp_id_bitmap,
MLXSW_SP_ACL_ERP_MAX_PER_REGION);
if (id < MLXSW_SP_ACL_ERP_MAX_PER_REGION) {
__set_bit(id, erp_table->erp_id_bitmap);
*p_id = id;
return 0;
}
return -ENOBUFS;
}
static void mlxsw_sp_acl_erp_id_put(struct mlxsw_sp_acl_erp_table *erp_table,
u8 id)
{
__clear_bit(id, erp_table->erp_id_bitmap);
}
static void
mlxsw_sp_acl_erp_master_mask_bit_set(unsigned long bit,
struct mlxsw_sp_acl_erp_master_mask *mask)
{
if (mask->count[bit]++ == 0)
__set_bit(bit, mask->bitmap);
}
static void
mlxsw_sp_acl_erp_master_mask_bit_clear(unsigned long bit,
struct mlxsw_sp_acl_erp_master_mask *mask)
{
if (--mask->count[bit] == 0)
__clear_bit(bit, mask->bitmap);
}
static int
mlxsw_sp_acl_erp_master_mask_update(struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
struct mlxsw_sp *mlxsw_sp = region->mlxsw_sp;
char percr_pl[MLXSW_REG_PERCR_LEN];
char *master_mask;
mlxsw_reg_percr_pack(percr_pl, region->id);
master_mask = mlxsw_reg_percr_master_mask_data(percr_pl);
bitmap_to_arr32((u32 *) master_mask, erp_table->master_mask.bitmap,
MLXSW_SP_ACL_TCAM_MASK_LEN);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(percr), percr_pl);
}
static int
mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table,
const struct mlxsw_sp_acl_erp *erp)
{
unsigned long bit;
int err;
for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
mlxsw_sp_acl_erp_master_mask_bit_set(bit,
&erp_table->master_mask);
err = mlxsw_sp_acl_erp_master_mask_update(erp_table);
if (err)
goto err_master_mask_update;
return 0;
err_master_mask_update:
for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
&erp_table->master_mask);
return err;
}
static int
mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table,
const struct mlxsw_sp_acl_erp *erp)
{
unsigned long bit;
int err;
for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
&erp_table->master_mask);
err = mlxsw_sp_acl_erp_master_mask_update(erp_table);
if (err)
goto err_master_mask_update;
return 0;
err_master_mask_update:
for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
mlxsw_sp_acl_erp_master_mask_bit_set(bit,
&erp_table->master_mask);
return err;
}
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key)
{
struct mlxsw_sp_acl_erp *erp;
int err;
erp = kzalloc(sizeof(*erp), GFP_KERNEL);
if (!erp)
return ERR_PTR(-ENOMEM);
err = mlxsw_sp_acl_erp_id_get(erp_table, &erp->id);
if (err)
goto err_erp_id_get;
memcpy(&erp->key, key, sizeof(*key));
bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
MLXSW_SP_ACL_TCAM_MASK_LEN);
list_add(&erp->list, &erp_table->atcam_erps_list);
refcount_set(&erp->refcnt, 1);
erp_table->num_atcam_erps++;
erp->erp_table = erp_table;
err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
if (err)
goto err_master_mask_set;
err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
mlxsw_sp_acl_erp_ht_params);
if (err)
goto err_rhashtable_insert;
return erp;
err_rhashtable_insert:
mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
err_master_mask_set:
erp_table->num_atcam_erps--;
list_del(&erp->list);
mlxsw_sp_acl_erp_id_put(erp_table, erp->id);
err_erp_id_get:
kfree(erp);
return ERR_PTR(err);
}
static void
mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp *erp)
{
struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
mlxsw_sp_acl_erp_ht_params);
mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
erp_table->num_atcam_erps--;
list_del(&erp->list);
mlxsw_sp_acl_erp_id_put(erp_table, erp->id);
kfree(erp);
}
static int
mlxsw_sp_acl_erp_table_alloc(struct mlxsw_sp_acl_erp_core *erp_core,
unsigned int num_erps,
enum mlxsw_sp_acl_atcam_region_type region_type,
unsigned long *p_index)
{
unsigned int num_rows, entry_size;
/* We only allow allocations of entire rows */
if (num_erps % erp_core->num_erp_banks != 0)
return -EINVAL;
entry_size = erp_core->erpt_entries_size[region_type];
num_rows = num_erps / erp_core->num_erp_banks;
*p_index = gen_pool_alloc(erp_core->erp_tables, num_rows * entry_size);
if (*p_index == 0)
return -ENOBUFS;
*p_index -= MLXSW_SP_ACL_ERP_GENALLOC_OFFSET;
return 0;
}
static void
mlxsw_sp_acl_erp_table_free(struct mlxsw_sp_acl_erp_core *erp_core,
unsigned int num_erps,
enum mlxsw_sp_acl_atcam_region_type region_type,
unsigned long index)
{
unsigned long base_index;
unsigned int entry_size;
size_t size;
entry_size = erp_core->erpt_entries_size[region_type];
base_index = index + MLXSW_SP_ACL_ERP_GENALLOC_OFFSET;
size = num_erps / erp_core->num_erp_banks * entry_size;
gen_pool_free(erp_core->erp_tables, base_index, size);
}
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_table_master_rp(struct mlxsw_sp_acl_erp_table *erp_table)
{
if (!list_is_singular(&erp_table->atcam_erps_list))
return NULL;
return list_first_entry(&erp_table->atcam_erps_list,
struct mlxsw_sp_acl_erp, list);
}
static int mlxsw_sp_acl_erp_index_get(struct mlxsw_sp_acl_erp_table *erp_table,
u8 *p_index)
{
u8 index;
index = find_first_zero_bit(erp_table->erp_index_bitmap,
erp_table->num_max_atcam_erps);
if (index < erp_table->num_max_atcam_erps) {
__set_bit(index, erp_table->erp_index_bitmap);
*p_index = index;
return 0;
}
return -ENOBUFS;
}
static void mlxsw_sp_acl_erp_index_put(struct mlxsw_sp_acl_erp_table *erp_table,
u8 index)
{
__clear_bit(index, erp_table->erp_index_bitmap);
}
static void
mlxsw_sp_acl_erp_table_locate(const struct mlxsw_sp_acl_erp_table *erp_table,
const struct mlxsw_sp_acl_erp *erp,
u8 *p_erpt_bank, u8 *p_erpt_index)
{
unsigned int entry_size = mlxsw_sp_acl_erp_table_entry_size(erp_table);
struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
unsigned int row;
*p_erpt_bank = erp->index % erp_core->num_erp_banks;
row = erp->index / erp_core->num_erp_banks;
*p_erpt_index = erp_table->base_index + row * entry_size;
}
static int
mlxsw_sp_acl_erp_table_erp_add(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp)
{
struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
enum mlxsw_reg_perpt_key_size key_size;
char perpt_pl[MLXSW_REG_PERPT_LEN];
u8 erpt_bank, erpt_index;
mlxsw_sp_acl_erp_table_locate(erp_table, erp, &erpt_bank, &erpt_index);
key_size = (enum mlxsw_reg_perpt_key_size) erp_table->aregion->type;
mlxsw_reg_perpt_pack(perpt_pl, erpt_bank, erpt_index, key_size, erp->id,
0, erp_table->base_index, erp->index,
erp->key.mask);
mlxsw_reg_perpt_erp_vector_pack(perpt_pl, erp_table->erp_index_bitmap,
MLXSW_SP_ACL_ERP_MAX_PER_REGION);
mlxsw_reg_perpt_erp_vector_set(perpt_pl, erp->index, true);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(perpt), perpt_pl);
}
static void mlxsw_sp_acl_erp_table_erp_del(struct mlxsw_sp_acl_erp *erp)
{
char empty_mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 };
struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
enum mlxsw_reg_perpt_key_size key_size;
char perpt_pl[MLXSW_REG_PERPT_LEN];
u8 erpt_bank, erpt_index;
mlxsw_sp_acl_erp_table_locate(erp_table, erp, &erpt_bank, &erpt_index);
key_size = (enum mlxsw_reg_perpt_key_size) erp_table->aregion->type;
mlxsw_reg_perpt_pack(perpt_pl, erpt_bank, erpt_index, key_size, erp->id,
0, erp_table->base_index, erp->index, empty_mask);
mlxsw_reg_perpt_erp_vector_pack(perpt_pl, erp_table->erp_index_bitmap,
MLXSW_SP_ACL_ERP_MAX_PER_REGION);
mlxsw_reg_perpt_erp_vector_set(perpt_pl, erp->index, false);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(perpt), perpt_pl);
}
static int
mlxsw_sp_acl_erp_table_enable(struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
char pererp_pl[MLXSW_REG_PERERP_LEN];
mlxsw_reg_pererp_pack(pererp_pl, region->id, false, true, 0,
erp_table->base_index, 0);
mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
MLXSW_SP_ACL_ERP_MAX_PER_REGION);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
}
static void
mlxsw_sp_acl_erp_table_disable(struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
char pererp_pl[MLXSW_REG_PERERP_LEN];
struct mlxsw_sp_acl_erp *master_rp;
master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
if (!master_rp)
return;
mlxsw_reg_pererp_pack(pererp_pl, region->id, false, false, 0, 0,
master_rp->id);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
}
static int
mlxsw_sp_acl_erp_table_relocate(struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_erp *erp;
int err;
list_for_each_entry(erp, &erp_table->atcam_erps_list, list) {
err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp);
if (err)
goto err_table_erp_add;
}
return 0;
err_table_erp_add:
list_for_each_entry_continue_reverse(erp, &erp_table->atcam_erps_list,
list)
mlxsw_sp_acl_erp_table_erp_del(erp);
return err;
}
static int
mlxsw_sp_acl_erp_table_expand(struct mlxsw_sp_acl_erp_table *erp_table)
{
unsigned int num_erps, old_num_erps = erp_table->num_max_atcam_erps;
struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
unsigned long old_base_index = erp_table->base_index;
int err;
if (erp_table->num_atcam_erps < erp_table->num_max_atcam_erps)
return 0;
if (erp_table->num_max_atcam_erps == MLXSW_SP_ACL_ERP_MAX_PER_REGION)
return -ENOBUFS;
num_erps = old_num_erps + erp_core->num_erp_banks;
err = mlxsw_sp_acl_erp_table_alloc(erp_core, num_erps,
erp_table->aregion->type,
&erp_table->base_index);
if (err)
return err;
erp_table->num_max_atcam_erps = num_erps;
err = mlxsw_sp_acl_erp_table_relocate(erp_table);
if (err)
goto err_table_relocate;
err = mlxsw_sp_acl_erp_table_enable(erp_table);
if (err)
goto err_table_enable;
mlxsw_sp_acl_erp_table_free(erp_core, old_num_erps,
erp_table->aregion->type, old_base_index);
return 0;
err_table_enable:
err_table_relocate:
erp_table->num_max_atcam_erps = old_num_erps;
mlxsw_sp_acl_erp_table_free(erp_core, num_erps,
erp_table->aregion->type,
erp_table->base_index);
erp_table->base_index = old_base_index;
return err;
}
static int
mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
struct mlxsw_sp_acl_erp *master_rp;
int err;
/* Initially, allocate a single eRP row. Expand later as needed */
err = mlxsw_sp_acl_erp_table_alloc(erp_core, erp_core->num_erp_banks,
erp_table->aregion->type,
&erp_table->base_index);
if (err)
return err;
erp_table->num_max_atcam_erps = erp_core->num_erp_banks;
/* Transition the sole RP currently configured (the master RP)
* to the eRP table
*/
master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
if (!master_rp) {
err = -EINVAL;
goto err_table_master_rp;
}
/* Maintain the same eRP bank for the master RP, so that we
* wouldn't need to update the bloom filter
*/
master_rp->index = master_rp->index % erp_core->num_erp_banks;
__set_bit(master_rp->index, erp_table->erp_index_bitmap);
err = mlxsw_sp_acl_erp_table_erp_add(erp_table, master_rp);
if (err)
goto err_table_master_rp_add;
err = mlxsw_sp_acl_erp_table_enable(erp_table);
if (err)
goto err_table_enable;
return 0;
err_table_enable:
mlxsw_sp_acl_erp_table_erp_del(master_rp);
err_table_master_rp_add:
__clear_bit(master_rp->index, erp_table->erp_index_bitmap);
err_table_master_rp:
mlxsw_sp_acl_erp_table_free(erp_core, erp_table->num_max_atcam_erps,
erp_table->aregion->type,
erp_table->base_index);
return err;
}
static void
mlxsw_sp_acl_erp_region_master_mask_trans(struct mlxsw_sp_acl_erp_table *erp_table)
{
struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
struct mlxsw_sp_acl_erp *master_rp;
mlxsw_sp_acl_erp_table_disable(erp_table);
master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
if (!master_rp)
return;
mlxsw_sp_acl_erp_table_erp_del(master_rp);
__clear_bit(master_rp->index, erp_table->erp_index_bitmap);
mlxsw_sp_acl_erp_table_free(erp_core, erp_table->num_max_atcam_erps,
erp_table->aregion->type,
erp_table->base_index);
}
static int
mlxsw_sp_acl_erp_region_erp_add(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp)
{
struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
char pererp_pl[MLXSW_REG_PERERP_LEN];
mlxsw_reg_pererp_pack(pererp_pl, region->id, false, true, 0,
erp_table->base_index, 0);
mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
MLXSW_SP_ACL_ERP_MAX_PER_REGION);
mlxsw_reg_pererp_erpt_vector_set(pererp_pl, erp->index, true);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
}
static void mlxsw_sp_acl_erp_region_erp_del(struct mlxsw_sp_acl_erp *erp)
{
struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
char pererp_pl[MLXSW_REG_PERERP_LEN];
mlxsw_reg_pererp_pack(pererp_pl, region->id, false, true, 0,
erp_table->base_index, 0);
mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
MLXSW_SP_ACL_ERP_MAX_PER_REGION);
mlxsw_reg_pererp_erpt_vector_set(pererp_pl, erp->index, false);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
}
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key)
{
struct mlxsw_sp_acl_erp *erp;
int err;
/* Expand the eRP table for the new eRP, if needed */
err = mlxsw_sp_acl_erp_table_expand(erp_table);
if (err)
return ERR_PTR(err);
erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
if (IS_ERR(erp))
return erp;
err = mlxsw_sp_acl_erp_index_get(erp_table, &erp->index);
if (err)
goto err_erp_index_get;
err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp);
if (err)
goto err_table_erp_add;
err = mlxsw_sp_acl_erp_region_erp_add(erp_table, erp);
if (err)
goto err_region_erp_add;
erp_table->ops = &erp_multiple_masks_ops;
return erp;
err_region_erp_add:
mlxsw_sp_acl_erp_table_erp_del(erp);
err_table_erp_add:
mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
err_erp_index_get:
mlxsw_sp_acl_erp_generic_destroy(erp);
return ERR_PTR(err);
}
static void
mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp)
{
mlxsw_sp_acl_erp_region_erp_del(erp);
mlxsw_sp_acl_erp_table_erp_del(erp);
mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
mlxsw_sp_acl_erp_generic_destroy(erp);
if (erp_table->num_atcam_erps == 2)
erp_table->ops = &erp_two_masks_ops;
}
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key)
{
struct mlxsw_sp_acl_erp *erp;
int err;
/* Transition to use eRP table instead of master mask */
err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
if (err)
return ERR_PTR(err);
erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
if (IS_ERR(erp)) {
err = PTR_ERR(erp);
goto err_erp_create;
}
err = mlxsw_sp_acl_erp_index_get(erp_table, &erp->index);
if (err)
goto err_erp_index_get;
err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp);
if (err)
goto err_table_erp_add;
err = mlxsw_sp_acl_erp_region_erp_add(erp_table, erp);
if (err)
goto err_region_erp_add;
erp_table->ops = &erp_two_masks_ops;
return erp;
err_region_erp_add:
mlxsw_sp_acl_erp_table_erp_del(erp);
err_table_erp_add:
mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
err_erp_index_get:
mlxsw_sp_acl_erp_generic_destroy(erp);
err_erp_create:
mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
return ERR_PTR(err);
}
static void
mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp)
{
mlxsw_sp_acl_erp_region_erp_del(erp);
mlxsw_sp_acl_erp_table_erp_del(erp);
mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
mlxsw_sp_acl_erp_generic_destroy(erp);
/* Transition to use master mask instead of eRP table */
mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
erp_table->ops = &erp_single_mask_ops;
}
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key)
{
struct mlxsw_sp_acl_erp *erp;
erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
if (IS_ERR(erp))
return erp;
erp_table->ops = &erp_single_mask_ops;
return erp;
}
static void
mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp)
{
mlxsw_sp_acl_erp_generic_destroy(erp);
erp_table->ops = &erp_no_mask_ops;
}
static void
mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp *erp)
{
WARN_ON(1);
}
struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
const char *mask)
{
struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
struct mlxsw_sp_acl_erp_key key;
struct mlxsw_sp_acl_erp *erp;
/* eRPs are allocated from a shared resource, but currently all
* allocations are done under RTNL.
*/
ASSERT_RTNL();
memcpy(key.mask, mask, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN);
erp = rhashtable_lookup_fast(&erp_table->erp_ht, &key,
mlxsw_sp_acl_erp_ht_params);
if (erp) {
refcount_inc(&erp->refcnt);
return erp;
}
return erp_table->ops->erp_create(erp_table, &key);
}
void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_erp *erp)
{
struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
ASSERT_RTNL();
if (!refcount_dec_and_test(&erp->refcnt))
return;
erp_table->ops->erp_destroy(erp_table, erp);
}
static struct mlxsw_sp_acl_erp_table *
mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
{
struct mlxsw_sp_acl_erp_table *erp_table;
int err;
erp_table = kzalloc(sizeof(*erp_table), GFP_KERNEL);
if (!erp_table)
return ERR_PTR(-ENOMEM);
err = rhashtable_init(&erp_table->erp_ht, &mlxsw_sp_acl_erp_ht_params);
if (err)
goto err_rhashtable_init;
erp_table->erp_core = aregion->atcam->erp_core;
erp_table->ops = &erp_no_mask_ops;
INIT_LIST_HEAD(&erp_table->atcam_erps_list);
erp_table->aregion = aregion;
return erp_table;
err_rhashtable_init:
kfree(erp_table);
return ERR_PTR(err);
}
static void
mlxsw_sp_acl_erp_table_destroy(struct mlxsw_sp_acl_erp_table *erp_table)
{
WARN_ON(!list_empty(&erp_table->atcam_erps_list));
rhashtable_destroy(&erp_table->erp_ht);
kfree(erp_table);
}
static int
mlxsw_sp_acl_erp_master_mask_init(struct mlxsw_sp_acl_atcam_region *aregion)
{
struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp;
char percr_pl[MLXSW_REG_PERCR_LEN];
char *master_mask;
mlxsw_reg_percr_pack(percr_pl, aregion->region->id);
master_mask = mlxsw_reg_percr_master_mask_data(percr_pl);
memset(master_mask, 0, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(percr), percr_pl);
}
static int
mlxsw_sp_acl_erp_region_param_init(struct mlxsw_sp_acl_atcam_region *aregion)
{
struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp;
char pererp_pl[MLXSW_REG_PERERP_LEN];
mlxsw_reg_pererp_pack(pererp_pl, aregion->region->id, false, false, 0,
0, 0);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
}
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion)
{
struct mlxsw_sp_acl_erp_table *erp_table;
int err;
erp_table = mlxsw_sp_acl_erp_table_create(aregion);
if (IS_ERR(erp_table))
return PTR_ERR(erp_table);
aregion->erp_table = erp_table;
/* Initialize the region's master mask to all zeroes */
err = mlxsw_sp_acl_erp_master_mask_init(aregion);
if (err)
goto err_erp_master_mask_init;
/* Initialize the region to not use the eRP table */
err = mlxsw_sp_acl_erp_region_param_init(aregion);
if (err)
goto err_erp_region_param_init;
return 0;
err_erp_region_param_init:
err_erp_master_mask_init:
mlxsw_sp_acl_erp_table_destroy(erp_table);
return err;
}
void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion)
{
mlxsw_sp_acl_erp_table_destroy(aregion->erp_table);
}
static int
mlxsw_sp_acl_erp_tables_sizes_query(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_erp_core *erp_core)
{
unsigned int size;
if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_2KB) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_4KB) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_8KB) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_12KB))
return -EIO;
size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_2KB);
erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_2KB] = size;
size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_4KB);
erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_4KB] = size;
size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_8KB);
erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB] = size;
size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_12KB);
erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_12KB] = size;
return 0;
}
static int mlxsw_sp_acl_erp_tables_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_erp_core *erp_core)
{
unsigned int erpt_bank_size;
int err;
if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_ERPT_BANK_SIZE) ||
!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_ERPT_BANKS))
return -EIO;
erpt_bank_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
ACL_MAX_ERPT_BANK_SIZE);
erp_core->num_erp_banks = MLXSW_CORE_RES_GET(mlxsw_sp->core,
ACL_MAX_ERPT_BANKS);
erp_core->erp_tables = gen_pool_create(0, -1);
if (!erp_core->erp_tables)
return -ENOMEM;
gen_pool_set_algo(erp_core->erp_tables, gen_pool_best_fit, NULL);
err = gen_pool_add(erp_core->erp_tables,
MLXSW_SP_ACL_ERP_GENALLOC_OFFSET, erpt_bank_size,
-1);
if (err)
goto err_gen_pool_add;
/* Different regions require masks of different sizes */
err = mlxsw_sp_acl_erp_tables_sizes_query(mlxsw_sp, erp_core);
if (err)
goto err_erp_tables_sizes_query;
return 0;
err_erp_tables_sizes_query:
err_gen_pool_add:
gen_pool_destroy(erp_core->erp_tables);
return err;
}
static void mlxsw_sp_acl_erp_tables_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_erp_core *erp_core)
{
gen_pool_destroy(erp_core->erp_tables);
}
int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam)
{
struct mlxsw_sp_acl_erp_core *erp_core;
int err;
erp_core = kzalloc(sizeof(*erp_core), GFP_KERNEL);
if (!erp_core)
return -ENOMEM;
erp_core->mlxsw_sp = mlxsw_sp;
atcam->erp_core = erp_core;
err = mlxsw_sp_acl_erp_tables_init(mlxsw_sp, erp_core);
if (err)
goto err_erp_tables_init;
return 0;
err_erp_tables_init:
kfree(erp_core);
return err;
}
void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam)
{
mlxsw_sp_acl_erp_tables_fini(mlxsw_sp, atcam->erp_core);
kfree(atcam->erp_core);
}
......@@ -92,6 +92,9 @@ mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,
#define MLXSW_SP_ACL_TCAM_CATCHALL_PRIO (~0U)
#define MLXSW_SP_ACL_TCAM_MASK_LEN \
(MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN * BITS_PER_BYTE)
struct mlxsw_sp_acl_tcam_group;
struct mlxsw_sp_acl_tcam_region {
......@@ -144,9 +147,46 @@ mlxsw_sp_acl_ctcam_entry_offset(struct mlxsw_sp_acl_ctcam_entry *centry)
return centry->parman_item.index;
}
enum mlxsw_sp_acl_atcam_region_type {
MLXSW_SP_ACL_ATCAM_REGION_TYPE_2KB,
MLXSW_SP_ACL_ATCAM_REGION_TYPE_4KB,
MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB,
MLXSW_SP_ACL_ATCAM_REGION_TYPE_12KB,
__MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX,
};
#define MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX \
(__MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX - 1)
struct mlxsw_sp_acl_atcam {
struct mlxsw_sp_acl_erp_core *erp_core;
};
struct mlxsw_sp_acl_atcam_region {
struct mlxsw_sp_acl_tcam_region *region;
struct mlxsw_sp_acl_atcam *atcam;
enum mlxsw_sp_acl_atcam_region_type type;
struct mlxsw_sp_acl_erp_table *erp_table;
};
int mlxsw_sp_acl_atcam_region_associate(struct mlxsw_sp *mlxsw_sp,
u16 region_id);
int mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam_region *region);
struct mlxsw_sp_acl_erp;
u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp);
struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
const char *mask);
void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_erp *erp);
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);
void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment