All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Tariq Toukan" <tariqt@nvidia.com>
Cc: linux-pci@vger.kernel.org, linux-rdma@vger.kernel.org
Subject: Re: [PATCH net V2 4/4] net/mlx5: Lag, save and restore vport max_tx_speed across LAG
Date: Fri, 23 Oct 2026 19:32:05 +0000	[thread overview]
Message-ID: <sashiko-outbox-183123@kernel.org> (raw)
In-Reply-To: <20261022193053.3815866-5-tariqt@nvidia.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] net/mlx5: broken LAG state check in mlx5_esw_vport_save_max_tx_speed()
- [Medium] net/mlx5: unreset aggregate speed in mlx5_lag_reset_vports_speed()
- [Medium] net/mlx5: single-PF PCIe bandwidth clamp in mlx5_lag_get_restore_speed()
--

commit a16b0bd296f6ec7da26bed226c7219e07a503e4a
Author: Or Har-Toov <ohartoov@nvidia.com>

net/mlx5: Lag, save and restore vport max_tx_speed across LAG

This commit saves each vport's max_tx_speed when enabled so the original
rate limit can be restored on lag teardown even if the link is down. If the
rate was not saved, teardown falls back to a link-derived speed.

Link: https://lore.kernel.org/all/20260915015118.875210-1-kuba@kernel.org/

> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
> index fc197d1dc9dfe..7cf9c15ec65d2 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
> @@ -935,6 +935,27 @@ static void esw_vport_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport
>  	esw_vport_cleanup_acl(esw, vport);
>  }
>  
> +static void mlx5_esw_vport_save_max_tx_speed(struct mlx5_eswitch *esw,
> +					     struct mlx5_vport *vport)
> +{
> +	u16 op_mod = MLX5_VPORT_STATE_OP_MOD_ESW_VPORT;
> +	int ret;
> +
> +	if (!MLX5_CAP_ESW(esw->dev, esw_vport_state_max_tx_speed) ||
> +	    vport->vport == MLX5_VPORT_UPLINK || vport->max_tx_speed)
> +		return;
> +
> +	if (mlx5_lag_is_active(esw->dev) && !vport->agg_max_tx_speed)
> +		return;
> +
> +	ret = mlx5_query_vport_max_tx_speed(esw->dev, op_mod, vport->vport,
> +					    true, &vport->max_tx_speed, NULL);

[Severity: High]
Does the check on mlx5_lag_is_active(esw->dev) and !vport->agg_max_tx_speed
in mlx5_esw_vport_save_max_tx_speed() match how vport->agg_max_tx_speed is
maintained across the driver?

In drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c,
mlx5_lag_modify_device_vports_speed() sets vport->agg_max_tx_speed = speed
on every vport and leaves it non-zero after programming firmware:

mlx5_lag_modify_device_vports_speed() {
    ...
		vport->agg_max_tx_speed = speed;

		if (!vport->enabled)
			continue;

		ret = mlx5_modify_vport_max_tx_speed(mdev, op_mod,
						     vport->vport, true, speed);
    ...
}

Similarly, mlx5_esw_vport_set_max_tx_speed() in
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c writes
vport->agg_max_tx_speed to firmware on vport enable without clearing
vport->agg_max_tx_speed. As a result, while lag is active, vports whose
firmware has already been overwritten with the aggregate speed still have a
non-zero vport->agg_max_tx_speed.

Meanwhile, mlx5_lag_is_active() in
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c returns true when Socket
Direct shared fdb is active, even when no lag bond is active and
vport->agg_max_tx_speed is 0:

drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c:mlx5_lag_is_active() {
    ...
	res  = ldev && (__mlx5_lag_is_active(ldev) ||
			__mlx5_lag_is_sd_active(ldev, dev));
    ...
}

When switching a Socket Direct device to switchdev mode via
mlx5_sd_eswitch_mode_set() -> mlx5_sd_activate_shared_fdb() ->
mlx5_lag_shared_fdb_create() in
drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c,
pf->sd_fdb_active is set to true without calling
mlx5_lag_set_vports_agg_speed():

mlx5_lag_shared_fdb_create() {
    ...
		mlx5_lag_for_each(i, 0, ldev, filter) {
			pf = mlx5_lag_pf(ldev, i);
			pf->sd_fdb_active = true;
		}
    ...
	if (filter == MLX5_LAG_FILTER_PORTS || filter == MLX5_LAG_FILTER_ALL)
		mlx5_lag_set_vports_agg_speed(ldev);
}

The same state (mlx5_lag_is_active(esw->dev) returning true while
vport->agg_max_tx_speed is 0 and firmware still holds the pre-lag speed) also
occurs during lag activation in mlx5_lag_shared_fdb_create() when ldev->lock
is dropped inside mlx5_lag_reload_ib_reps_from_locked() before
mlx5_lag_set_vports_agg_speed() runs, if mlx5_lag_update_agg_speed() fails,
or when creating adjacent vports via mlx5_esw_adj_vport_create().

If a vport is enabled in any of those states (or if the initial
mlx5_query_vport_max_tx_speed() call fails), vport->max_tx_speed remains 0.
Once lag applies the aggregate speed to firmware (setting
vport->agg_max_tx_speed != 0) and that vport is later disabled and
re-enabled while lag is active, won't mlx5_esw_vport_save_max_tx_speed() see
vport->max_tx_speed == 0 and vport->agg_max_tx_speed != 0, query firmware,
and overwrite vport->max_tx_speed with the active lag aggregate rate?

[ ... ]

> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> index afe0b9909f2dc..3e623c96b2a1d 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> @@ -1712,11 +1712,37 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)
>  	}
>  }
>  
> +static int mlx5_lag_get_restore_speed(struct mlx5_core_dev *mdev,
> +				      struct mlx5_vport *vport, u32 *speed)
> +{
> +	u32 pci_bw;
> +
> +	if (vport->max_tx_speed) {
> +		*speed = vport->max_tx_speed;
> +		return 0;
> +	}
> +
> +	*speed = 0;
> +	mlx5_port_oper_linkspeed(mdev, speed);
> +	if (!*speed && mlx5_port_max_linkspeed(mdev, speed))
> +		return -ENODATA;
> +
> +	pci_bw = pcie_bandwidth_mbps(mdev->pdev);
> +	if (pci_bw)
> +		*speed = min(*speed, pci_bw);
> +	*speed /= MLX5_MAX_TX_SPEED_UNIT;
> +	return 0;
> +}

