mlxsw: spectrum: acl: Implement delta for ERP
authorJiri Pirko <jiri@mellanox.com>
Wed, 14 Nov 2018 08:22:35 +0000 (08:22 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 15 Nov 2018 22:43:43 +0000 (14:43 -0800)
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>
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_atcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h

index db3d2790aeecf9c3b93fe4c66df7856d637dfc70..d3babcc49fd2fdd70a9ec946f78a56793e733242 100644 (file)
@@ -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);
index 12798ce33a60b7bb7aba4f54bfb3e5d06311ca53..e7bd8733e58e4bceb0ca0232856225df2ced6cd5 100644 (file)
@@ -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
index bfd0c8a6cabf75d285ba61c6ce12cb3c8da3583b..d9a4b7e8434b438e523e94a32b9642d803032276 100644 (file)
@@ -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)
index 854436c5ea93907145e9a74999f4f2337687cbc7..9a73759d901f105baf983540ef86ee82bd5dde56 100644 (file)
@@ -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);