mlxsw: spectrum: acl: Implement delta for ERP

Allow ERP sharing for multiple mask. Do it by properly implementing
delta_create() objagg object. Use the computed delta info for inserting
rules in A-TCAM.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jiri Pirko 2018-11-14 08:22:35 +00:00 коммит произвёл David S. Miller
Родитель c293ba3403
Коммит c22291f7cf
4 изменённых файлов: 237 добавлений и 12 удалений

Просмотреть файл

@ -2834,8 +2834,9 @@ static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
u32 priority,
const char *tcam_region_info,
const char *key, u8 erp_id,
bool large_exists, u32 lkey_id,
u32 action_pointer)
u16 delta_start, u8 delta_mask,
u8 delta_value, bool large_exists,
u32 lkey_id, u32 action_pointer)
{
MLXSW_REG_ZERO(ptce3, payload);
mlxsw_reg_ptce3_v_set(payload, valid);
@ -2844,6 +2845,9 @@ static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
mlxsw_reg_ptce3_tcam_region_info_memcpy_to(payload, tcam_region_info);
mlxsw_reg_ptce3_flex2_key_blocks_memcpy_to(payload, key);
mlxsw_reg_ptce3_erp_id_set(payload, erp_id);
mlxsw_reg_ptce3_delta_start_set(payload, delta_start);
mlxsw_reg_ptce3_delta_mask_set(payload, delta_mask);
mlxsw_reg_ptce3_delta_value_set(payload, delta_value);
mlxsw_reg_ptce3_large_exists_set(payload, large_exists);
mlxsw_reg_ptce3_large_entry_key_id_set(payload, lkey_id);
mlxsw_reg_ptce3_action_pointer_set(payload, action_pointer);

Просмотреть файл

@ -398,6 +398,9 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
mlxsw_reg_ptce3_pack(ptce3_pl, true, MLXSW_REG_PTCE3_OP_WRITE_WRITE,
priority, region->tcam_region_info,
aentry->ht_key.enc_key, erp_id,
aentry->delta_info.start,
aentry->delta_info.mask,
aentry->delta_info.value,
refcount_read(&lkey_id->refcnt) != 1, lkey_id->id,
kvdl_index);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
@ -419,11 +422,16 @@ mlxsw_sp_acl_atcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_lkey_id *lkey_id = aentry->lkey_id;
struct mlxsw_sp_acl_tcam_region *region = aregion->region;
u8 erp_id = mlxsw_sp_acl_erp_mask_erp_id(aentry->erp_mask);
char *enc_key = aentry->ht_key.enc_key;
char ptce3_pl[MLXSW_REG_PTCE3_LEN];
mlxsw_reg_ptce3_pack(ptce3_pl, false, MLXSW_REG_PTCE3_OP_WRITE_WRITE, 0,
region->tcam_region_info, aentry->ht_key.enc_key,
erp_id, refcount_read(&lkey_id->refcnt) != 1,
region->tcam_region_info,
enc_key, erp_id,
aentry->delta_info.start,
aentry->delta_info.mask,
aentry->delta_info.value,
refcount_read(&lkey_id->refcnt) != 1,
lkey_id->id, 0);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
aregion->ops->lkey_id_put(aregion, lkey_id);
@ -438,17 +446,30 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam_region *region = aregion->region;
char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 };
struct mlxsw_afk *afk = mlxsw_sp_acl_afk(mlxsw_sp->acl);
const struct mlxsw_sp_acl_erp_delta *delta;
struct mlxsw_sp_acl_erp_mask *erp_mask;
int err;
mlxsw_afk_encode(afk, region->key_info, &rulei->values,
aentry->ht_key.enc_key, mask);
aentry->full_enc_key, mask);
erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, false);
if (IS_ERR(erp_mask))
return PTR_ERR(erp_mask);
aentry->erp_mask = erp_mask;
aentry->ht_key.erp_id = mlxsw_sp_acl_erp_mask_erp_id(erp_mask);
memcpy(aentry->ht_key.enc_key, aentry->full_enc_key,
sizeof(aentry->ht_key.enc_key));
/* Compute all needed delta information and clear the delta bits
* from the encrypted key.
*/
delta = mlxsw_sp_acl_erp_delta(aentry->erp_mask);
aentry->delta_info.start = mlxsw_sp_acl_erp_delta_start(delta);
aentry->delta_info.mask = mlxsw_sp_acl_erp_delta_mask(delta);
aentry->delta_info.value =
mlxsw_sp_acl_erp_delta_value(delta, aentry->full_enc_key);
mlxsw_sp_acl_erp_delta_clear(delta, aentry->ht_key.enc_key);
/* We can't insert identical rules into the A-TCAM, so fail and
* let the rule spill into C-TCAM

Просмотреть файл

@ -29,6 +29,8 @@ struct mlxsw_sp_acl_erp_core {
struct mlxsw_sp_acl_erp_key {
char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN];
#define __MASK_LEN 0x38
#define __MASK_IDX(i) (__MASK_LEN - (i) - 1)
bool ctcam;
};
@ -58,6 +60,7 @@ struct mlxsw_sp_acl_erp_table {
unsigned int num_atcam_erps;
unsigned int num_max_atcam_erps;
unsigned int num_ctcam_erps;
unsigned int num_deltas;
struct objagg *objagg;
};
@ -629,7 +632,7 @@ __mlxsw_sp_acl_erp_table_other_inc(struct mlxsw_sp_acl_erp_table *erp_table,
{
int err;
/* If there are C-TCAM eRPs in use we need to transition
/* If there are C-TCAM eRP or deltas in use we need to transition
* the region to use eRP table, if it is not already done
*/
if (erp_table->ops != &erp_two_masks_ops &&
@ -639,7 +642,7 @@ __mlxsw_sp_acl_erp_table_other_inc(struct mlxsw_sp_acl_erp_table *erp_table,
return err;
}
/* When C-TCAM is used, the eRP table must be used */
/* When C-TCAM or deltas are used, the eRP table must be used */
if (erp_table->ops != &erp_multiple_masks_ops)
erp_table->ops = &erp_multiple_masks_ops;
@ -654,17 +657,23 @@ static int mlxsw_sp_acl_erp_ctcam_inc(struct mlxsw_sp_acl_erp_table *erp_table)
&erp_table->num_ctcam_erps);
}
static int mlxsw_sp_acl_erp_delta_inc(struct mlxsw_sp_acl_erp_table *erp_table)
{
return __mlxsw_sp_acl_erp_table_other_inc(erp_table,
&erp_table->num_deltas);
}
static void
__mlxsw_sp_acl_erp_table_other_dec(struct mlxsw_sp_acl_erp_table *erp_table,
unsigned int *dec_num)
{
(*dec_num)--;
/* If there are no C-TCAM eRPs in use, the state we
/* If there are no C-TCAM eRP or deltas in use, the state we
* transition to depends on the number of A-TCAM eRPs currently
* in use.
*/
if (erp_table->num_ctcam_erps > 0)
if (erp_table->num_ctcam_erps > 0 || erp_table->num_deltas > 0)
return;
switch (erp_table->num_atcam_erps) {
@ -706,6 +715,12 @@ static void mlxsw_sp_acl_erp_ctcam_dec(struct mlxsw_sp_acl_erp_table *erp_table)
&erp_table->num_ctcam_erps);
}
static void mlxsw_sp_acl_erp_delta_dec(struct mlxsw_sp_acl_erp_table *erp_table)
{
__mlxsw_sp_acl_erp_table_other_dec(erp_table,
&erp_table->num_deltas);
}
static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key)
@ -813,7 +828,8 @@ mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
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->num_ctcam_erps == 0)
if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0 &&
erp_table->num_deltas == 0)
erp_table->ops = &erp_two_masks_ops;
}
@ -961,14 +977,179 @@ u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask)
return erp->id;
}
struct mlxsw_sp_acl_erp_delta {
struct mlxsw_sp_acl_erp_key key;
u16 start;
u8 mask;
};
u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta)
{
return delta->start;
}
u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta)
{
return delta->mask;
}
u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
const char *enc_key)
{
u16 start = delta->start;
u8 mask = delta->mask;
u16 tmp;
if (!mask)
return 0;
tmp = (unsigned char) enc_key[__MASK_IDX(start / 8)];
if (start / 8 + 1 < __MASK_LEN)
tmp |= (unsigned char) enc_key[__MASK_IDX(start / 8 + 1)] << 8;
tmp >>= start % 8;
tmp &= mask;
return tmp;
}
void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
const char *enc_key)
{
u16 start = delta->start;
u8 mask = delta->mask;
unsigned char *byte;
u16 tmp;
tmp = mask;
tmp <<= start % 8;
tmp = ~tmp;
byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8)];
*byte &= tmp & 0xff;
if (start / 8 + 1 < __MASK_LEN) {
byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8 + 1)];
*byte &= (tmp >> 8) & 0xff;
}
}
static const struct mlxsw_sp_acl_erp_delta
mlxsw_sp_acl_erp_delta_default = {};
const struct mlxsw_sp_acl_erp_delta *
mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask)
{
struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask;
const struct mlxsw_sp_acl_erp_delta *delta;
delta = objagg_obj_delta_priv(objagg_obj);
if (!delta)
delta = &mlxsw_sp_acl_erp_delta_default;
return delta;
}
static int
mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key,
const struct mlxsw_sp_acl_erp_key *key,
u16 *delta_start, u8 *delta_mask)
{
int offset = 0;
int si = -1;
u16 pmask;
u16 mask;
int i;
/* The difference between 2 masks can be up to 8 consecutive bits. */
for (i = 0; i < __MASK_LEN; i++) {
if (parent_key->mask[__MASK_IDX(i)] == key->mask[__MASK_IDX(i)])
continue;
if (si == -1)
si = i;
else if (si != i - 1)
return -EINVAL;
}
if (si == -1) {
/* The masks are the same, this cannot happen.
* That means the caller is broken.
*/
WARN_ON(1);
*delta_start = 0;
*delta_mask = 0;
return 0;
}
pmask = (unsigned char) parent_key->mask[__MASK_IDX(si)];
mask = (unsigned char) key->mask[__MASK_IDX(si)];
if (si + 1 < __MASK_LEN) {
pmask |= (unsigned char) parent_key->mask[__MASK_IDX(si + 1)] << 8;
mask |= (unsigned char) key->mask[__MASK_IDX(si + 1)] << 8;
}
if ((pmask ^ mask) & pmask)
return -EINVAL;
mask &= ~pmask;
while (!(mask & (1 << offset)))
offset++;
while (!(mask & 1))
mask >>= 1;
if (mask & 0xff00)
return -EINVAL;
*delta_start = si * 8 + offset;
*delta_mask = mask;
return 0;
}
static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj,
void *obj)
{
return ERR_PTR(-EOPNOTSUPP);
struct mlxsw_sp_acl_erp_key *parent_key = parent_obj;
struct mlxsw_sp_acl_atcam_region *aregion = priv;
struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
struct mlxsw_sp_acl_erp_key *key = obj;
struct mlxsw_sp_acl_erp_delta *delta;
u16 delta_start;
u8 delta_mask;
int err;
if (parent_key->ctcam || key->ctcam)
return ERR_PTR(-EINVAL);
err = mlxsw_sp_acl_erp_delta_fill(parent_key, key,
&delta_start, &delta_mask);
if (err)
return ERR_PTR(-EINVAL);
delta = kzalloc(sizeof(*delta), GFP_KERNEL);
if (!delta)
return ERR_PTR(-ENOMEM);
delta->start = delta_start;
delta->mask = delta_mask;
err = mlxsw_sp_acl_erp_delta_inc(erp_table);
if (err)
goto err_erp_delta_inc;
memcpy(&delta->key, key, sizeof(*key));
err = mlxsw_sp_acl_erp_master_mask_set(erp_table, &delta->key);
if (err)
goto err_master_mask_set;
return delta;
err_master_mask_set:
mlxsw_sp_acl_erp_delta_dec(erp_table);
err_erp_delta_inc:
kfree(delta);
return ERR_PTR(err);
}
static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv)
{
struct mlxsw_sp_acl_erp_delta *delta = delta_priv;
struct mlxsw_sp_acl_atcam_region *aregion = priv;
struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
mlxsw_sp_acl_erp_master_mask_clear(erp_table, &delta->key);
mlxsw_sp_acl_erp_delta_dec(erp_table);
kfree(delta);
}
static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj)

