[PATCH net V3 8/8] net/mlx5e: Serialize TC and IPsec offload exclusion counters

From: Tariq Toukan

Date: Thu Nov 05 2026 - 16:25:19 EST


From: Cosmin Ratiu <cratiu@xxxxxxxxxx>

The counters enforcing TC and IPsec packet offload mutual exclusion are
not consistently serialized. IPsec admission conditionally takes
mode_lock for writing, while TC admission holds it only for read.
Neither release path takes the lock, and devices without an eswitch
cannot rely on it at all.

These are per-core-device counters, separate from the eswitch mode and
encapsulation reservations protected by mode_lock. Move them into
mdev->offload_block and protect their checks, increments and decrements
with a dedicated spinlock that is available even without an eswitch.
A spinlock is used because some of these functions are called from
atomic context and is a good fit because the critical sections are and
will stay really short.

IPsec admission then no longer needs mode_lock to update these counters.
Add warnings for unbalanced releases. Keep mlx5_esw_lock() for eswitch
operation admission and temporary mode/encapsulation reservations.

This was found by upstream Sashiko during review of [1], and spun-off
into a fix from the larger patch.

[1]
https://lore.kernel.org/netdev/178907726586.219967.4695695146458742597@xxxxxxxxxx/

Fixes: c8e350e62fc5 ("net/mlx5e: Make TC and IPsec offloads mutually exclusive on a netdev")
Signed-off-by: Cosmin Ratiu <cratiu@xxxxxxxxxx>
Reviewed-by: Jianbo Liu <jianbol@xxxxxxxxxx>
Reviewed-by: Dragos Tatulea <dtatulea@xxxxxxxxxx>
Signed-off-by: Tariq Toukan <tariqt@xxxxxxxxxx>
---
.../mellanox/mlx5/core/en_accel/ipsec_fs.c | 43 ++++++-------------
.../net/ethernet/mellanox/mlx5/core/en_tc.c | 18 +++++---
.../net/ethernet/mellanox/mlx5/core/main.c | 1 +
include/linux/mlx5/driver.h | 8 +++-
4 files changed, 31 insertions(+), 39 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
index dbe5547d7a49..55de9abbd72b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
@@ -2585,45 +2585,26 @@ void mlx5e_accel_ipsec_fs_read_stats(struct mlx5e_priv *priv, void *ipsec_stats)
}
}

-#ifdef CONFIG_MLX5_ESWITCH
static int mlx5e_ipsec_block_tc_offload(struct mlx5_core_dev *mdev)
{
- struct mlx5_eswitch *esw = mdev->priv.eswitch;
- int err = 0;
-
- if (esw) {
- err = mlx5_esw_lock(esw);
- if (err)
- return err;
- }
-
- if (mdev->num_block_ipsec) {
- err = -EBUSY;
- goto unlock;
- }
+ int ret = 0;

- mdev->num_block_tc++;
-
-unlock:
- if (esw)
- mlx5_esw_unlock(esw);
-
- return err;
-}
-#else
-static int mlx5e_ipsec_block_tc_offload(struct mlx5_core_dev *mdev)
-{
- if (mdev->num_block_ipsec)
- return -EBUSY;
+ spin_lock_bh(&mdev->offload_block.lock);
+ if (mdev->offload_block.num_block_ipsec)
+ ret = -EBUSY;
+ else
+ mdev->offload_block.num_block_tc++;
+ spin_unlock_bh(&mdev->offload_block.lock);

- mdev->num_block_tc++;
- return 0;
+ return ret;
}
-#endif

static void mlx5e_ipsec_unblock_tc_offload(struct mlx5_core_dev *mdev)
{
- mdev->num_block_tc--;
+ spin_lock_bh(&mdev->offload_block.lock);
+ if (!WARN_ON_ONCE(!mdev->offload_block.num_block_tc))
+ mdev->offload_block.num_block_tc--;
+ spin_unlock_bh(&mdev->offload_block.lock);
}

int mlx5e_accel_ipsec_fs_add_rule(struct mlx5e_ipsec_sa_entry *sa_entry)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 708b7987ea4c..9f97c9d680bb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -4836,16 +4836,19 @@ static bool is_tc_ipsec_order_check_needed(struct net_device *filter, struct mlx
static int mlx5e_tc_block_ipsec_offload(struct net_device *filter, struct mlx5e_priv *priv)
{
struct mlx5_core_dev *mdev = priv->mdev;
+ int ret = 0;

if (!is_tc_ipsec_order_check_needed(filter, priv))
return 0;

- if (mdev->num_block_tc)
- return -EBUSY;
-
- mdev->num_block_ipsec++;
+ spin_lock_bh(&mdev->offload_block.lock);
+ if (mdev->offload_block.num_block_tc)
+ ret = -EBUSY;
+ else
+ mdev->offload_block.num_block_ipsec++;
+ spin_unlock_bh(&mdev->offload_block.lock);

- return 0;
+ return ret;
}

static void mlx5e_tc_unblock_ipsec_offload(struct net_device *filter, struct mlx5e_priv *priv)
@@ -4853,7 +4856,10 @@ static void mlx5e_tc_unblock_ipsec_offload(struct net_device *filter, struct mlx
if (!is_tc_ipsec_order_check_needed(filter, priv))
return;

- priv->mdev->num_block_ipsec--;
+ spin_lock_bh(&priv->mdev->offload_block.lock);
+ if (!WARN_ON_ONCE(!priv->mdev->offload_block.num_block_ipsec))
+ priv->mdev->offload_block.num_block_ipsec--;
+ spin_unlock_bh(&priv->mdev->offload_block.lock);
}

int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 1a45474ce03d..0439fcfb9ce5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1814,6 +1814,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
lockdep_register_key(&dev->lock_key);
mutex_init(&dev->intf_state_mutex);
lockdep_set_class(&dev->intf_state_mutex, &dev->lock_key);
+ spin_lock_init(&dev->offload_block.lock);
mutex_init(&dev->mlx5e_res.uplink_netdev_lock);
mutex_init(&dev->wc_state_lock);

diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index abb99371ff13..84e9f6bdb7c1 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -789,8 +789,12 @@ struct mlx5_core_dev {
u32 vsc_addr;
struct mlx5_hv_vhca *hv_vhca;
struct mlx5_hwmon *hwmon;
- u64 num_block_tc;
- u64 num_block_ipsec;
+ struct {
+ /* Protects the fields below. */
+ spinlock_t lock;
+ u64 num_block_tc;
+ u64 num_block_ipsec;
+ } offload_block;
#ifdef CONFIG_MLX5_MACSEC
struct mlx5_macsec_fs *macsec_fs;
/* MACsec notifier chain to sync MACsec core and IB database */
--
2.44.0