Commit 99b36850 authored by Jianbo Liu's avatar Jianbo Liu Committed by Jakub Kicinski
Browse files

net/mlx5e: Prevent concurrent access to IPSec ASO context



The query or updating IPSec offload object is through Access ASO WQE.
The driver uses a single mlx5e_ipsec_aso struct for each PF, which
contains a shared DMA-mapped context for all ASO operations.

A race condition exists because the ASO spinlock is released before
the hardware has finished processing WQE. If a second operation is
initiated immediately after, it overwrites the shared context in the
DMA area.

When the first operation's completion is processed later, it reads
this corrupted context, leading to unexpected behavior and incorrect
results.

This commit fixes the race by introducing a private context within
each IPSec offload object. The shared ASO context is now copied to
this private context while the ASO spinlock is held. Subsequent
processing uses this saved, per-object context, ensuring its integrity
is maintained.

Fixes: 1ed78fc0 ("net/mlx5e: Update IPsec soft and hard limits")
Signed-off-by: default avatarJianbo Liu <jianbol@nvidia.com>
Reviewed-by: default avatarLeon Romanovsky <leonro@nvidia.com>
Signed-off-by: default avatarTariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/20260316094603.6999-3-tariqt@nvidia.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent b7e3a5d9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -287,6 +287,7 @@ struct mlx5e_ipsec_sa_entry {
	struct mlx5e_ipsec_dwork *dwork;
	struct mlx5e_ipsec_limits limits;
	u32 rx_mapped_id;
	u8 ctx[MLX5_ST_SZ_BYTES(ipsec_aso)];
};

struct mlx5_accel_pol_xfrm_attrs {
+8 −9
Original line number Diff line number Diff line
@@ -370,20 +370,18 @@ static void mlx5e_ipsec_aso_update_soft(struct mlx5e_ipsec_sa_entry *sa_entry,
static void mlx5e_ipsec_handle_limits(struct mlx5e_ipsec_sa_entry *sa_entry)
{
	struct mlx5_accel_esp_xfrm_attrs *attrs = &sa_entry->attrs;
	struct mlx5e_ipsec *ipsec = sa_entry->ipsec;
	struct mlx5e_ipsec_aso *aso = ipsec->aso;
	bool soft_arm, hard_arm;
	u64 hard_cnt;

	lockdep_assert_held(&sa_entry->x->lock);

	soft_arm = !MLX5_GET(ipsec_aso, aso->ctx, soft_lft_arm);
	hard_arm = !MLX5_GET(ipsec_aso, aso->ctx, hard_lft_arm);
	soft_arm = !MLX5_GET(ipsec_aso, sa_entry->ctx, soft_lft_arm);
	hard_arm = !MLX5_GET(ipsec_aso, sa_entry->ctx, hard_lft_arm);
	if (!soft_arm && !hard_arm)
		/* It is not lifetime event */
		return;

	hard_cnt = MLX5_GET(ipsec_aso, aso->ctx, remove_flow_pkt_cnt);
	hard_cnt = MLX5_GET(ipsec_aso, sa_entry->ctx, remove_flow_pkt_cnt);
	if (!hard_cnt || hard_arm) {
		/* It is possible to see packet counter equal to zero without
		 * hard limit event armed. Such situation can be if packet
@@ -454,10 +452,8 @@ static void mlx5e_ipsec_handle_event(struct work_struct *_work)
		container_of(_work, struct mlx5e_ipsec_work, work);
	struct mlx5e_ipsec_sa_entry *sa_entry = work->data;
	struct mlx5_accel_esp_xfrm_attrs *attrs;
	struct mlx5e_ipsec_aso *aso;
	int ret;

	aso = sa_entry->ipsec->aso;
	attrs = &sa_entry->attrs;

	spin_lock_bh(&sa_entry->x->lock);
@@ -466,8 +462,9 @@ static void mlx5e_ipsec_handle_event(struct work_struct *_work)
		goto unlock;

	if (attrs->replay_esn.trigger &&
	    !MLX5_GET(ipsec_aso, aso->ctx, esn_event_arm)) {
		u32 mode_param = MLX5_GET(ipsec_aso, aso->ctx, mode_parameter);
	    !MLX5_GET(ipsec_aso, sa_entry->ctx, esn_event_arm)) {
		u32 mode_param = MLX5_GET(ipsec_aso, sa_entry->ctx,
					  mode_parameter);

		mlx5e_ipsec_update_esn_state(sa_entry, mode_param);
	}
@@ -629,6 +626,8 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry,
			/* We are in atomic context */
			udelay(10);
	} while (ret && time_is_after_jiffies(expires));
	if (!ret)
		memcpy(sa_entry->ctx, aso->ctx, MLX5_ST_SZ_BYTES(ipsec_aso));
	spin_unlock_bh(&aso->lock);
	return ret;
}