Просмотреть файл

@ -154,7 +154,9 @@ struct mlxsw_sp_acl_atcam_region {
};
struct mlxsw_sp_acl_atcam_entry_ht_key {
char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key */
char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key,
* minus delta bits.
*/
u8 erp_id;
};
@ -165,6 +167,12 @@ struct mlxsw_sp_acl_atcam_chunk {
struct mlxsw_sp_acl_atcam_entry {
struct rhash_head ht_node;
struct mlxsw_sp_acl_atcam_entry_ht_key ht_key;
char full_enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key */
struct {
u16 start;
u8 mask;
u8 value;
} delta_info;
struct mlxsw_sp_acl_ctcam_entry centry;
struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
struct mlxsw_sp_acl_erp_mask *erp_mask;
@ -209,11 +217,22 @@ int mlxsw_sp_acl_atcam_init(struct mlxsw_sp *mlxsw_sp,
void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);
struct mlxsw_sp_acl_erp_delta;
u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta);
u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta);
u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
const char *enc_key);
void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
const char *enc_key);
struct mlxsw_sp_acl_erp_mask;
bool
mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask);
u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask);
const struct mlxsw_sp_acl_erp_delta *
mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask);
struct mlxsw_sp_acl_erp_mask *
mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion,
const char *mask, bool ctcam);