Commit 04816dc9 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'mlxsw-various-acl-fixes'

Petr Machata says:

====================
mlxsw: Various ACL fixes

Ido Schimmel writes:

Fix various problems in the ACL (i.e., flower offload) code. See the
commit messages for more details.
====================

Link: https://lore.kernel.org/r/cover.1713797103.git.petrm@nvidia.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 5ea7b72d fb4e2b70
Loading
Loading
Loading
Loading
+72 −43
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/netdevice.h>
#include <linux/mutex.h>
#include <linux/refcount.h>
#include <linux/idr.h>
#include <net/devlink.h>
#include <trace/events/mlxsw.h>

@@ -58,41 +59,43 @@ int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_acl_tcam_region_id_get(struct mlxsw_sp_acl_tcam *tcam,
					   u16 *p_id)
{
	u16 id;
	int id;

	id = ida_alloc_max(&tcam->used_regions, tcam->max_regions - 1,
			   GFP_KERNEL);
	if (id < 0)
		return id;

	id = find_first_zero_bit(tcam->used_regions, tcam->max_regions);
	if (id < tcam->max_regions) {
		__set_bit(id, tcam->used_regions);
	*p_id = id;

	return 0;
}
	return -ENOBUFS;
}

static void mlxsw_sp_acl_tcam_region_id_put(struct mlxsw_sp_acl_tcam *tcam,
					    u16 id)
{
	__clear_bit(id, tcam->used_regions);
	ida_free(&tcam->used_regions, id);
}

static int mlxsw_sp_acl_tcam_group_id_get(struct mlxsw_sp_acl_tcam *tcam,
					  u16 *p_id)
{
	u16 id;
	int id;

	id = ida_alloc_max(&tcam->used_groups, tcam->max_groups - 1,
			   GFP_KERNEL);
	if (id < 0)
		return id;

	id = find_first_zero_bit(tcam->used_groups, tcam->max_groups);
	if (id < tcam->max_groups) {
		__set_bit(id, tcam->used_groups);
	*p_id = id;

	return 0;
}
	return -ENOBUFS;
}

static void mlxsw_sp_acl_tcam_group_id_put(struct mlxsw_sp_acl_tcam *tcam,
					   u16 id)
{
	__clear_bit(id, tcam->used_groups);
	ida_free(&tcam->used_groups, id);
}

struct mlxsw_sp_acl_tcam_pattern {
@@ -715,7 +718,9 @@ static void mlxsw_sp_acl_tcam_vregion_rehash_work(struct work_struct *work)
			     rehash.dw.work);
	int credits = MLXSW_SP_ACL_TCAM_VREGION_REHASH_CREDITS;

	mutex_lock(&vregion->lock);
	mlxsw_sp_acl_tcam_vregion_rehash(vregion->mlxsw_sp, vregion, &credits);
	mutex_unlock(&vregion->lock);
	if (credits < 0)
		/* Rehash gone out of credits so it was interrupted.
		 * Schedule the work as soon as possible to continue.
@@ -725,6 +730,17 @@ static void mlxsw_sp_acl_tcam_vregion_rehash_work(struct work_struct *work)
		mlxsw_sp_acl_tcam_vregion_rehash_work_schedule(vregion);
}

static void
mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(struct mlxsw_sp_acl_tcam_rehash_ctx *ctx)
{
	/* The entry markers are relative to the current chunk and therefore
	 * needs to be reset together with the chunk marker.
	 */
	ctx->current_vchunk = NULL;
	ctx->start_ventry = NULL;
	ctx->stop_ventry = NULL;
}

