Merge branch 'mlxsw-Implement-periodic-ERP-rehash'

Ido Schimmel says:

====================
mlxsw: Implement periodic ERP rehash

Currently, an ERP set is created for each region according to rules
inserted and order of their insertion. However that might lead to
suboptimal ERP sets and possible unnecessary spillage into C-TCAM.
This patchset aims to fix this problem and introduces periodical checking
of used ERP sets and in case a better ERP set is possible for the given
set of rules, it rehashes the region to use the better ERP set.

Patch 1 prepares devlink params infra in order to fix the
        init/fini sequences.
Patch 2 implements hints infra in objagg library.
Patch 3 fixes a typo
Patch 4 adds number of root objects directly into objagg stats.
Patches 5-7 do split of multiple structs in Spectrum TCAM code.
Patch 8 introduces initial implementation of ERP rehash logic,
         according to objagg hints.
Patch 9 adds hints priv passing trought the layers.
Patch 10 adds multi field into PAGT reg. (new patch)
Patch 11 implements actual region rules migration in TCAM code.
Patch 12 adds a devlink param so user is able to control
         rehash interval.
Patch 13 adds couple of tracepoints in order to track
         rehash procedures.
Patch 14 adds a simple selftest to test region rehash.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-02-08 15:02:50 -08:00
commit 3e32675c05
22 changed files with 1966 additions and 323 deletions

View file

@ -1,2 +1,10 @@
fw_load_policy [DEVICE, GENERIC]
Configuration mode: driverinit
acl_region_rehash_interval [DEVICE, DRIVER-SPECIFIC]
Sets an interval for periodic ACL region rehashes.
The value is in milliseconds, minimal value is "3000".
Value "0" disables the periodic work.
The first rehash will be run right after value is set.
Type: u32
Configuration mode: runtime

View file

@ -258,6 +258,9 @@ int bnxt_dl_register(struct bnxt *bp)
netdev_err(bp->dev, "devlink_port_params_register failed");
goto err_dl_port_unreg;
}
devlink_params_publish(dl);
return 0;
err_dl_port_unreg:

View file

@ -3981,6 +3981,7 @@ static int mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret)
goto err_params_unregister;
devlink_params_publish(devlink);
pci_save_state(pdev);
return 0;

View file

@ -1062,6 +1062,9 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
goto err_driver_init;
}
if (mlxsw_driver->params_register && !reload)
devlink_params_publish(devlink);
return 0;
err_driver_init:
@ -1131,6 +1134,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
return;
}
if (mlxsw_core->driver->params_unregister && !reload)
devlink_params_unpublish(devlink);
if (mlxsw_core->driver->fini)
mlxsw_core->driver->fini(mlxsw_core);
mlxsw_thermal_fini(mlxsw_core->thermal);

View file

@ -394,4 +394,9 @@ static inline void mlxsw_thermal_fini(struct mlxsw_thermal *thermal)
#endif
enum mlxsw_devlink_param_id {
MLXSW_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
};
#endif

View file

@ -2199,6 +2199,14 @@ MLXSW_ITEM32(reg, pagt, size, 0x00, 0, 8);
*/
MLXSW_ITEM32(reg, pagt, acl_group_id, 0x08, 0, 16);
/* reg_pagt_multi
* Multi-ACL
* 0 - This ACL is the last ACL in the multi-ACL
* 1 - This ACL is part of a multi-ACL
* Access: RW
*/
MLXSW_ITEM32_INDEXED(reg, pagt, multi, 0x30, 31, 1, 0x04, 0x00, false);
/* reg_pagt_acl_id
* ACL identifier
* Access: RW
@ -2212,12 +2220,13 @@ static inline void mlxsw_reg_pagt_pack(char *payload, u16 acl_group_id)
}
static inline void mlxsw_reg_pagt_acl_id_pack(char *payload, int index,
u16 acl_id)
u16 acl_id, bool multi)
{
u8 size = mlxsw_reg_pagt_size_get(payload);
if (index >= size)
mlxsw_reg_pagt_size_set(payload, index + 1);
mlxsw_reg_pagt_multi_set(payload, index, multi);
mlxsw_reg_pagt_acl_id_set(payload, index, acl_id);
}

View file

@ -4413,6 +4413,71 @@ static void mlxsw_sp_params_unregister(struct mlxsw_core *mlxsw_core)
ARRAY_SIZE(mlxsw_sp_devlink_params));
}
static int
mlxsw_sp_params_acl_region_rehash_intrvl_get(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
ctx->val.vu32 = mlxsw_sp_acl_region_rehash_intrvl_get(mlxsw_sp);
return 0;
}
static int
mlxsw_sp_params_acl_region_rehash_intrvl_set(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
return mlxsw_sp_acl_region_rehash_intrvl_set(mlxsw_sp, ctx->val.vu32);
}
static const struct devlink_param mlxsw_sp2_devlink_params[] = {
DEVLINK_PARAM_DRIVER(MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
"acl_region_rehash_interval",
DEVLINK_PARAM_TYPE_U32,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
mlxsw_sp_params_acl_region_rehash_intrvl_get,
mlxsw_sp_params_acl_region_rehash_intrvl_set,
NULL),
};
static int mlxsw_sp2_params_register(struct mlxsw_core *mlxsw_core)
{
struct devlink *devlink = priv_to_devlink(mlxsw_core);
union devlink_param_value value;
int err;
err = mlxsw_sp_params_register(mlxsw_core);
if (err)
return err;
err = devlink_params_register(devlink, mlxsw_sp2_devlink_params,
ARRAY_SIZE(mlxsw_sp2_devlink_params));
if (err)
goto err_devlink_params_register;
value.vu32 = 0;
devlink_param_driverinit_value_set(devlink,
MLXSW_DEVLINK_PARAM_ID_ACL_REGION_REHASH_INTERVAL,
value);
return 0;
err_devlink_params_register:
mlxsw_sp_params_unregister(mlxsw_core);
return err;
}
static void mlxsw_sp2_params_unregister(struct mlxsw_core *mlxsw_core)
{
devlink_params_unregister(priv_to_devlink(mlxsw_core),
mlxsw_sp2_devlink_params,
ARRAY_SIZE(mlxsw_sp2_devlink_params));
mlxsw_sp_params_unregister(mlxsw_core);
}
static struct mlxsw_driver mlxsw_sp1_driver = {
.kind = mlxsw_sp1_driver_name,
.priv_size = sizeof(struct mlxsw_sp),
@ -4461,8 +4526,8 @@ static struct mlxsw_driver mlxsw_sp2_driver = {
.sb_occ_tc_port_bind_get = mlxsw_sp_sb_occ_tc_port_bind_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register,
.params_register = mlxsw_sp_params_register,
.params_unregister = mlxsw_sp_params_unregister,
.params_register = mlxsw_sp2_params_register,
.params_unregister = mlxsw_sp2_params_unregister,
.txhdr_len = MLXSW_TXHDR_LEN,
.profile = &mlxsw_sp2_config_profile,
.res_query_enabled = true,

View file

@ -690,6 +690,8 @@ struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp);
u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val);
/* spectrum_acl_tcam.c */
struct mlxsw_sp_acl_tcam;
@ -704,10 +706,13 @@ struct mlxsw_sp_acl_tcam_ops {
size_t region_priv_size;
int (*region_init)(struct mlxsw_sp *mlxsw_sp, void *region_priv,
void *tcam_priv,
struct mlxsw_sp_acl_tcam_region *region);
struct mlxsw_sp_acl_tcam_region *region,
void *hints_priv);
void (*region_fini)(struct mlxsw_sp *mlxsw_sp, void *region_priv);
int (*region_associate)(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam_region *region);
void * (*region_rehash_hints_get)(void *region_priv);
void (*region_rehash_hints_put)(void *hints_priv);
size_t chunk_priv_size;
void (*chunk_init)(void *region_priv, void *chunk_priv,
unsigned int priority);

