Linux PCI subsystem development
 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: 16+ 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-26 20:32   ` netdev-bot+sashiko
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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox