@@ -80,6 +80,7 @@ config MLXSW_SPECTRUM
depends on IPV6_GRE || IPV6_GRE=n
select GENERIC_ALLOCATOR
select PARMAN
+ select PRUNE
select MLXFW
default m
---help---
@@ -2760,13 +2760,24 @@ MLXSW_ITEM32(reg, ptce3, large_entry_key_id, 0x98, 0, 24);
*/
MLXSW_ITEM32(reg, ptce3, action_pointer, 0xA0, 0, 24);
+static inline void
+mlxsw_reg_ptce3_prune_vector_pack(char *payload,
+ unsigned long *prune_vector,
+ unsigned long size)
+{
+ unsigned long bit;
+
+ for_each_set_bit(bit, prune_vector, size)
+ mlxsw_reg_ptce3_prune_vector_set(payload, bit, true);
+}
+
static inline void mlxsw_reg_ptce3_pack(char *payload, bool valid,
enum mlxsw_reg_ptce3_op op,
u32 priority,
const char *tcam_region_info,
const char *key, u8 erp_id,
- bool large_exists, u32 lkey_id,
- u32 action_pointer)
+ bool prune_ctcam, bool large_exists,
+ u32 lkey_id, u32 action_pointer)
{
MLXSW_REG_ZERO(ptce3, payload);
mlxsw_reg_ptce3_v_set(payload, valid);
@@ -2775,6 +2786,7 @@ 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_prune_ctcam_set(payload, prune_ctcam);
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);
@@ -30,7 +30,8 @@ struct mlxsw_sp1_acl_tcam_entry {
static int
mlxsw_sp1_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregion,
struct mlxsw_sp_acl_ctcam_entry *centry,
- const char *mask)
+ const char *key, const char *mask,
+ u32 priority)
{
return 0;
}
@@ -2,6 +2,7 @@
/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
#include <linux/kernel.h>
+#include <linux/prune.h>
#include "spectrum.h"
#include "spectrum_acl_tcam.h"
@@ -27,14 +28,139 @@ struct mlxsw_sp2_acl_tcam_entry {
struct mlxsw_afa_block *act_block;
};
+struct mlxsw_afa_block *
+mlxsw_sp2_acl_tcam_aentry_act_block(struct mlxsw_sp_acl_atcam_entry *aentry)
+{
+ struct mlxsw_sp2_acl_tcam_entry *entry;
+
+ entry = container_of(aentry, struct mlxsw_sp2_acl_tcam_entry, aentry);
+ return entry->act_block;
+}
+
+void
+mlxsw_sp2_acl_tcam_prune_vector_bit_set(struct mlxsw_sp_acl_erp *erp,
+ struct mlxsw_sp_acl_atcam_entry *aentry,
+ bool pruned)
+{
+ if (pruned)
+ __set_bit(mlxsw_sp_acl_erp_index(erp), aentry->prune_bitmap);
+ else
+ __clear_bit(mlxsw_sp_acl_erp_index(erp), aentry->prune_bitmap);
+}
+
+static void
+mlxsw_sp2_acl_tcam_prune_update(struct mlxsw_sp_acl_erp *erp,
+ struct mlxsw_sp_acl_atcam_entry *aentry)
+{
+ struct mlxsw_sp_acl_atcam_region *aregion;
+ struct mlxsw_sp *mlxsw_sp;
+ u32 priority;
+ int err;
+
+ aregion = mlxsw_sp_acl_erp_aregion(erp);
+ mlxsw_sp = aregion->region->mlxsw_sp;
+ priority = aentry->prune_item->priority;
+
+ err = mlxsw_sp_acl_tcam_priority_get(mlxsw_sp,
+ aentry->prune_item->priority,
+ &priority, true);
+ if (err) {
+ WARN(err, "Failed to retrieve rule priority while updating rule with priority %u\n",
+ aentry->prune_item->priority);
+ return;
+ }
+ err = mlxsw_sp_acl_atcam_region_entry_update(mlxsw_sp, aregion, aentry,
+ priority);
+ if (err) {
+ WARN(err, "Failed to updating prune vector while update rule with priority %u\n",
+ aentry->prune_item->priority);
+ return;
+ }
+}
+
+static void
+mlxsw_sp2_acl_tcam_prune_ctcam_update(struct mlxsw_sp_acl_erp *erp,
+ struct mlxsw_sp_acl_atcam_entry *aentry,
+ bool pruned)
+{
+ bool ctcam_pruned = aentry->num_ctcam_erps_scan > 0 ? true : false;
+
+ pruned ? aentry->num_ctcam_erps_scan-- : aentry->num_ctcam_erps_scan++;
+
+ if ((aentry->num_ctcam_erps_scan > 0 && !ctcam_pruned) ||
+ (ctcam_pruned & !aentry->num_ctcam_erps_scan))
+ mlxsw_sp2_acl_tcam_prune_update(erp, aentry);
+}
+
+static void
+mlxsw_sp2_acl_tcam_prune_vector_update(struct mlxsw_sp_acl_erp *erp,
+ struct mlxsw_sp_acl_atcam_entry *aentry,
+ bool pruned)
+{
+ mlxsw_sp2_acl_tcam_prune_vector_bit_set(erp, aentry, pruned);
+ mlxsw_sp2_acl_tcam_prune_update(erp, aentry);
+}
+
+static void
+mlxsw_sp2_acl_tcam_prune_notification_handler(struct mlxsw_sp_acl_erp *erp,
+ struct mlxsw_sp_acl_atcam_entry
+ *aentry,
+ bool pruned)
+{
+ enum mlxsw_sp_acl_atcam_region_type region_type;
+ bool ctcam_erp, ctcam_entry;
+
+ region_type = mlxsw_sp_acl_erp_region_type(aentry->erp);
+ ctcam_entry = mlxsw_sp_acl_erp_is_ctcam_erp(aentry->erp);
+ ctcam_erp = mlxsw_sp_acl_erp_is_ctcam_erp(erp);
+
+ /* Discard notification:
+ * For ctcam rule prune vector is irrelevant
+ * For 8KB region type we should always not prune
+ */
+ if (!ctcam_entry && region_type != MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB) {
+ if (ctcam_erp)
+ mlxsw_sp2_acl_tcam_prune_ctcam_update(erp, aentry,
+ pruned);
+ else
+ mlxsw_sp2_acl_tcam_prune_vector_update(erp, aentry,
+ pruned);
+ }
+}
+
+static void
+mlxsw_sp2_acl_atcam_entry_prune_notify(struct list_head *prune_item_notify_list,
+ struct prune_table *table, bool pruned)
+{
+ struct mlxsw_sp_acl_atcam_entry *aentry;
+ struct mlxsw_sp_acl_erp *erp;
+ struct prune_item *item;
+
+ list_for_each_entry(item, prune_item_notify_list, list) {
+ aentry = item->priv;
+ erp = prune_table_priv(table);
+ mlxsw_sp2_acl_tcam_prune_notification_handler(erp, aentry,
+ pruned);
+ }
+}
+
+static const struct prune_ops mlxsw_sp2_acl_atcam_prune_ops = {
+ .prune_item_notify = mlxsw_sp2_acl_atcam_entry_prune_notify
+};
+
static int
mlxsw_sp2_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregion,
struct mlxsw_sp_acl_ctcam_entry *centry,
- const char *mask)
+ const char *key,
+ const char *mask,
+ u32 priority)
{
struct mlxsw_sp_acl_atcam_region *aregion;
struct mlxsw_sp_acl_atcam_entry *aentry;
+ struct prune_item *prune_item;
struct mlxsw_sp_acl_erp *erp;
+ bool non_def_prune_vector;
+ int err;
aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
@@ -44,7 +170,25 @@ mlxsw_sp2_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregio
return PTR_ERR(erp);
aentry->erp = erp;
+ bitmap_from_arr32(aentry->key_bitmap, (void *) key,
+ MLXSW_SP_ACL_TCAM_MASK_LEN);
+
+ prune_item = prune_item_create(aregion->region->prune,
+ mlxsw_sp_acl_erp_prune_table(erp),
+ priority, aentry->key_bitmap, aentry,
+ &non_def_prune_vector);
+ if (IS_ERR(prune_item)) {
+ err = PTR_ERR(prune_item);
+ goto err_prune_item_create;
+ }
+ aentry->prune_item = prune_item;
+ aentry->num_ctcam_erps_scan = 0;
+
return 0;
+
+err_prune_item_create:
+ mlxsw_sp_acl_erp_put(aregion, erp);
+ return err;
}
static void
@@ -53,10 +197,15 @@ mlxsw_sp2_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region *cregio
{
struct mlxsw_sp_acl_atcam_region *aregion;
struct mlxsw_sp_acl_atcam_entry *aentry;
+ int err;
aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
+ err = prune_item_destroy(aentry->prune_item);
+ WARN(err, "Failed to destroy prune item while removing rule with priority %u\n",
+ aentry->prune_item->priority);
+
mlxsw_sp_acl_erp_put(aregion, aentry->erp);
}
@@ -148,7 +297,8 @@ mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
return mlxsw_sp_acl_atcam_region_init(mlxsw_sp, &tcam->atcam,
®ion->aregion, _region,
- &mlxsw_sp2_acl_ctcam_region_ops);
+ &mlxsw_sp2_acl_ctcam_region_ops,
+ &mlxsw_sp2_acl_atcam_prune_ops);
}
static void
@@ -8,6 +8,7 @@
#include <linux/string.h>
#include <linux/rhashtable.h>
#include <linux/netdevice.h>
+#include <linux/prune.h>
#include <net/net_namespace.h>
#include <net/tc_act/tc_vlan.h>
@@ -847,6 +848,10 @@ int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
if (err)
goto err_acl_ops_init;
+ err = prune_init();
+ if (err)
+ goto err_prune_init;
+
/* Create the delayed work for the rule activity_update */
INIT_DELAYED_WORK(&acl->rule_activity_update.dw,
mlxsw_sp_acl_rul_activity_update_work);
@@ -854,6 +859,8 @@ int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp)
mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0);
return 0;
+err_prune_init:
+ mlxsw_sp_acl_tcam_fini(mlxsw_sp, &acl->tcam);
err_acl_ops_init:
mlxsw_sp_fid_put(fid);
err_fid_get:
@@ -870,6 +877,7 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw);
+ prune_fini();
mlxsw_sp_acl_tcam_fini(mlxsw_sp, &acl->tcam);
WARN_ON(!list_empty(&acl->rules));
mlxsw_sp_fid_put(acl->dummy_fid);
@@ -7,6 +7,7 @@
#include <linux/gfp.h>
#include <linux/refcount.h>
#include <linux/rhashtable.h>
+#include <linux/prune.h>
#include "reg.h"
#include "core.h"
@@ -317,7 +318,9 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam,
struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_tcam_region *region,
- const struct mlxsw_sp_acl_ctcam_region_ops *ops)
+ const struct mlxsw_sp_acl_ctcam_region_ops *ops,
+ const struct prune_ops
+ *mlxsw_acl_atcam_prune_ops)
{
int err;
@@ -339,9 +342,17 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
region, ops);
if (err)
goto err_ctcam_region_init;
+ region->prune = prune_create(MLXSW_SP_ACL_TCAM_MASK_LEN,
+ mlxsw_acl_atcam_prune_ops, region);
+ if (IS_ERR(region->prune)) {
+ err = PTR_ERR(region->prune);
+ goto err_prune_create;
+ }
return 0;
+err_prune_create:
+ mlxsw_sp_acl_ctcam_region_fini(&aregion->cregion);
err_ctcam_region_init:
mlxsw_sp_acl_erp_region_fini(aregion);
err_erp_region_init:
@@ -353,6 +364,7 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
void mlxsw_sp_acl_atcam_region_fini(struct mlxsw_sp_acl_atcam_region *aregion)
{
+ prune_destroy(aregion->region->prune);
mlxsw_sp_acl_ctcam_region_fini(&aregion->cregion);
mlxsw_sp_acl_erp_region_fini(aregion);
aregion->ops->fini(aregion);
@@ -376,19 +388,16 @@ static int
mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_atcam_entry *aentry,
- struct mlxsw_sp_acl_rule_info *rulei)
+ struct mlxsw_sp_acl_rule_info *rulei,
+ u32 priority)
{
struct mlxsw_sp_acl_tcam_region *region = aregion->region;
u8 erp_id = mlxsw_sp_acl_erp_id(aentry->erp);
struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
char ptce3_pl[MLXSW_REG_PTCE3_LEN];
- u32 kvdl_index, priority;
+ u32 kvdl_index;
int err;
- err = mlxsw_sp_acl_tcam_priority_get(mlxsw_sp, rulei, &priority, true);
- if (err)
- return err;
-
lkey_id = aregion->ops->lkey_id_get(aregion, rulei, erp_id);
if (IS_ERR(lkey_id))
return PTR_ERR(lkey_id);
@@ -398,8 +407,11 @@ 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->num_ctcam_erps_scan > 0),
refcount_read(&lkey_id->refcnt) != 1, lkey_id->id,
kvdl_index);
+ mlxsw_reg_ptce3_prune_vector_pack(ptce3_pl, aentry->prune_bitmap,
+ MLXSW_SP_ACL_ERP_MAX_PER_REGION);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
if (err)
goto err_ptce3_write;
@@ -411,6 +423,35 @@ mlxsw_sp_acl_atcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
return err;
}
+int
+mlxsw_sp_acl_atcam_region_entry_update(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_acl_atcam_region *aregion,
+ struct mlxsw_sp_acl_atcam_entry *aentry,
+ u32 priority)
+{
+ struct mlxsw_sp_acl_tcam_region *region = aregion->region;
+ struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
+ char ptce3_pl[MLXSW_REG_PTCE3_LEN];
+ struct mlxsw_afa_block *act_block;
+ u32 kvdl_index;
+ u8 erp_id;
+
+ erp_id = mlxsw_sp_acl_erp_id(aentry->erp);
+ lkey_id = aentry->lkey_id;
+ act_block = mlxsw_sp2_acl_tcam_aentry_act_block(aentry);
+ kvdl_index = mlxsw_afa_block_first_kvdl_index(act_block);
+
+ mlxsw_reg_ptce3_pack(ptce3_pl, true, MLXSW_REG_PTCE3_OP_WRITE_UPDATE,
+ priority, region->tcam_region_info,
+ aentry->ht_key.enc_key, erp_id,
+ !(aentry->num_ctcam_erps_scan > 0),
+ refcount_read(&lkey_id->refcnt) != 1, lkey_id->id,
+ kvdl_index);
+ mlxsw_reg_ptce3_prune_vector_pack(ptce3_pl, aentry->prune_bitmap,
+ MLXSW_SP_ACL_ERP_MAX_PER_REGION);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce3), ptce3_pl);
+}
+
static void
mlxsw_sp_acl_atcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_region *aregion,
@@ -423,12 +464,56 @@ mlxsw_sp_acl_atcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp,
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,
+ erp_id, !(aentry->num_ctcam_erps_scan > 0),
+ 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);
}
+void mlxsw_sp_acl_atcam_prune_vector_to_bitmap(bool non_def_prune_vector,
+ struct mlxsw_sp_acl_atcam_entry
+ *aentry)
+{
+ enum mlxsw_sp_acl_atcam_region_type region_type;
+ struct prune_vector_item *vector_item;
+ struct list_head *prune_vector_list;
+ struct mlxsw_sp_acl_erp *erp;
+ struct prune_table *table;
+
+ region_type = mlxsw_sp_acl_erp_region_type(aentry->erp);
+ if (region_type == MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB) {
+ /* Note: For SP-2 a region with 8 key blocks must be set to
+ * either to all 1's or all 0's.
+ * The driver set it to all 0's so this rule doesn't prune
+ * any other rules.
+ */
+ bitmap_zero(aentry->prune_bitmap,
+ MLXSW_SP_ACL_ERP_MAX_PER_REGION);
+ return;
+ }
+ bitmap_fill(aentry->prune_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
+
+ if (!non_def_prune_vector) /* prune all erps */
+ return;
+
+ /* set the relevant erps for further lookups */
+ prune_vector_list = prune_item_prune_vector(aentry->prune_item);
+ list_for_each_entry(vector_item, prune_vector_list, list) {
+ if (!vector_item->pruned) {
+ table = vector_item->table;
+ erp = prune_table_priv(table);
+ if (mlxsw_sp_acl_erp_is_ctcam_erp(erp)) {
+ /* look-up towards the CTCAM */
+ aentry->num_ctcam_erps_scan++;
+ continue;
+ }
+ mlxsw_sp2_acl_tcam_prune_vector_bit_set(erp, aentry,
+ false);
+ }
+ }
+}
+
static int
__mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_region *aregion,
@@ -436,21 +521,32 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei)
{
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);
+ char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 };
+ struct prune_item *prune_item;
struct mlxsw_sp_acl_erp *erp;
unsigned int blocks_count;
+ bool non_def_prune_vector;
+ u32 priority;
int err;
blocks_count = mlxsw_afk_key_info_blocks_count_get(region->key_info);
mlxsw_afk_encode(afk, region->key_info, &rulei->values,
aentry->ht_key.enc_key, mask, 0, blocks_count - 1);
+ err = mlxsw_sp_acl_tcam_priority_get(mlxsw_sp, rulei->priority,
+ &priority, true);
+ if (err)
+ return err;
+
erp = mlxsw_sp_acl_erp_get(aregion, mask, false);
if (IS_ERR(erp))
return PTR_ERR(erp);
+
aentry->erp = erp;
aentry->ht_key.erp_id = mlxsw_sp_acl_erp_id(erp);
+ bitmap_from_arr32(aentry->key_bitmap, (void *) aentry->ht_key.enc_key,
+ MLXSW_SP_ACL_TCAM_MASK_LEN);
/* We can't insert identical rules into the A-TCAM, so fail and
* let the rule spill into C-TCAM
@@ -461,14 +557,30 @@ __mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
if (err)
goto err_rhashtable_insert;
+ prune_item = prune_item_create(aregion->region->prune,
+ mlxsw_sp_acl_erp_prune_table(erp),
+ rulei->priority, aentry->key_bitmap,
+ aentry, &non_def_prune_vector);
+ if (IS_ERR(prune_item)) {
+ err = PTR_ERR(prune_item);
+ goto err_prune_item_create;
+ }
+ aentry->prune_item = prune_item;
+ aentry->num_ctcam_erps_scan = 0;
+
+ mlxsw_sp_acl_atcam_prune_vector_to_bitmap(non_def_prune_vector,
+ aentry);
err = mlxsw_sp_acl_atcam_region_entry_insert(mlxsw_sp, aregion, aentry,
- rulei);
+ rulei, priority);
if (err)
goto err_rule_insert;
return 0;
err_rule_insert:
+ if (!prune_item_destroy(aentry->prune_item))
+ WARN(1, "Failed to destroy prune item\n");
+err_prune_item_create:
rhashtable_remove_fast(&aregion->entries_ht, &aentry->ht_node,
mlxsw_sp_acl_atcam_entries_ht_params);
err_rhashtable_insert:
@@ -481,7 +593,13 @@ __mlxsw_sp_acl_atcam_entry_del(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_atcam_entry *aentry)
{
+ int err;
+
mlxsw_sp_acl_atcam_region_entry_remove(mlxsw_sp, aregion, aentry);
+ err = prune_item_destroy(aentry->prune_item);
+ WARN(err, "Failed to destroy prune item while removing rule with priority %u\n",
+ aentry->prune_item->priority);
+
rhashtable_remove_fast(&aregion->entries_ht, &aentry->ht_node,
mlxsw_sp_acl_atcam_entries_ht_params);
mlxsw_sp_acl_erp_put(aregion, aentry->erp);
@@ -53,8 +53,8 @@ mlxsw_sp_acl_ctcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
char *key;
int err;
- err = mlxsw_sp_acl_tcam_priority_get(mlxsw_sp, rulei, &priority,
- fillup_priority);
+ err = mlxsw_sp_acl_tcam_priority_get(mlxsw_sp, rulei->priority,
+ &priority, fillup_priority);
if (err)
return err;
@@ -67,7 +67,8 @@ mlxsw_sp_acl_ctcam_region_entry_insert(struct mlxsw_sp *mlxsw_sp,
mlxsw_afk_encode(afk, region->key_info, &rulei->values, key, mask, 0,
blocks_count - 1);
- err = cregion->ops->entry_insert(cregion, centry, mask);
+ err = cregion->ops->entry_insert(cregion, centry, key, mask,
+ rulei->priority);
if (err)
return err;
@@ -10,6 +10,7 @@
#include <linux/rhashtable.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
+#include <linux/prune.h>
#include "core.h"
#include "reg.h"
@@ -18,7 +19,6 @@
/* 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];
@@ -35,12 +35,13 @@ struct mlxsw_sp_acl_erp_key {
struct mlxsw_sp_acl_erp {
struct mlxsw_sp_acl_erp_key key;
u8 id;
- u8 index;
+ u8 index; /* used for erp index in prune vector */
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 prune_table *prune_table;
};
struct mlxsw_sp_acl_erp_master_mask {
@@ -129,6 +130,28 @@ u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp)
return erp->id;
}
+u8 mlxsw_sp_acl_erp_index(const struct mlxsw_sp_acl_erp *erp)
+{
+ return erp->index;
+}
+struct prune_table *
+mlxsw_sp_acl_erp_prune_table(const struct mlxsw_sp_acl_erp *erp)
+{
+ return erp->prune_table;
+}
+
+enum mlxsw_sp_acl_atcam_region_type
+mlxsw_sp_acl_erp_region_type(const struct mlxsw_sp_acl_erp *erp)
+{
+ return erp->erp_table->aregion->type;
+}
+
+struct mlxsw_sp_acl_atcam_region *
+mlxsw_sp_acl_erp_aregion(const struct mlxsw_sp_acl_erp *erp)
+{
+ return erp->erp_table->aregion;
+}
+
static unsigned int
mlxsw_sp_acl_erp_table_entry_size(const struct mlxsw_sp_acl_erp_table *erp_table)
{
@@ -245,6 +268,7 @@ 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;
+ struct prune *prune;
int err;
erp = kzalloc(sizeof(*erp), GFP_KERNEL);
@@ -272,8 +296,19 @@ mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table,
if (err)
goto err_rhashtable_insert;
+ prune = erp_table->aregion->region->prune;
+ erp->prune_table = prune_table_create(prune, erp->mask_bitmap,
+ MLXSW_SP_ACL_TCAM_MASK_LEN,
+ erp);
+ if (IS_ERR(erp->prune_table)) {
+ err = PTR_ERR(erp->prune_table);
+ goto err_prune_table_create;
+ }
return erp;
+err_prune_table_create:
+ rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
+ mlxsw_sp_acl_erp_ht_params);
err_rhashtable_insert:
mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
err_master_mask_set:
@@ -288,8 +323,12 @@ mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table,
static void
mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp *erp)
{
+ int err;
struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
+ err = prune_table_destroy(erp->prune_table);
+ WARN(err, "Failed to destroy prune table\n");
+
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);
@@ -688,6 +727,7 @@ __mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
struct mlxsw_sp_acl_erp_key *key)
{
struct mlxsw_sp_acl_erp *erp;
+ struct prune *prune;
int err;
erp = kzalloc(sizeof(*erp), GFP_KERNEL);
@@ -710,6 +750,15 @@ __mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
if (err)
goto err_rhashtable_insert;
+ prune = erp_table->aregion->region->prune;
+ erp->prune_table = prune_table_create(prune, erp->mask_bitmap,
+ MLXSW_SP_ACL_TCAM_MASK_LEN,
+ erp);
+ if (IS_ERR(erp->prune_table)) {
+ err = PTR_ERR(erp->prune_table);
+ goto err_prune_table_create;
+ }
+
err = mlxsw_sp_acl_erp_region_ctcam_enable(erp_table);
if (err)
goto err_erp_region_ctcam_enable;
@@ -720,6 +769,9 @@ __mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
return erp;
err_erp_region_ctcam_enable:
+ if (prune_table_destroy(erp->prune_table))
+ WARN(1, "Failed to destroy prune table err %d\n", err);
+err_prune_table_create:
rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
mlxsw_sp_acl_erp_ht_params);
err_rhashtable_insert:
@@ -765,8 +817,12 @@ static void
mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp *erp)
{
struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
+ int err;
mlxsw_sp_acl_erp_region_ctcam_disable(erp_table);
+ err = prune_table_destroy(erp->prune_table);
+ WARN(err, "Failed to destroy prune table\n");
+
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);
@@ -862,7 +918,6 @@ mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
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;
@@ -8,6 +8,7 @@
#include <linux/list.h>
#include <linux/rhashtable.h>
#include <linux/netdevice.h>
+#include <linux/prune.h>
#include "reg.h"
#include "core.h"
@@ -82,8 +83,8 @@ void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
}
int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_acl_rule_info *rulei,
- u32 *priority, bool fillup_priority)
+ u32 rule_priority, u32 *priority,
+ bool fillup_priority)
{
u64 max_priority;
@@ -96,11 +97,11 @@ int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
return -EIO;
max_priority = MLXSW_CORE_RES_GET(mlxsw_sp->core, KVD_SIZE);
- if (rulei->priority > max_priority)
+ if (rule_priority > max_priority)
return -EINVAL;
/* Unlike in TC, in HW, higher number means higher priority. */
- *priority = max_priority - rulei->priority;
+ *priority = max_priority - rule_priority;
return 0;
}
@@ -6,6 +6,7 @@
#include <linux/list.h>
#include <linux/parman.h>
+#include <linux/prune.h>
#include "reg.h"
#include "spectrum.h"
@@ -27,9 +28,8 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam *tcam);
int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_acl_rule_info *rulei,
- u32 *priority, bool fillup_priority);
-
+ u32 rule_priority, u32 *priority,
+ bool fillup_priority);
struct mlxsw_sp_acl_profile_ops {
size_t ruleset_priv_size;
int (*ruleset_add)(struct mlxsw_sp *mlxsw_sp,
@@ -58,6 +58,7 @@ mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,
#define MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT 16
#define MLXSW_SP_ACL_TCAM_REGION_RESIZE_STEP 16
+#define MLXSW_SP_ACL_ERP_MAX_PER_REGION 16
#define MLXSW_SP_ACL_TCAM_CATCHALL_PRIO (~0U)
@@ -75,6 +76,7 @@ struct mlxsw_sp_acl_tcam_region {
char tcam_region_info[MLXSW_REG_PXXX_TCAM_REGION_INFO_LEN];
struct mlxsw_afk_key_info *key_info;
struct mlxsw_sp *mlxsw_sp;
+ struct prune *prune;
unsigned long priv[0];
/* priv has to be always the last item */
};
@@ -96,7 +98,9 @@ struct mlxsw_sp_acl_ctcam_entry {
struct mlxsw_sp_acl_ctcam_region_ops {
int (*entry_insert)(struct mlxsw_sp_acl_ctcam_region *cregion,
struct mlxsw_sp_acl_ctcam_entry *centry,
- const char *mask);
+ const char *key,
+ const char *mask,
+ u32 priority);
void (*entry_remove)(struct mlxsw_sp_acl_ctcam_region *cregion,
struct mlxsw_sp_acl_ctcam_entry *centry);
};
@@ -168,6 +172,11 @@ struct mlxsw_sp_acl_atcam_entry {
struct mlxsw_sp_acl_ctcam_entry centry;
struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
struct mlxsw_sp_acl_erp *erp;
+ struct prune_item *prune_item;
+ DECLARE_BITMAP(key_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
+ DECLARE_BITMAP(prune_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
+ /* Counts the number of ctcam erps that the rule should scan */
+ unsigned int num_ctcam_erps_scan;
};
static inline struct mlxsw_sp_acl_atcam_region *
@@ -189,7 +198,9 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam,
struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_tcam_region *region,
- const struct mlxsw_sp_acl_ctcam_region_ops *ops);
+ const struct mlxsw_sp_acl_ctcam_region_ops *ops,
+ const struct prune_ops
+ *mlxsw_acl_atcam_prune_ops);
void mlxsw_sp_acl_atcam_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_atcam_chunk_init(struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_atcam_chunk *achunk,
@@ -208,11 +219,22 @@ int mlxsw_sp_acl_atcam_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);
void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);
-
+int
+mlxsw_sp_acl_atcam_region_entry_update(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_acl_atcam_region *aregion,
+ struct mlxsw_sp_acl_atcam_entry *aentry,
+ u32 priority);
struct mlxsw_sp_acl_erp;
bool mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp *erp);
u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp);
+u8 mlxsw_sp_acl_erp_index(const struct mlxsw_sp_acl_erp *erp);
+struct prune_table *
+mlxsw_sp_acl_erp_prune_table(const struct mlxsw_sp_acl_erp *erp);
+enum mlxsw_sp_acl_atcam_region_type
+mlxsw_sp_acl_erp_region_type(const struct mlxsw_sp_acl_erp *erp);
+struct mlxsw_sp_acl_atcam_region *
+mlxsw_sp_acl_erp_aregion(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, bool ctcam);
@@ -225,4 +247,12 @@ int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);
+struct mlxsw_afa_block;
+
+struct mlxsw_afa_block *
+mlxsw_sp2_acl_tcam_aentry_act_block(struct mlxsw_sp_acl_atcam_entry *aentry);
+void
+mlxsw_sp2_acl_tcam_prune_vector_bit_set(struct mlxsw_sp_acl_erp *erp,
+ struct mlxsw_sp_acl_atcam_entry *aentry,
+ bool pruned);
#endif
A packet needs to be evaluated against a data base of rules. For A-TCAM the ASIC has to do up to 16 lookups, 1 per ERP. In order to reduce the number of lookups the driver utilize the prube library. In Spectrum-2 the every rule has a prune_vector and a ctcam_prune. The prune_vector instruct the ASIC which ERPs are not relevant for further lookups and the ctcam_prune instructs the ASIC to do lookup on the C-TCAM. The driver set the vector according to rule priority and the ctcam_prune of the matched rule. That increase the performance, since ASIC make only the needed lookups. There is a special case for 8 key blocks region type. The ASIC support prune vector of all 1's (prune) or all 0's (do lookup). In the driver we choose to set all 0's therefore the performance will be less then optimal but correct. Signed-off-by: Tal Bar <talb@mellanox.com> --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 1 + drivers/net/ethernet/mellanox/mlxsw/reg.h | 16 ++- .../ethernet/mellanox/mlxsw/spectrum1_acl_tcam.c | 3 +- .../ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c | 154 ++++++++++++++++++++- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 8 ++ .../ethernet/mellanox/mlxsw/spectrum_acl_atcam.c | 138 ++++++++++++++++-- .../ethernet/mellanox/mlxsw/spectrum_acl_ctcam.c | 7 +- .../net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c | 61 +++++++- .../ethernet/mellanox/mlxsw/spectrum_acl_tcam.c | 9 +- .../ethernet/mellanox/mlxsw/spectrum_acl_tcam.h | 42 +++++- 10 files changed, 408 insertions(+), 31 deletions(-)