[Severity: Medium]
On Socket Direct devices where a single physical port spans multiple pcie
functions in the same group (port->group_id), calling
pcie_bandwidth_mbps(mdev->pdev) in mlx5_lag_get_restore_speed() only checks
a single function's pcie bandwidth.

Earlier in drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c,
mlx5_lag_port_pcie_bw() sums the bandwidth across all member functions in
the Socket Direct group:

drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c:mlx5_lag_port_pcie_bw() {
    ...
	if (!port->group_id)
		return mlx5_lag_dev_pcie_bw(port->dev, bw);

	*bw = 0;
	mlx5_lag_for_each(idx, 0, ldev, port->group_id) {
		member = mlx5_lag_pf(ldev, idx);
		if (!member)
			continue;

		err = mlx5_lag_dev_pcie_bw(member->dev, &member_bw);
    ...
}

Should mlx5_lag_get_restore_speed() use mlx5_lag_port_pcie_bw() instead of
pcie_bandwidth_mbps(mdev->pdev) so that Socket Direct ports are not clamped
to a single function's pcie bandwidth on fallback restore?

> +
>  void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev)
>  {
> +	u16 op_mod = MLX5_VPORT_STATE_OP_MOD_ESW_VPORT;
>  	struct mlx5_core_dev *mdev;
> +	struct mlx5_eswitch *esw;
> +	struct mlx5_vport *vport;
>  	struct lag_func *pf;
> -	u32 speed;
> +	u32 restore_speed;
> +	unsigned long i;
>  	int pf_idx;
>  	int ret;
>  
> @@ -1728,17 +1754,39 @@ void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev)
>  		mdev = pf->dev;
>  		if (!mdev)
>  			continue;
> -
> -		ret = mlx5_port_oper_linkspeed(mdev, &speed);
> -		if (ret) {
> -			mlx5_core_dbg(mdev,
> -				      "Failed to reset vports speed for device %s. Oper speed is not available (err=%d)\n",
> -				      dev_name(mdev->device), ret);
> +		esw = mdev->priv.eswitch;
> +		if (!esw || !MLX5_CAP_ESW(mdev, esw_vport_state_max_tx_speed))
>  			continue;
> -		}
>  
> -		speed = speed / MLX5_MAX_TX_SPEED_UNIT;
> -		mlx5_lag_modify_device_vports_speed(mdev, speed);
> +		mutex_lock(&esw->state_lock);
> +		mlx5_esw_for_each_vport(esw, i, vport) {
> +			if (!vport || vport->vport == MLX5_VPORT_UPLINK)
> +				continue;
> +
> +			if (!vport->enabled) {
> +				vport->agg_max_tx_speed = vport->max_tx_speed;
> +				continue;
> +			}
> +
> +			if (mlx5_lag_get_restore_speed(mdev, vport,
> +						       &restore_speed)) {
> +				vport->agg_max_tx_speed = 0;
> +				continue;
> +			}
> +
> +			ret = mlx5_modify_vport_max_tx_speed(mdev, op_mod,
> +							     vport->vport,
> +							     true,
> +							     restore_speed);
> +			if (ret) {
> +				mlx5_core_dbg(mdev,
> +					      "Failed to restore vport %d max tx speed, err=%d\n",
> +					      vport->vport, ret);
> +				continue;
> +			}
> +			vport->agg_max_tx_speed = 0;
> +		}
> +		mutex_unlock(&esw->state_lock);
>  	}
>  }
>  #endif

