Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c22291f7 authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller
Browse files

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: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c293ba34
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -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);
+24 −3
Original line number Diff line number Diff line
@@ -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
+187 −6
Original line number Diff line number Diff line
@@ -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)
+20 −1
Original line number Diff line number Diff line
@@ -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);