mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
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:
parent
c293ba3403
commit
c22291f7cf
4 changed files with 237 additions and 12 deletions
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue