net/mlx5: E-Switch, use state lock for vport state changes

Protect vport admin state modifications and vport iteration with the
eswitch state_lock mutex to ensure proper serialization of concurrent
vport state changes.

Currently, calls to mlx5_modify_vport_admin_state() and loops iterating
over eswitch vports can race with each other, potentially leading to
inconsistent vport state. Fix this by acquiring esw->state_lock

Fixes: 7d0314b11c ("net/mlx5e: Modify uplink state on interface up/down")
Signed-off-by: Mark Bloch <mbloch@nvidia.com>
Reviewed-by: Shay Drori <shayd@nvidia.com>
Reviewed-by: Or Har-Toov <ohartoov@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260816065015.3280733-2-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Mark Bloch 2026-08-16 09:50:13 +03:00 committed by Jakub Kicinski
parent c5ae83ee02
commit ff0f9b7aa1
5 changed files with 31 additions and 5 deletions

View File

@ -689,11 +689,13 @@ static int mlx5e_rep_open(struct net_device *dev)
if (err)
goto unlock;
mutex_lock(&rep->esw->state_lock);
if (!mlx5_modify_vport_admin_state(priv->mdev,
MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
rep->vport, 1,
MLX5_VPORT_ADMIN_STATE_UP))
netif_carrier_on(dev);
mutex_unlock(&rep->esw->state_lock);
unlock:
mutex_unlock(&priv->state_lock);
@ -708,10 +710,12 @@ static int mlx5e_rep_close(struct net_device *dev)
int ret;
mutex_lock(&priv->state_lock);
mutex_lock(&rep->esw->state_lock);
mlx5_modify_vport_admin_state(priv->mdev,
MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
rep->vport, 1,
MLX5_VPORT_ADMIN_STATE_DOWN);
mutex_unlock(&rep->esw->state_lock);
ret = mlx5e_close_locked(dev);
mutex_unlock(&priv->state_lock);
return ret;
@ -783,22 +787,25 @@ static int mlx5e_rep_change_carrier(struct net_device *dev, bool new_carrier)
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5e_rep_priv *rpriv = priv->ppriv;
struct mlx5_eswitch_rep *rep = rpriv->rep;
int err;
int err = 0;
mutex_lock(&rep->esw->state_lock);
if (new_carrier) {
err = mlx5_modify_vport_admin_state(priv->mdev, MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
rep->vport, 1, MLX5_VPORT_ADMIN_STATE_UP);
if (err)
return err;
goto unlock;
netif_carrier_on(dev);
} else {
err = mlx5_modify_vport_admin_state(priv->mdev, MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
rep->vport, 1, MLX5_VPORT_ADMIN_STATE_DOWN);
if (err)
return err;
goto unlock;
netif_carrier_off(dev);
}
return 0;
unlock:
mutex_unlock(&rep->esw->state_lock);
return err;
}
static const struct net_device_ops mlx5e_netdev_ops_rep = {
@ -1339,9 +1346,12 @@ static int mlx5e_uplink_rep_enable(struct mlx5e_priv *priv)
mlx5e_rep_tc_enable(priv);
if (MLX5_CAP_GEN(mdev, uplink_follow))
if (MLX5_CAP_GEN(mdev, uplink_follow)) {
mutex_lock(&mdev->priv.eswitch->state_lock);
mlx5_modify_vport_admin_state(mdev, MLX5_VPORT_STATE_OP_MOD_UPLINK,
0, 0, MLX5_VPORT_ADMIN_STATE_AUTO);
mutex_unlock(&mdev->priv.eswitch->state_lock);
}
mlx5_lag_add_netdev(mdev, netdev);
priv->events_nb.notifier_call = uplink_rep_async_event;
mlx5_notifier_register(mdev, &priv->events_nb);

View File

@ -9,6 +9,8 @@ int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev, u16 vport,
{
u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)] = {};
lockdep_assert_held(&dev->priv.eswitch->state_lock);
MLX5_SET(modify_vport_state_in, in, opcode,
MLX5_CMD_OP_MODIFY_VPORT_STATE);
MLX5_SET(modify_vport_state_in, in, op_mod,

View File

@ -2567,6 +2567,7 @@ static void mlx5_esw_fdb_active(struct mlx5_eswitch *esw)
mlx5_esw_fdb_drop_destroy(esw);
mlx5_mpfs_enable(esw->dev);
mutex_lock(&esw->state_lock);
mlx5_esw_for_each_vf_vport(esw, i, vport, U16_MAX) {
if (!vport->adjacent)
continue;
@ -2574,6 +2575,7 @@ static void mlx5_esw_fdb_active(struct mlx5_eswitch *esw)
vport->vport);
mlx5_esw_adj_vport_modify(esw->dev, vport->vport, true);
}
mutex_unlock(&esw->state_lock);
esw->offloads_inactive = false;
esw_warn(esw->dev, "MPFS/FDB active\n");
@ -2587,6 +2589,7 @@ static void mlx5_esw_fdb_inactive(struct mlx5_eswitch *esw)
mlx5_mpfs_disable(esw->dev);
mlx5_esw_fdb_drop_create(esw);
mutex_lock(&esw->state_lock);
mlx5_esw_for_each_vf_vport(esw, i, vport, U16_MAX) {
if (!vport->adjacent)
continue;
@ -2595,6 +2598,7 @@ static void mlx5_esw_fdb_inactive(struct mlx5_eswitch *esw)
mlx5_esw_adj_vport_modify(esw->dev, vport->vport, false);
}
mutex_unlock(&esw->state_lock);
esw->offloads_inactive = true;
esw_warn(esw->dev, "MPFS/FDB inactive\n");

View File

@ -1471,6 +1471,7 @@ static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev,
if (!MLX5_CAP_ESW(mdev, esw_vport_state_max_tx_speed))
return;
mutex_lock(&esw->state_lock);
mlx5_esw_for_each_vport(esw, i, vport) {
if (!vport)
continue;
@ -1490,6 +1491,7 @@ static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev,
"Failed to set vport %d speed %d, err=%d\n",
vport->vport, speed, ret);
}
mutex_unlock(&esw->state_lock);
}
void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)

View File

@ -89,6 +89,10 @@ int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
{
u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)] = {};
#ifdef CONFIG_MLX5_ESWITCH
lockdep_assert_held(&mdev->priv.eswitch->state_lock);
#endif
MLX5_SET(modify_vport_state_in, in, opcode,
MLX5_CMD_OP_MODIFY_VPORT_STATE);
MLX5_SET(modify_vport_state_in, in, op_mod, opmod);
@ -106,6 +110,10 @@ int mlx5_modify_vport_max_tx_speed(struct mlx5_core_dev *mdev, u8 opmod,
u8 admin_state;
int err;
#ifdef CONFIG_MLX5_ESWITCH
lockdep_assert_held(&mdev->priv.eswitch->state_lock);
#endif
err = mlx5_query_vport_admin_state(mdev, opmod, vport, other_vport,
&admin_state);
if (err)