[Severity: Medium]
What happens in mlx5_lag_reset_vports_speed() if a vport was enabled while
lag was active (so firmware was programmed with the aggregate speed), its
baseline vport->max_tx_speed was not saved (remaining 0), and the vport is
then disabled before lag teardown?

When mlx5_lag_reset_vports_speed() runs, the !vport->enabled branch sets
vport->agg_max_tx_speed = vport->max_tx_speed (0) instead of computing a
fallback speed via mlx5_lag_get_restore_speed(). When that vport is later
re-enabled after lag teardown in mlx5_esw_vport_enable():

drivers/net/ethernet/mellanox/mlx5/core/eswitch.c:mlx5_esw_vport_enable() {
    ...
	mlx5_esw_vport_save_max_tx_speed(esw, vport);
	if (vport->agg_max_tx_speed)
		mlx5_esw_vport_set_max_tx_speed(esw, vport);
    ...
}

mlx5_esw_vport_save_max_tx_speed() sees vport->max_tx_speed == 0 and
!mlx5_lag_is_active(esw->dev), queries firmware (which still holds the
unrestored lag aggregate speed), and saves the aggregate speed into
vport->max_tx_speed, while skipping mlx5_esw_vport_set_max_tx_speed()
because vport->agg_max_tx_speed is 0.

Also, for an enabled vport, if mlx5_modify_vport_max_tx_speed() fails in
mlx5_lag_reset_vports_speed(), the error path continues before updating
vport->agg_max_tx_speed. Won't that leave the stale lag aggregate rate in
vport->agg_max_tx_speed, causing a subsequent disable and re-enable of the
vport outside of lag to replay the old lag aggregate speed into firmware?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261022193053.3815866-1-tariqt@nvidia.com?part=4

  reply	other threads:[~2026-10-23 19:32 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-22 19:30 [PATCH net V2 0/4] net/mlx5: fixes for LAG max_tx_speed update flow Tariq Toukan
2026-10-22 19:30 ` [PATCH net V2 1/4] {RDMA,net}/mlx5: cache and use TX-enabled aggregate speed for vports Tariq Toukan
2026-10-23 19:32   ` sashiko-bot
2026-10-28 17:57     ` Or Har-Toov
2026-10-26 20:32   ` netdev-bot+sashiko
2026-10-28 18:06     ` Or Har-Toov
2026-10-22 19:30 ` [PATCH net V2 2/4] net/mlx5: Lag, limit aggregated TX speed by PCIe bandwidth Tariq Toukan
2026-10-23 19:32   ` sashiko-bot
2026-10-23 20:06   ` Bjorn Helgaas
2026-10-28 18:08     ` Or Har-Toov
2026-10-26 20:32   ` netdev-bot+sashiko
2026-10-22 19:30 ` [PATCH net V2 3/4] {RDMA,net}/mlx5: notify IB uplink on LAG aggregate speed change Tariq Toukan
2026-10-23 19:32   ` sashiko-bot
2026-10-26 20:32   ` netdev-bot+sashiko
2026-10-22 19:30 ` [PATCH net V2 4/4] net/mlx5: Lag, save and restore vport max_tx_speed across LAG Tariq Toukan
2026-10-23 19:32   ` sashiko-bot [this message]
2026-10-26 20:32   ` netdev-bot+sashiko
2026-10-22 19:37 ` [PATCH net V2 0/4] net/mlx5: fixes for LAG max_tx_speed update flow netdev-bot+sinfo
2026-10-25 14:59   ` Or Har-Toov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-183123@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=tariqt@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.