View file

@ -112,7 +112,8 @@ mlxsw_sp1_acl_ctcam_region_catchall_del(struct mlxsw_sp *mlxsw_sp,
static int
mlxsw_sp1_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
void *tcam_priv,
struct mlxsw_sp_acl_tcam_region *_region)
struct mlxsw_sp_acl_tcam_region *_region,
void *hints_priv)
{
struct mlxsw_sp1_acl_tcam_region *region = region_priv;
int err;

View file

@ -139,7 +139,8 @@ static void mlxsw_sp2_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
static int
mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
void *tcam_priv,
struct mlxsw_sp_acl_tcam_region *_region)
struct mlxsw_sp_acl_tcam_region *_region,
void *hints_priv)
{
struct mlxsw_sp2_acl_tcam_region *region = region_priv;
struct mlxsw_sp2_acl_tcam *tcam = tcam_priv;
@ -147,7 +148,8 @@ mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
region->region = _region;
return mlxsw_sp_acl_atcam_region_init(mlxsw_sp, &tcam->atcam,
&region->aregion, _region,
&region->aregion,
_region, hints_priv,
&mlxsw_sp2_acl_ctcam_region_ops);
}
@ -166,6 +168,18 @@ mlxsw_sp2_acl_tcam_region_associate(struct mlxsw_sp *mlxsw_sp,
return mlxsw_sp_acl_atcam_region_associate(mlxsw_sp, region->id);
}
static void *mlxsw_sp2_acl_tcam_region_rehash_hints_get(void *region_priv)
{
struct mlxsw_sp2_acl_tcam_region *region = region_priv;
return mlxsw_sp_acl_atcam_rehash_hints_get(&region->aregion);
}
static void mlxsw_sp2_acl_tcam_region_rehash_hints_put(void *hints_priv)
{
mlxsw_sp_acl_atcam_rehash_hints_put(hints_priv);
}
static void mlxsw_sp2_acl_tcam_chunk_init(void *region_priv, void *chunk_priv,
unsigned int priority)
{
@ -243,6 +257,8 @@ const struct mlxsw_sp_acl_tcam_ops mlxsw_sp2_acl_tcam_ops = {
.region_init = mlxsw_sp2_acl_tcam_region_init,
.region_fini = mlxsw_sp2_acl_tcam_region_fini,
.region_associate = mlxsw_sp2_acl_tcam_region_associate,
.region_rehash_hints_get = mlxsw_sp2_acl_tcam_region_rehash_hints_get,
.region_rehash_hints_put = mlxsw_sp2_acl_tcam_region_rehash_hints_put,
.chunk_priv_size = sizeof(struct mlxsw_sp2_acl_tcam_chunk),
.chunk_init = mlxsw_sp2_acl_tcam_chunk_init,
.chunk_fini = mlxsw_sp2_acl_tcam_chunk_fini,

View file

@ -640,7 +640,7 @@ mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
int err;
mlxsw_sp_acl_ruleset_ref_inc(ruleset);
rule = kzalloc(sizeof(*rule) + ops->rule_priv_size(mlxsw_sp),
rule = kzalloc(sizeof(*rule) + ops->rule_priv_size,
GFP_KERNEL);
if (!rule) {
err = -ENOMEM;
@ -912,3 +912,19 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_afk_destroy(acl->afk);
kfree(acl);
}
u32 mlxsw_sp_acl_region_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp)
{
struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_get(mlxsw_sp,
&acl->tcam);
}
int mlxsw_sp_acl_region_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp, u32 val)
{
struct mlxsw_sp_acl *acl = mlxsw_sp->acl;
return mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(mlxsw_sp,
&acl->tcam, val);
}

View file

@ -318,6 +318,7 @@ 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,
void *hints_priv,
const struct mlxsw_sp_acl_ctcam_region_ops *ops)
{
int err;
@ -334,7 +335,7 @@ mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
err = aregion->ops->init(aregion);
if (err)
goto err_ops_init;
err = mlxsw_sp_acl_erp_region_init(aregion);
err = mlxsw_sp_acl_erp_region_init(aregion, hints_priv);
if (err)
goto err_erp_region_init;
err = mlxsw_sp_acl_ctcam_region_init(mlxsw_sp, &aregion->cregion,
@ -634,3 +635,14 @@ void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
{
mlxsw_sp_acl_erps_fini(mlxsw_sp, atcam);
}
void *
mlxsw_sp_acl_atcam_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion)
{
return mlxsw_sp_acl_erp_rehash_hints_get(aregion);
}
void mlxsw_sp_acl_atcam_rehash_hints_put(void *hints_priv)
{
mlxsw_sp_acl_erp_rehash_hints_put(hints_priv);
}

View file

@ -1200,6 +1200,32 @@ mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key,
return 0;
}
static bool mlxsw_sp_acl_erp_delta_check(void *priv, const void *parent_obj,
const void *obj)
{
const struct mlxsw_sp_acl_erp_key *parent_key = parent_obj;
const struct mlxsw_sp_acl_erp_key *key = obj;
u16 delta_start;
u8 delta_mask;
int err;
err = mlxsw_sp_acl_erp_delta_fill(parent_key, key,
&delta_start, &delta_mask);
return err ? false : true;
}
static int mlxsw_sp_acl_erp_hints_obj_cmp(const void *obj1, const void *obj2)
{
const struct mlxsw_sp_acl_erp_key *key1 = obj1;
const struct mlxsw_sp_acl_erp_key *key2 = obj2;
/* For hints purposes, two objects are considered equal
* in case the masks are the same. Does not matter what
* the "ctcam" value is.
*/
return memcmp(key1->mask, key2->mask, sizeof(key1->mask));
}
static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj,
void *obj)
{
@ -1254,12 +1280,17 @@ static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv)
kfree(delta);
}
static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj)
static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj,
unsigned int root_id)
{
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;
if (!key->ctcam &&
root_id != OBJAGG_OBJ_ROOT_ID_INVALID &&
root_id >= MLXSW_SP_ACL_ERP_MAX_PER_REGION)
return ERR_PTR(-ENOBUFS);
return erp_table->ops->erp_create(erp_table, key);
}
@ -1273,6 +1304,8 @@ static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv)
static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = {
.obj_size = sizeof(struct mlxsw_sp_acl_erp_key),
.delta_check = mlxsw_sp_acl_erp_delta_check,
.hints_obj_cmp = mlxsw_sp_acl_erp_hints_obj_cmp,
.delta_create = mlxsw_sp_acl_erp_delta_create,
.delta_destroy = mlxsw_sp_acl_erp_delta_destroy,
.root_create = mlxsw_sp_acl_erp_root_create,
@ -1280,7 +1313,8 @@ static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = {
};
static struct mlxsw_sp_acl_erp_table *
mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion,
struct objagg_hints *hints)
{
struct mlxsw_sp_acl_erp_table *erp_table;
int err;
@ -1290,7 +1324,7 @@ mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
return ERR_PTR(-ENOMEM);
erp_table->objagg = objagg_create(&mlxsw_sp_acl_erp_objagg_ops,
aregion);
hints, aregion);
if (IS_ERR(erp_table->objagg)) {
err = PTR_ERR(erp_table->objagg);
goto err_objagg_create;
@ -1337,12 +1371,88 @@ mlxsw_sp_acl_erp_region_param_init(struct mlxsw_sp_acl_atcam_region *aregion)
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
}
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion)
static int
mlxsw_sp_acl_erp_hints_check(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_region *aregion,
struct objagg_hints *hints, bool *p_rehash_needed)
{
struct mlxsw_sp_acl_erp_table *erp_table;
struct objagg *objagg = aregion->erp_table->objagg;
const struct objagg_stats *ostats;
const struct objagg_stats *hstats;
int err;
erp_table = mlxsw_sp_acl_erp_table_create(aregion);
*p_rehash_needed = false;
ostats = objagg_stats_get(objagg);
if (IS_ERR(ostats)) {
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get ERP stats\n");
return PTR_ERR(ostats);
}
hstats = objagg_hints_stats_get(hints);
if (IS_ERR(hstats)) {
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get ERP hints stats\n");
err = PTR_ERR(hstats);
goto err_hints_stats_get;
}
/* Very basic criterion for now. */
if (hstats->root_count < ostats->root_count)
*p_rehash_needed = true;
err = 0;
objagg_stats_put(hstats);
err_hints_stats_get:
objagg_stats_put(ostats);
return err;
}
void *
mlxsw_sp_acl_erp_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion)
{
struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp;
struct objagg_hints *hints;
bool rehash_needed;
int err;
hints = objagg_hints_get(aregion->erp_table->objagg,
OBJAGG_OPT_ALGO_SIMPLE_GREEDY);
if (IS_ERR(hints)) {
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to create ERP hints\n");
return ERR_CAST(hints);
}
err = mlxsw_sp_acl_erp_hints_check(mlxsw_sp, aregion, hints,
&rehash_needed);
if (err)
goto errout;
if (!rehash_needed) {
err = -EAGAIN;
goto errout;
}
return hints;
errout:
objagg_hints_put(hints);
return ERR_PTR(err);
}
void mlxsw_sp_acl_erp_rehash_hints_put(void *hints_priv)
{
struct objagg_hints *hints = hints_priv;
objagg_hints_put(hints);
}
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion,
void *hints_priv)
{
struct mlxsw_sp_acl_erp_table *erp_table;
struct objagg_hints *hints = hints_priv;
int err;
erp_table = mlxsw_sp_acl_erp_table_create(aregion, hints);
if (IS_ERR(erp_table))
return PTR_ERR(erp_table);
aregion->erp_table = erp_table;

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,8 @@ struct mlxsw_sp_acl_tcam {
unsigned long *used_groups; /* bit array */
unsigned int max_groups;
unsigned int max_group_size;
struct list_head vregion_list;
u32 vregion_rehash_intrvl; /* ms */
unsigned long priv[0];
/* priv has to be always the last item */
};
@ -26,6 +28,11 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam *tcam);
void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam *tcam);
u32 mlxsw_sp_acl_tcam_vregion_rehash_intrvl_get(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam *tcam);
int mlxsw_sp_acl_tcam_vregion_rehash_intrvl_set(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_tcam *tcam,
u32 val);
int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei,
u32 *priority, bool fillup_priority);
@ -43,7 +50,7 @@ struct mlxsw_sp_acl_profile_ops {
struct mlxsw_sp_port *mlxsw_sp_port,
bool ingress);
u16 (*ruleset_group_id)(void *ruleset_priv);
size_t (*rule_priv_size)(struct mlxsw_sp *mlxsw_sp);
size_t rule_priv_size;
int (*rule_add)(struct mlxsw_sp *mlxsw_sp,
void *ruleset_priv, void *rule_priv,
struct mlxsw_sp_acl_rule_info *rulei);
@ -67,11 +74,10 @@ mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,
(MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN * BITS_PER_BYTE)
struct mlxsw_sp_acl_tcam_group;
struct mlxsw_sp_acl_tcam_vregion;
struct mlxsw_sp_acl_tcam_region {
struct list_head list; /* Member of a TCAM group */
struct list_head chunk_list; /* List of chunks under this region */
struct mlxsw_sp_acl_tcam_group *group;
struct mlxsw_sp_acl_tcam_vregion *vregion;
enum mlxsw_reg_ptar_key_type key_type;
u16 id; /* ACL ID and region ID - they are same */
char tcam_region_info[MLXSW_REG_PXXX_TCAM_REGION_INFO_LEN];
@ -207,6 +213,7 @@ 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,
void *hints_priv,
const struct mlxsw_sp_acl_ctcam_region_ops *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,
@ -230,6 +237,9 @@ 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);
void *
mlxsw_sp_acl_atcam_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_atcam_rehash_hints_put(void *hints_priv);
struct mlxsw_sp_acl_erp_delta;
@ -260,7 +270,11 @@ void mlxsw_sp_acl_erp_bf_remove(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam_region *aregion,
struct mlxsw_sp_acl_erp_mask *erp_mask,
struct mlxsw_sp_acl_atcam_entry *aentry);
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion);
void *
mlxsw_sp_acl_erp_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_erp_rehash_hints_put(void *hints_priv);
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion,
void *hints_priv);
void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_atcam *atcam);

View file

@ -6,14 +6,19 @@
struct objagg_ops {
size_t obj_size;
bool (*delta_check)(void *priv, const void *parent_obj,
const void *obj);
int (*hints_obj_cmp)(const void *obj1, const void *obj2);
void * (*delta_create)(void *priv, void *parent_obj, void *obj);
void (*delta_destroy)(void *priv, void *delta_priv);
void * (*root_create)(void *priv, void *obj);
void * (*root_create)(void *priv, void *obj, unsigned int root_id);
#define OBJAGG_OBJ_ROOT_ID_INVALID UINT_MAX
void (*root_destroy)(void *priv, void *root_priv);
};
struct objagg;
struct objagg_obj;
struct objagg_hints;
const void *objagg_obj_root_priv(const struct objagg_obj *objagg_obj);
const void *objagg_obj_delta_priv(const struct objagg_obj *objagg_obj);
@ -21,7 +26,8 @@ const void *objagg_obj_raw(const struct objagg_obj *objagg_obj);
struct objagg_obj *objagg_obj_get(struct objagg *objagg, void *obj);
void objagg_obj_put(struct objagg *objagg, struct objagg_obj *objagg_obj);
struct objagg *objagg_create(const struct objagg_ops *ops, void *priv);
struct objagg *objagg_create(const struct objagg_ops *ops,
struct objagg_hints *hints, void *priv);
void objagg_destroy(struct objagg *objagg);
struct objagg_obj_stats {
@ -36,6 +42,7 @@ struct objagg_obj_stats_info {
};
struct objagg_stats {
unsigned int root_count;
unsigned int stats_info_count;
struct objagg_obj_stats_info stats_info[];
};
@ -43,4 +50,14 @@ struct objagg_stats {
const struct objagg_stats *objagg_stats_get(struct objagg *objagg);
void objagg_stats_put(const struct objagg_stats *objagg_stats);
enum objagg_opt_algo_type {
OBJAGG_OPT_ALGO_SIMPLE_GREEDY,
};
struct objagg_hints *objagg_hints_get(struct objagg *objagg,
enum objagg_opt_algo_type opt_algo_type);
void objagg_hints_put(struct objagg_hints *objagg_hints);
const struct objagg_stats *
objagg_hints_stats_get(struct objagg_hints *objagg_hints);
#endif

View file

@ -358,6 +358,7 @@ struct devlink_param_item {
const struct devlink_param *param;
union devlink_param_value driverinit_value;
bool driverinit_value_valid;
bool published;
};
enum devlink_param_generic_id {
@ -618,6 +619,8 @@ int devlink_params_register(struct devlink *devlink,
void devlink_params_unregister(struct devlink *devlink,
const struct devlink_param *params,
size_t params_count);
void devlink_params_publish(struct devlink *devlink);
void devlink_params_unpublish(struct devlink *devlink);
int devlink_port_params_register(struct devlink_port *devlink_port,
const struct devlink_param *params,
size_t params_count);
@ -724,6 +727,14 @@ static inline void devlink_unregister(struct devlink *devlink)
{
}
static inline void devlink_params_publish(struct devlink *devlink)
{
}
static inline void devlink_params_unpublish(struct devlink *devlink)
{
}
static inline void devlink_free(struct devlink *devlink)
{
kfree(devlink);

View file

@ -11,6 +11,7 @@
struct mlxsw_sp;
struct mlxsw_sp_acl_atcam_region;
struct mlxsw_sp_acl_tcam_vregion;
TRACE_EVENT(mlxsw_sp_acl_atcam_entry_add_ctcam_spill,
TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
@ -32,6 +33,66 @@ TRACE_EVENT(mlxsw_sp_acl_atcam_entry_add_ctcam_spill,
__entry->mlxsw_sp, __entry->aregion)
);
TRACE_EVENT(mlxsw_sp_acl_tcam_vregion_rehash,
TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_acl_tcam_vregion *vregion),
TP_ARGS(mlxsw_sp, vregion),
TP_STRUCT__entry(
__field(const void *, mlxsw_sp)
__field(const void *, vregion)
),
TP_fast_assign(
__entry->mlxsw_sp = mlxsw_sp;
__entry->vregion = vregion;
),
TP_printk("mlxsw_sp %p, vregion %p",
__entry->mlxsw_sp, __entry->vregion)
);
TRACE_EVENT(mlxsw_sp_acl_tcam_vregion_migrate,
TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_acl_tcam_vregion *vregion),
TP_ARGS(mlxsw_sp, vregion),
TP_STRUCT__entry(
__field(const void *, mlxsw_sp)
__field(const void *, vregion)
),
TP_fast_assign(
__entry->mlxsw_sp = mlxsw_sp;
__entry->vregion = vregion;
),
TP_printk("mlxsw_sp %p, vregion %p",
__entry->mlxsw_sp, __entry->vregion)
);
TRACE_EVENT(mlxsw_sp_acl_tcam_vregion_rehash_dis,
TP_PROTO(const struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_acl_tcam_vregion *vregion),
TP_ARGS(mlxsw_sp, vregion),
TP_STRUCT__entry(
__field(const void *, mlxsw_sp)
__field(const void *, vregion)
),
TP_fast_assign(
__entry->mlxsw_sp = mlxsw_sp;
__entry->vregion = vregion;
),
TP_printk("mlxsw_sp %p, vregion %p",
__entry->mlxsw_sp, __entry->vregion)
);
#endif /* _MLXSW_TRACEPOINT_H */
/* This part must be outside protection */

View file

@ -4,6 +4,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/rhashtable.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/sort.h>
#include <linux/objagg.h>
@ -11,6 +12,34 @@
#define CREATE_TRACE_POINTS
#include <trace/events/objagg.h>
struct objagg_hints {
struct rhashtable node_ht;
struct rhashtable_params ht_params;
struct list_head node_list;
unsigned int node_count;
unsigned int root_count;
unsigned int refcount;
const struct objagg_ops *ops;
};
struct objagg_hints_node {
struct rhash_head ht_node; /* member of objagg_hints->node_ht */
struct list_head list; /* member of objagg_hints->node_list */
struct objagg_hints_node *parent;
unsigned int root_id;
struct objagg_obj_stats_info stats_info;
unsigned long obj[0];
};
static struct objagg_hints_node *
objagg_hints_lookup(struct objagg_hints *objagg_hints, void *obj)
{
if (!objagg_hints)
return NULL;
return rhashtable_lookup_fast(&objagg_hints->node_ht, obj,
objagg_hints->ht_params);
}
struct objagg {
const struct objagg_ops *ops;
void *priv;
@ -18,6 +47,8 @@ struct objagg {
struct rhashtable_params ht_params;
struct list_head obj_list;
unsigned int obj_count;
struct ida root_ida;
struct objagg_hints *hints;
};
struct objagg_obj {
@ -30,6 +61,7 @@ struct objagg_obj {
void *delta_priv; /* user delta private */
void *root_priv; /* user root private */
};
unsigned int root_id;
unsigned int refcount; /* counts number of users of this object
* including nested objects
*/
@ -130,7 +162,8 @@ static struct objagg_obj *objagg_obj_lookup(struct objagg *objagg, void *obj)
static int objagg_obj_parent_assign(struct objagg *objagg,
struct objagg_obj *objagg_obj,
struct objagg_obj *parent)
struct objagg_obj *parent,
bool take_parent_ref)
{
void *delta_priv;
@ -144,7 +177,8 @@ static int objagg_obj_parent_assign(struct objagg *objagg,
*/
objagg_obj->parent = parent;
objagg_obj->delta_priv = delta_priv;
objagg_obj_ref_inc(objagg_obj->parent);
if (take_parent_ref)
objagg_obj_ref_inc(objagg_obj->parent);
trace_objagg_obj_parent_assign(objagg, objagg_obj,
parent,
parent->refcount);
@ -164,7 +198,7 @@ static int objagg_obj_parent_lookup_assign(struct objagg *objagg,
if (!objagg_obj_is_root(objagg_obj_cur))
continue;
err = objagg_obj_parent_assign(objagg, objagg_obj,
objagg_obj_cur);
objagg_obj_cur, true);
if (!err)
return 0;
}
@ -184,16 +218,68 @@ static void objagg_obj_parent_unassign(struct objagg *objagg,
__objagg_obj_put(objagg, objagg_obj->parent);
}
static int objagg_obj_root_create(struct objagg *objagg,
struct objagg_obj *objagg_obj)
static int objagg_obj_root_id_alloc(struct objagg *objagg,
struct objagg_obj *objagg_obj,
struct objagg_hints_node *hnode)
{
objagg_obj->root_priv = objagg->ops->root_create(objagg->priv,
objagg_obj->obj);
if (IS_ERR(objagg_obj->root_priv))
return PTR_ERR(objagg_obj->root_priv);
unsigned int min, max;
int root_id;
/* In case there are no hints available, the root id is invalid. */
if (!objagg->hints) {
objagg_obj->root_id = OBJAGG_OBJ_ROOT_ID_INVALID;
return 0;
}
if (hnode) {
min = hnode->root_id;
max = hnode->root_id;
} else {
/* For objects with no hint, start after the last
* hinted root_id.
*/
min = objagg->hints->root_count;
max = ~0;
}
root_id = ida_alloc_range(&objagg->root_ida, min, max, GFP_KERNEL);
if (root_id < 0)
return root_id;
objagg_obj->root_id = root_id;
return 0;
}
static void objagg_obj_root_id_free(struct objagg *objagg,
struct objagg_obj *objagg_obj)
{
if (!objagg->hints)
return;
ida_free(&objagg->root_ida, objagg_obj->root_id);
}
static int objagg_obj_root_create(struct objagg *objagg,
struct objagg_obj *objagg_obj,
struct objagg_hints_node *hnode)
{
int err;
err = objagg_obj_root_id_alloc(objagg, objagg_obj, hnode);
if (err)
return err;
objagg_obj->root_priv = objagg->ops->root_create(objagg->priv,
objagg_obj->obj,
objagg_obj->root_id);
if (IS_ERR(objagg_obj->root_priv)) {
err = PTR_ERR(objagg_obj->root_priv);
goto err_root_create;
}
trace_objagg_obj_root_create(objagg, objagg_obj);
return 0;
err_root_create:
objagg_obj_root_id_free(objagg, objagg_obj);
return err;
}
static void objagg_obj_root_destroy(struct objagg *objagg,
@ -201,19 +287,69 @@ static void objagg_obj_root_destroy(struct objagg *objagg,
{
trace_objagg_obj_root_destroy(objagg, objagg_obj);
objagg->ops->root_destroy(objagg->priv, objagg_obj->root_priv);
objagg_obj_root_id_free(objagg, objagg_obj);
}
static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj);
static int objagg_obj_init_with_hints(struct objagg *objagg,
struct objagg_obj *objagg_obj,
bool *hint_found)
{
struct objagg_hints_node *hnode;
struct objagg_obj *parent;
int err;
hnode = objagg_hints_lookup(objagg->hints, objagg_obj->obj);
if (!hnode) {
*hint_found = false;
return 0;
}
*hint_found = true;
if (!hnode->parent)
return objagg_obj_root_create(objagg, objagg_obj, hnode);
parent = __objagg_obj_get(objagg, hnode->parent->obj);
if (IS_ERR(parent))
return PTR_ERR(parent);
err = objagg_obj_parent_assign(objagg, objagg_obj, parent, false);
if (err) {
*hint_found = false;
err = 0;
goto err_parent_assign;
}
return 0;
err_parent_assign:
objagg_obj_put(objagg, parent);
return err;
}
static int objagg_obj_init(struct objagg *objagg,
struct objagg_obj *objagg_obj)
{
bool hint_found;
int err;
/* First, try to use hints if they are available and
* if they provide result.
*/
err = objagg_obj_init_with_hints(objagg, objagg_obj, &hint_found);
if (err)
return err;
if (hint_found)
return 0;
/* Try to find if the object can be aggregated under an existing one. */
err = objagg_obj_parent_lookup_assign(objagg, objagg_obj);
if (!err)
return 0;
/* If aggregation is not possible, make the object a root. */
return objagg_obj_root_create(objagg, objagg_obj);
return objagg_obj_root_create(objagg, objagg_obj, NULL);
}
static void objagg_obj_fini(struct objagg *objagg,
@ -349,8 +485,9 @@ EXPORT_SYMBOL(objagg_obj_put);
/**
* objagg_create - creates a new objagg instance
* @ops: user-specific callbacks
* @priv: pointer to a private data passed to the ops
* @ops: user-specific callbacks
* @objagg_hints: hints, can be NULL
* @priv: pointer to a private data passed to the ops
*
* Note: all locking must be provided by the caller.
*
@ -374,18 +511,25 @@ EXPORT_SYMBOL(objagg_obj_put);
* Returns a pointer to newly created objagg instance in case of success,
* otherwise it returns pointer error using ERR_PTR macro.
*/
struct objagg *objagg_create(const struct objagg_ops *ops, void *priv)
struct objagg *objagg_create(const struct objagg_ops *ops,
struct objagg_hints *objagg_hints, void *priv)
{
struct objagg *objagg;
int err;
if (WARN_ON(!ops || !ops->root_create || !ops->root_destroy ||
!ops->delta_create || !ops->delta_destroy))
!ops->delta_check || !ops->delta_create ||
!ops->delta_destroy))
return ERR_PTR(-EINVAL);
objagg = kzalloc(sizeof(*objagg), GFP_KERNEL);
if (!objagg)
return ERR_PTR(-ENOMEM);
objagg->ops = ops;
if (objagg_hints) {
objagg->hints = objagg_hints;
objagg_hints->refcount++;
}
objagg->priv = priv;
INIT_LIST_HEAD(&objagg->obj_list);
@ -397,6 +541,8 @@ struct objagg *objagg_create(const struct objagg_ops *ops, void *priv)
if (err)
goto err_rhashtable_init;
ida_init(&objagg->root_ida);
trace_objagg_create(objagg);
return objagg;
@ -415,8 +561,11 @@ EXPORT_SYMBOL(objagg_create);
void objagg_destroy(struct objagg *objagg)
{
trace_objagg_destroy(objagg);
ida_destroy(&objagg->root_ida);
WARN_ON(!list_empty(&objagg->obj_list));
rhashtable_destroy(&objagg->obj_ht);
if (objagg->hints)
objagg_hints_put(objagg->hints);
kfree(objagg);
}
EXPORT_SYMBOL(objagg_destroy);
@ -472,6 +621,8 @@ const struct objagg_stats *objagg_stats_get(struct objagg *objagg)
objagg_stats->stats_info[i].objagg_obj = objagg_obj;
objagg_stats->stats_info[i].is_root =
objagg_obj_is_root(objagg_obj);
if (objagg_stats->stats_info[i].is_root)
objagg_stats->root_count++;
i++;
}
objagg_stats->stats_info_count = i;
@ -485,7 +636,7 @@ const struct objagg_stats *objagg_stats_get(struct objagg *objagg)
EXPORT_SYMBOL(objagg_stats_get);
/**
* objagg_stats_puts - puts stats of the objagg instance
* objagg_stats_put - puts stats of the objagg instance
* @objagg_stats: objagg instance stats
*
* Note: all locking must be provided by the caller.
@ -496,6 +647,406 @@ void objagg_stats_put(const struct objagg_stats *objagg_stats)
}
EXPORT_SYMBOL(objagg_stats_put);
static struct objagg_hints_node *
objagg_hints_node_create(struct objagg_hints *objagg_hints,
struct objagg_obj *objagg_obj, size_t obj_size,
struct objagg_hints_node *parent_hnode)
{
unsigned int user_count = objagg_obj->stats.user_count;
struct objagg_hints_node *hnode;
int err;
hnode = kzalloc(sizeof(*hnode) + obj_size, GFP_KERNEL);
if (!hnode)
return ERR_PTR(-ENOMEM);
memcpy(hnode->obj, &objagg_obj->obj, obj_size);
hnode->stats_info.stats.user_count = user_count;
hnode->stats_info.stats.delta_user_count = user_count;
if (parent_hnode) {
parent_hnode->stats_info.stats.delta_user_count += user_count;
} else {
hnode->root_id = objagg_hints->root_count++;
hnode->stats_info.is_root = true;
}
hnode->stats_info.objagg_obj = objagg_obj;
err = rhashtable_insert_fast(&objagg_hints->node_ht, &hnode->ht_node,
objagg_hints->ht_params);
if (err)
goto err_ht_insert;
list_add(&hnode->list, &objagg_hints->node_list);
hnode->parent = parent_hnode;
objagg_hints->node_count++;
return hnode;
err_ht_insert:
kfree(hnode);
return ERR_PTR(err);
}
static void objagg_hints_flush(struct objagg_hints *objagg_hints)
{
struct objagg_hints_node *hnode, *tmp;
list_for_each_entry_safe(hnode, tmp, &objagg_hints->node_list, list) {
list_del(&hnode->list);
rhashtable_remove_fast(&objagg_hints->node_ht, &hnode->ht_node,
objagg_hints->ht_params);
kfree(hnode);
}
}
struct objagg_tmp_node {
struct objagg_obj *objagg_obj;
bool crossed_out;
};
struct objagg_tmp_graph {
struct objagg_tmp_node *nodes;
unsigned long nodes_count;
unsigned long *edges;
};
static int objagg_tmp_graph_edge_index(struct objagg_tmp_graph *graph,
int parent_index, int index)
{
return index * graph->nodes_count + parent_index;
}
static void objagg_tmp_graph_edge_set(struct objagg_tmp_graph *graph,
int parent_index, int index)
{
int edge_index = objagg_tmp_graph_edge_index(graph, index,
parent_index);
__set_bit(edge_index, graph->edges);
}
static bool objagg_tmp_graph_is_edge(struct objagg_tmp_graph *graph,
int parent_index, int index)
{
int edge_index = objagg_tmp_graph_edge_index(graph, index,
parent_index);
return test_bit(edge_index, graph->edges);
}
static unsigned int objagg_tmp_graph_node_weight(struct objagg_tmp_graph *graph,
unsigned int index)
{
struct objagg_tmp_node *node = &graph->nodes[index];
unsigned int weight = node->objagg_obj->stats.user_count;
int j;
/* Node weight is sum of node users and all other nodes users
* that this node can represent with delta.
*/
if (node->crossed_out)
return 0;
for (j = 0; j < graph->nodes_count; j++) {
if (!objagg_tmp_graph_is_edge(graph, index, j))
continue;
node = &graph->nodes[j];
if (node->crossed_out)
continue;
weight += node->objagg_obj->stats.user_count;
}
return weight;
}
static int objagg_tmp_graph_node_max_weight(struct objagg_tmp_graph *graph)
{
unsigned int max_weight = 0;
unsigned int weight;
int max_index = -1;
int i;
for (i = 0; i < graph->nodes_count; i++) {
weight = objagg_tmp_graph_node_weight(graph, i);
if (weight > max_weight) {
max_weight = weight;
max_index = i;
}
}
return max_index;
}
static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg)
{
unsigned int nodes_count = objagg->obj_count;
struct objagg_tmp_graph *graph;
struct objagg_tmp_node *node;
struct objagg_tmp_node *pnode;
struct objagg_obj *objagg_obj;
size_t alloc_size;
int i, j;
graph = kzalloc(sizeof(*graph), GFP_KERNEL);
if (!graph)
return NULL;
graph->nodes = kcalloc(nodes_count, sizeof(*graph->nodes), GFP_KERNEL);
if (!graph->nodes)
goto err_nodes_alloc;
graph->nodes_count = nodes_count;
alloc_size = BITS_TO_LONGS(nodes_count * nodes_count) *
sizeof(unsigned long);
graph->edges = kzalloc(alloc_size, GFP_KERNEL);
if (!graph->edges)
goto err_edges_alloc;
i = 0;
list_for_each_entry(objagg_obj, &objagg->obj_list, list) {
node = &graph->nodes[i++];
node->objagg_obj = objagg_obj;
}
/* Assemble a temporary graph. Insert edge X->Y in case Y can be
* in delta of X.
*/
for (i = 0; i < nodes_count; i++) {
for (j = 0; j < nodes_count; j++) {
if (i == j)
continue;
pnode = &graph->nodes[i];
node = &graph->nodes[j];
if (objagg->ops->delta_check(objagg->priv,
pnode->objagg_obj->obj,
node->objagg_obj->obj)) {
objagg_tmp_graph_edge_set(graph, i, j);
}
}
}
return graph;
err_edges_alloc:
kfree(graph->nodes);
err_nodes_alloc:
kfree(graph);
return NULL;
}
static void objagg_tmp_graph_destroy(struct objagg_tmp_graph *graph)
{
kfree(graph->edges);
kfree(graph->nodes);
kfree(graph);
}
static int
objagg_opt_simple_greedy_fillup_hints(struct objagg_hints *objagg_hints,
struct objagg *objagg)
{
struct objagg_hints_node *hnode, *parent_hnode;
struct objagg_tmp_graph *graph;
struct objagg_tmp_node *node;
int index;
int j;
int err;
graph = objagg_tmp_graph_create(objagg);
if (!graph)
return -ENOMEM;
/* Find the nodes from the ones that can accommodate most users
* and cross them out of the graph. Save them to the hint list.
*/
while ((index = objagg_tmp_graph_node_max_weight(graph)) != -1) {
node = &graph->nodes[index];
node->crossed_out = true;
hnode = objagg_hints_node_create(objagg_hints,
node->objagg_obj,
objagg->ops->obj_size,
NULL);
if (IS_ERR(hnode)) {
err = PTR_ERR(hnode);
goto out;
}
parent_hnode = hnode;
for (j = 0; j < graph->nodes_count; j++) {
if (!objagg_tmp_graph_is_edge(graph, index, j))
continue;
node = &graph->nodes[j];
if (node->crossed_out)
continue;
node->crossed_out = true;
hnode = objagg_hints_node_create(objagg_hints,
node->objagg_obj,
objagg->ops->obj_size,
parent_hnode);
if (IS_ERR(hnode)) {
err = PTR_ERR(hnode);
goto out;
}
}
}
err = 0;
out:
objagg_tmp_graph_destroy(graph);
return err;
}
struct objagg_opt_algo {
int (*fillup_hints)(struct objagg_hints *objagg_hints,
struct objagg *objagg);
};
static const struct objagg_opt_algo objagg_opt_simple_greedy = {
.fillup_hints = objagg_opt_simple_greedy_fillup_hints,
};
static const struct objagg_opt_algo *objagg_opt_algos[] = {
[OBJAGG_OPT_ALGO_SIMPLE_GREEDY] = &objagg_opt_simple_greedy,
};
static int objagg_hints_obj_cmp(struct rhashtable_compare_arg *arg,
const void *obj)
{
struct rhashtable *ht = arg->ht;
struct objagg_hints *objagg_hints =
container_of(ht, struct objagg_hints, node_ht);
const struct objagg_ops *ops = objagg_hints->ops;
const char *ptr = obj;
ptr += ht->p.key_offset;
return ops->hints_obj_cmp ? ops->hints_obj_cmp(ptr, arg->key) :
memcmp(ptr, arg->key, ht->p.key_len);
}
/**
* objagg_hints_get - obtains hints instance
* @objagg: objagg instance
* @opt_algo_type: type of hints finding algorithm
*
* Note: all locking must be provided by the caller.
*
* According to the algo type, the existing objects of objagg instance
* are going to be went-through to assemble an optimal tree. We call this
* tree hints. These hints can be later on used for creation of
* a new objagg instance. There, the future object creations are going
* to be consulted with these hints in order to find out, where exactly
* the new object should be put as a root or delta.
*
* Returns a pointer to hints instance in case of success,
* otherwise it returns pointer error using ERR_PTR macro.
*/
struct objagg_hints *objagg_hints_get(struct objagg *objagg,
enum objagg_opt_algo_type opt_algo_type)
{
const struct objagg_opt_algo *algo = objagg_opt_algos[opt_algo_type];
struct objagg_hints *objagg_hints;
int err;
objagg_hints = kzalloc(sizeof(*objagg_hints), GFP_KERNEL);
if (!objagg_hints)
return ERR_PTR(-ENOMEM);
objagg_hints->ops = objagg->ops;
objagg_hints->refcount = 1;
INIT_LIST_HEAD(&objagg_hints->node_list);
objagg_hints->ht_params.key_len = objagg->ops->obj_size;
objagg_hints->ht_params.key_offset =
offsetof(struct objagg_hints_node, obj);
objagg_hints->ht_params.head_offset =
offsetof(struct objagg_hints_node, ht_node);
objagg_hints->ht_params.obj_cmpfn = objagg_hints_obj_cmp;
err = rhashtable_init(&objagg_hints->node_ht, &objagg_hints->ht_params);
if (err)
goto err_rhashtable_init;
err = algo->fillup_hints(objagg_hints, objagg);
if (err)
goto err_fillup_hints;
if (WARN_ON(objagg_hints->node_count != objagg->obj_count))
goto err_node_count_check;
return objagg_hints;
err_node_count_check:
err_fillup_hints:
objagg_hints_flush(objagg_hints);
rhashtable_destroy(&objagg_hints->node_ht);
err_rhashtable_init:
kfree(objagg_hints);
return ERR_PTR(err);
}
EXPORT_SYMBOL(objagg_hints_get);
/**
* objagg_hints_put - puts hints instance
* @objagg_hints: objagg hints instance
*
* Note: all locking must be provided by the caller.
*/
void objagg_hints_put(struct objagg_hints *objagg_hints)
{
if (--objagg_hints->refcount)
return;
objagg_hints_flush(objagg_hints);
rhashtable_destroy(&objagg_hints->node_ht);
kfree(objagg_hints);
}
EXPORT_SYMBOL(objagg_hints_put);
/**
* objagg_hints_stats_get - obtains stats of the hints instance
* @objagg_hints: hints instance
*
* Note: all locking must be provided by the caller.
*
* The returned structure contains statistics of all objects
* currently in use, ordered by following rules:
* 1) Root objects are always on lower indexes than the rest.
* 2) Objects with higher delta user count are always on lower
* indexes.
* 3) In case multiple objects have the same delta user count,
* the objects are ordered by user count.
*
* Returns a pointer to stats instance in case of success,
* otherwise it returns pointer error using ERR_PTR macro.
*/
const struct objagg_stats *
objagg_hints_stats_get(struct objagg_hints *objagg_hints)
{
struct objagg_stats *objagg_stats;
struct objagg_hints_node *hnode;
int i;
objagg_stats = kzalloc(struct_size(objagg_stats, stats_info,
objagg_hints->node_count),
GFP_KERNEL);
if (!objagg_stats)
return ERR_PTR(-ENOMEM);
i = 0;
list_for_each_entry(hnode, &objagg_hints->node_list, list) {
memcpy(&objagg_stats->stats_info[i], &hnode->stats_info,
sizeof(objagg_stats->stats_info[0]));
if (objagg_stats->stats_info[i].is_root)
objagg_stats->root_count++;
i++;
}
objagg_stats->stats_info_count = i;
sort(objagg_stats->stats_info, objagg_stats->stats_info_count,
sizeof(struct objagg_obj_stats_info),
objagg_stats_info_sort_cmp_func, NULL);
return objagg_stats;
}
EXPORT_SYMBOL(objagg_hints_stats_get);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
MODULE_DESCRIPTION("Object aggregation manager");

View file

@ -87,6 +87,15 @@ static void world_obj_put(struct world *world, struct objagg *objagg,
#define MAX_KEY_ID_DIFF 5
static bool delta_check(void *priv, const void *parent_obj, const void *obj)
{
const struct tokey *parent_key = parent_obj;
const struct tokey *key = obj;
int diff = key->id - parent_key->id;
return diff >= 0 && diff <= MAX_KEY_ID_DIFF;
}
static void *delta_create(void *priv, void *parent_obj, void *obj)
{
struct tokey *parent_key = parent_obj;
@ -95,7 +104,7 @@ static void *delta_create(void *priv, void *parent_obj, void *obj)
int diff = key->id - parent_key->id;
struct delta *delta;
if (diff < 0 || diff > MAX_KEY_ID_DIFF)
if (!delta_check(priv, parent_obj, obj))
return ERR_PTR(-EINVAL);
delta = kzalloc(sizeof(*delta), GFP_KERNEL);
@ -115,7 +124,7 @@ static void delta_destroy(void *priv, void *delta_priv)
kfree(delta);
}
static void *root_create(void *priv, void *obj)
static void *root_create(void *priv, void *obj, unsigned int id)
{
struct world *world = priv;
struct tokey *key = obj;
@ -268,6 +277,12 @@ static int check_stats_nodelta(struct objagg *objagg)
return err;
}
static bool delta_check_dummy(void *priv, const void *parent_obj,
const void *obj)
{
return false;
}
static void *delta_create_dummy(void *priv, void *parent_obj, void *obj)
{
return ERR_PTR(-EOPNOTSUPP);
@ -279,6 +294,7 @@ static void delta_destroy_dummy(void *priv, void *delta_priv)
static const struct objagg_ops nodelta_ops = {
.obj_size = sizeof(struct tokey),
.delta_check = delta_check_dummy,
.delta_create = delta_create_dummy,
.delta_destroy = delta_destroy_dummy,
.root_create = root_create,
@ -292,7 +308,7 @@ static int test_nodelta(void)
int i;
int err;
objagg = objagg_create(&nodelta_ops, &world);
objagg = objagg_create(&nodelta_ops, NULL, &world);
if (IS_ERR(objagg))
return PTR_ERR(objagg);
@ -357,6 +373,7 @@ static int test_nodelta(void)
static const struct objagg_ops delta_ops = {
.obj_size = sizeof(struct tokey),
.delta_check = delta_check,
.delta_create = delta_create,
.delta_destroy = delta_destroy,
.root_create = root_create,
@ -793,7 +810,7 @@ static int test_delta(void)
int i;
int err;
objagg = objagg_create(&delta_ops, &world);
objagg = objagg_create(&delta_ops, NULL, &world);
if (IS_ERR(objagg))
return PTR_ERR(objagg);
@ -815,6 +832,170 @@ static int test_delta(void)
return err;
}
struct hints_case {
const unsigned int *key_ids;
size_t key_ids_count;
struct expect_stats expect_stats;
struct expect_stats expect_stats_hints;
};
static const unsigned int hints_case_key_ids[] = {
1, 7, 3, 5, 3, 1, 30, 8, 8, 5, 6, 8,
};
static const struct hints_case hints_case = {
.key_ids = hints_case_key_ids,
.key_ids_count = ARRAY_SIZE(hints_case_key_ids),
.expect_stats =
EXPECT_STATS(7, ROOT(1, 2, 7), ROOT(7, 1, 4), ROOT(30, 1, 1),
DELTA(8, 3), DELTA(3, 2),
DELTA(5, 2), DELTA(6, 1)),
.expect_stats_hints =
EXPECT_STATS(7, ROOT(3, 2, 9), ROOT(1, 2, 2), ROOT(30, 1, 1),
DELTA(8, 3), DELTA(5, 2),
DELTA(6, 1), DELTA(7, 1)),
};
static void __pr_debug_stats(const struct objagg_stats *stats)
{
int i;
for (i = 0; i < stats->stats_info_count; i++)
pr_debug("Stat index %d key %u: u %d, d %d, %s\n", i,
obj_to_key_id(stats->stats_info[i].objagg_obj),
stats->stats_info[i].stats.user_count,
stats->stats_info[i].stats.delta_user_count,
stats->stats_info[i].is_root ? "root" : "noroot");
}
static void pr_debug_stats(struct objagg *objagg)
{
const struct objagg_stats *stats;
stats = objagg_stats_get(objagg);
if (IS_ERR(stats))
return;
__pr_debug_stats(stats);
objagg_stats_put(stats);
}
static void pr_debug_hints_stats(struct objagg_hints *objagg_hints)
{
const struct objagg_stats *stats;
stats = objagg_hints_stats_get(objagg_hints);
if (IS_ERR(stats))
return;
__pr_debug_stats(stats);
objagg_stats_put(stats);
}
static int check_expect_hints_stats(struct objagg_hints *objagg_hints,
const struct expect_stats *expect_stats,
const char **errmsg)
{
const struct objagg_stats *stats;
int err;
stats = objagg_hints_stats_get(objagg_hints);
if (IS_ERR(stats))
return PTR_ERR(stats);
err = __check_expect_stats(stats, expect_stats, errmsg);
objagg_stats_put(stats);
return err;
}
static int test_hints_case(const struct hints_case *hints_case)
{
struct objagg_obj *objagg_obj;
struct objagg_hints *hints;
struct world world2 = {};
struct world world = {};
struct objagg *objagg2;
struct objagg *objagg;
const char *errmsg;
int i;
int err;
objagg = objagg_create(&delta_ops, NULL, &world);
if (IS_ERR(objagg))
return PTR_ERR(objagg);
for (i = 0; i < hints_case->key_ids_count; i++) {
objagg_obj = world_obj_get(&world, objagg,
hints_case->key_ids[i]);
if (IS_ERR(objagg_obj)) {
err = PTR_ERR(objagg_obj);
goto err_world_obj_get;
}
}
pr_debug_stats(objagg);
err = check_expect_stats(objagg, &hints_case->expect_stats, &errmsg);
if (err) {
pr_err("Stats: %s\n", errmsg);
goto err_check_expect_stats;
}
hints = objagg_hints_get(objagg, OBJAGG_OPT_ALGO_SIMPLE_GREEDY);
if (IS_ERR(hints)) {
err = PTR_ERR(hints);
goto err_hints_get;
}
pr_debug_hints_stats(hints);
err = check_expect_hints_stats(hints, &hints_case->expect_stats_hints,
&errmsg);
if (err) {
pr_err("Hints stats: %s\n", errmsg);
goto err_check_expect_hints_stats;
}
objagg2 = objagg_create(&delta_ops, hints, &world2);
if (IS_ERR(objagg))
return PTR_ERR(objagg);
for (i = 0; i < hints_case->key_ids_count; i++) {
objagg_obj = world_obj_get(&world2, objagg2,
hints_case->key_ids[i]);
if (IS_ERR(objagg_obj)) {
err = PTR_ERR(objagg_obj);
goto err_world2_obj_get;
}
}
pr_debug_stats(objagg2);
err = check_expect_stats(objagg2, &hints_case->expect_stats_hints,
&errmsg);
if (err) {
pr_err("Stats2: %s\n", errmsg);
goto err_check_expect_stats2;
}
err = 0;
err_check_expect_stats2:
err_world2_obj_get:
for (i--; i >= 0; i--)
world_obj_put(&world2, objagg, hints_case->key_ids[i]);
objagg_hints_put(hints);
objagg_destroy(objagg2);
i = hints_case->key_ids_count;
err_check_expect_hints_stats:
err_hints_get:
err_check_expect_stats:
err_world_obj_get:
for (i--; i >= 0; i--)
world_obj_put(&world, objagg, hints_case->key_ids[i]);
objagg_destroy(objagg);
return err;
}
static int test_hints(void)
{
return test_hints_case(&hints_case);
}
static int __init test_objagg_init(void)
{
int err;
@ -822,7 +1003,10 @@ static int __init test_objagg_init(void)
err = test_nodelta();
if (err)
return err;
return test_delta();
err = test_delta();
if (err)
return err;
return test_hints();
}
static void __exit test_objagg_exit(void)

View file

@ -2858,6 +2858,7 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
u32 portid, u32 seq, int flags)
{
union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
const struct devlink_param *param = param_item->param;
struct devlink_param_gset_ctx ctx;
struct nlattr *param_values_list;
@ -2876,12 +2877,15 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
return -EOPNOTSUPP;
param_value[i] = param_item->driverinit_value;
} else {
if (!param_item->published)
continue;
ctx.cmode = i;
err = devlink_param_get(devlink, param, &ctx);
if (err)
return err;
param_value[i] = ctx.val;
}
param_value_set[i] = true;
}
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
@ -2916,7 +2920,7 @@ static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
goto param_nest_cancel;
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
if (!devlink_param_cmode_is_supported(param, i))
if (!param_value_set[i])
continue;
err = devlink_nl_param_value_fill_one(msg, param->type,
i, param_value[i]);
@ -5886,6 +5890,48 @@ void devlink_params_unregister(struct devlink *devlink,
}
EXPORT_SYMBOL_GPL(devlink_params_unregister);
/**
* devlink_params_publish - publish configuration parameters
*
* @devlink: devlink
*
* Publish previously registered configuration parameters.
*/
void devlink_params_publish(struct devlink *devlink)
{
struct devlink_param_item *param_item;
list_for_each_entry(param_item, &devlink->param_list, list) {
if (param_item->published)
continue;
param_item->published = true;
devlink_param_notify(devlink, 0, param_item,
DEVLINK_CMD_PARAM_NEW);
}
}
EXPORT_SYMBOL_GPL(devlink_params_publish);
/**
* devlink_params_unpublish - unpublish configuration parameters
*
* @devlink: devlink
*
* Unpublish previously registered configuration parameters.
*/
void devlink_params_unpublish(struct devlink *devlink)
{
struct devlink_param_item *param_item;
list_for_each_entry(param_item, &devlink->param_list, list) {
if (!param_item->published)
continue;
param_item->published = false;
devlink_param_notify(devlink, 0, param_item,
DEVLINK_CMD_PARAM_DEL);
}
}
EXPORT_SYMBOL_GPL(devlink_params_unpublish);
/**
* devlink_port_params_register - register port configuration parameters
*

View file

@ -9,11 +9,11 @@ lib_dir=$(dirname $0)/../../../../net/forwarding
ALL_TESTS="single_mask_test identical_filters_test two_masks_test \
multiple_masks_test ctcam_edge_cases_test delta_simple_test \
delta_two_masks_one_key_test bloom_simple_test \
bloom_complex_test bloom_delta_test"
delta_two_masks_one_key_test delta_simple_rehash_test \
bloom_simple_test bloom_complex_test bloom_delta_test"
NUM_NETIFS=2
source $lib_dir/tc_common.sh
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
tcflags="skip_hw"
@ -494,6 +494,77 @@ delta_two_masks_one_key_test()
log_test "delta two masks one key test ($tcflags)"
}
delta_simple_rehash_test()
{
RET=0
if [[ "$tcflags" != "skip_sw" ]]; then
return 0;
fi
devlink dev param set $DEVLINK_DEV \
name acl_region_rehash_interval cmode runtime value 0
check_err $? "Failed to set ACL region rehash interval"
tp_record_all mlxsw:mlxsw_sp_acl_tcam_vregion_rehash 7
tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_rehash
check_fail $? "Rehash trace was hit even when rehash should be disabled"
devlink dev param set $DEVLINK_DEV \
name acl_region_rehash_interval cmode runtime value 3000
check_err $? "Failed to set ACL region rehash interval"
sleep 1
tc filter add dev $h2 ingress protocol ip pref 1 handle 101 flower \
$tcflags dst_ip 192.0.1.0/25 action drop
tc filter add dev $h2 ingress protocol ip pref 2 handle 102 flower \
$tcflags dst_ip 192.0.2.2 action drop
tc filter add dev $h2 ingress protocol ip pref 3 handle 103 flower \
$tcflags dst_ip 192.0.3.0/24 action drop
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
-t ip -q
tc_check_packets "dev $h2 ingress" 101 1
check_fail $? "Matched a wrong filter"
tc_check_packets "dev $h2 ingress" 103 1
check_fail $? "Matched a wrong filter"
tc_check_packets "dev $h2 ingress" 102 1
check_err $? "Did not match on correct filter"
tp_record_all mlxsw:* 3
tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_rehash
check_err $? "Rehash trace was not hit"
tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_migrate
check_err $? "Migrate trace was not hit"
tp_record_all mlxsw:* 3
tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_rehash
check_err $? "Rehash trace was not hit"
tp_check_hits_any mlxsw:mlxsw_sp_acl_tcam_vregion_migrate
check_fail $? "Migrate trace was hit when no migration should happen"
$MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
-t ip -q
tc_check_packets "dev $h2 ingress" 101 1
check_fail $? "Matched a wrong filter after rehash"
tc_check_packets "dev $h2 ingress" 103 1
check_fail $? "Matched a wrong filter after rehash"
tc_check_packets "dev $h2 ingress" 102 2
check_err $? "Did not match on correct filter after rehash"
tc filter del dev $h2 ingress protocol ip pref 3 handle 103 flower
tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower
tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
log_test "delta simple rehash test ($tcflags)"
}
bloom_simple_test()
{
# Bloom filter requires that the eRP table is used. This test