static void
mlxsw_sp_acl_tcam_rehash_ctx_vchunk_changed(struct mlxsw_sp_acl_tcam_vchunk *vchunk)
{
@@ -747,7 +763,7 @@ mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(struct mlxsw_sp_acl_tcam_vregion *v
	 * the current chunk pointer to make sure all chunks
	 * are properly migrated.
	 */
	vregion->rehash.ctx.current_vchunk = NULL;
	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(&vregion->rehash.ctx);
}

static struct mlxsw_sp_acl_tcam_vregion *
@@ -820,10 +836,14 @@ mlxsw_sp_acl_tcam_vregion_destroy(struct mlxsw_sp *mlxsw_sp,
	struct mlxsw_sp_acl_tcam *tcam = vregion->tcam;

	if (vgroup->vregion_rehash_enabled && ops->region_rehash_hints_get) {
		struct mlxsw_sp_acl_tcam_rehash_ctx *ctx = &vregion->rehash.ctx;

		mutex_lock(&tcam->lock);
		list_del(&vregion->tlist);
		mutex_unlock(&tcam->lock);
		cancel_delayed_work_sync(&vregion->rehash.dw);
		if (cancel_delayed_work_sync(&vregion->rehash.dw) &&
		    ctx->hints_priv)
			ops->region_rehash_hints_put(ctx->hints_priv);
	}
	mlxsw_sp_acl_tcam_vgroup_vregion_detach(mlxsw_sp, vregion);
	if (vregion->region2)
@@ -1154,8 +1174,14 @@ mlxsw_sp_acl_tcam_ventry_activity_get(struct mlxsw_sp *mlxsw_sp,
				      struct mlxsw_sp_acl_tcam_ventry *ventry,
				      bool *activity)
{
	return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp,
						    ventry->entry, activity);
	struct mlxsw_sp_acl_tcam_vregion *vregion = ventry->vchunk->vregion;
	int err;

	mutex_lock(&vregion->lock);
	err = mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, ventry->entry,
						   activity);
	mutex_unlock(&vregion->lock);
	return err;
}

static int
@@ -1189,6 +1215,8 @@ mlxsw_sp_acl_tcam_vchunk_migrate_start(struct mlxsw_sp *mlxsw_sp,
{
	struct mlxsw_sp_acl_tcam_chunk *new_chunk;

	WARN_ON(vchunk->chunk2);

	new_chunk = mlxsw_sp_acl_tcam_chunk_create(mlxsw_sp, vchunk, region);
	if (IS_ERR(new_chunk))
		return PTR_ERR(new_chunk);
@@ -1207,7 +1235,7 @@ mlxsw_sp_acl_tcam_vchunk_migrate_end(struct mlxsw_sp *mlxsw_sp,
{
	mlxsw_sp_acl_tcam_chunk_destroy(mlxsw_sp, vchunk->chunk2);
	vchunk->chunk2 = NULL;
	ctx->current_vchunk = NULL;
	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);
}

static int
@@ -1230,6 +1258,9 @@ mlxsw_sp_acl_tcam_vchunk_migrate_one(struct mlxsw_sp *mlxsw_sp,
		return 0;
	}

	if (list_empty(&vchunk->ventry_list))
		goto out;

	/* If the migration got interrupted, we have the ventry to start from
	 * stored in context.
	 */
@@ -1239,6 +1270,8 @@ mlxsw_sp_acl_tcam_vchunk_migrate_one(struct mlxsw_sp *mlxsw_sp,
		ventry = list_first_entry(&vchunk->ventry_list,
					  typeof(*ventry), list);

	WARN_ON(ventry->vchunk != vchunk);

	list_for_each_entry_from(ventry, &vchunk->ventry_list, list) {
		/* During rollback, once we reach the ventry that failed
		 * to migrate, we are done.
@@ -1279,6 +1312,7 @@ mlxsw_sp_acl_tcam_vchunk_migrate_one(struct mlxsw_sp *mlxsw_sp,
		}
	}

out:
	mlxsw_sp_acl_tcam_vchunk_migrate_end(mlxsw_sp, vchunk, ctx);
	return 0;
}
@@ -1292,6 +1326,9 @@ mlxsw_sp_acl_tcam_vchunk_migrate_all(struct mlxsw_sp *mlxsw_sp,
	struct mlxsw_sp_acl_tcam_vchunk *vchunk;
	int err;

	if (list_empty(&vregion->vchunk_list))
		return 0;

	/* If the migration got interrupted, we have the vchunk
	 * we are working on stored in context.
	 */
@@ -1320,16 +1357,17 @@ mlxsw_sp_acl_tcam_vregion_migrate(struct mlxsw_sp *mlxsw_sp,
	int err, err2;

	trace_mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion);
	mutex_lock(&vregion->lock);
	err = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion,
						   ctx, credits);
	if (err) {
		if (ctx->this_is_rollback)
			return err;
		/* In case migration was not successful, we need to swap
		 * so the original region pointer is assigned again
		 * to vregion->region.
		 */
		swap(vregion->region, vregion->region2);
		ctx->current_vchunk = NULL;
		mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);
		ctx->this_is_rollback = true;
		err2 = mlxsw_sp_acl_tcam_vchunk_migrate_all(mlxsw_sp, vregion,
							    ctx, credits);
@@ -1340,7 +1378,6 @@ mlxsw_sp_acl_tcam_vregion_migrate(struct mlxsw_sp *mlxsw_sp,
			/* Let the rollback to be continued later on. */
		}
	}
	mutex_unlock(&vregion->lock);
	trace_mlxsw_sp_acl_tcam_vregion_migrate_end(mlxsw_sp, vregion);
	return err;
}
@@ -1389,6 +1426,7 @@ mlxsw_sp_acl_tcam_vregion_rehash_start(struct mlxsw_sp *mlxsw_sp,

	ctx->hints_priv = hints_priv;
	ctx->this_is_rollback = false;
	mlxsw_sp_acl_tcam_rehash_ctx_vchunk_reset(ctx);

	return 0;

@@ -1441,7 +1479,8 @@ mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,
	err = mlxsw_sp_acl_tcam_vregion_migrate(mlxsw_sp, vregion,
						ctx, credits);
	if (err) {
		dev_err(mlxsw_sp->bus_info->dev, "Failed to migrate vregion\n");
		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to migrate vregion\n");
		return;
	}

	if (*credits >= 0)
@@ -1549,19 +1588,11 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
	if (max_tcam_regions < max_regions)
		max_regions = max_tcam_regions;

	tcam->used_regions = bitmap_zalloc(max_regions, GFP_KERNEL);
	if (!tcam->used_regions) {
		err = -ENOMEM;
		goto err_alloc_used_regions;
	}
	ida_init(&tcam->used_regions);
	tcam->max_regions = max_regions;

	max_groups = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_GROUPS);
	tcam->used_groups = bitmap_zalloc(max_groups, GFP_KERNEL);
	if (!tcam->used_groups) {
		err = -ENOMEM;
		goto err_alloc_used_groups;
	}
	ida_init(&tcam->used_groups);
	tcam->max_groups = max_groups;
	tcam->max_group_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
						  ACL_MAX_GROUP_SIZE);
@@ -1575,10 +1606,8 @@ int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
	return 0;

err_tcam_init:
	bitmap_free(tcam->used_groups);
err_alloc_used_groups:
	bitmap_free(tcam->used_regions);
err_alloc_used_regions:
	ida_destroy(&tcam->used_groups);
	ida_destroy(&tcam->used_regions);
	mlxsw_sp_acl_tcam_rehash_params_unregister(mlxsw_sp);
err_rehash_params_register:
	mutex_destroy(&tcam->lock);
@@ -1591,8 +1620,8 @@ void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
	const struct mlxsw_sp_acl_tcam_ops *ops = mlxsw_sp->acl_tcam_ops;

	ops->fini(mlxsw_sp, tcam->priv);
	bitmap_free(tcam->used_groups);
	bitmap_free(tcam->used_regions);
	ida_destroy(&tcam->used_groups);
	ida_destroy(&tcam->used_regions);
	mlxsw_sp_acl_tcam_rehash_params_unregister(mlxsw_sp);
	mutex_destroy(&tcam->lock);
}
+3 −2
Original line number Diff line number Diff line
@@ -6,15 +6,16 @@

#include <linux/list.h>
#include <linux/parman.h>
#include <linux/idr.h>

#include "reg.h"
#include "spectrum.h"
#include "core_acl_flex_keys.h"

struct mlxsw_sp_acl_tcam {
	unsigned long *used_regions; /* bit array */
	struct ida used_regions;
	unsigned int max_regions;
	unsigned long *used_groups;  /* bit array */
	struct ida used_groups;
	unsigned int max_groups;
	unsigned int max_group_size;
	struct mutex lock; /* guards vregion list */