Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
From: Or Har-Toov <ohartoov@nvidia.com>
To: netdev-bot+sashiko@kernel.org, tariqt@nvidia.com
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@kernel.org,
	kuba@kernel.org, netdev@vger.kernel.org, pabeni@redhat.com,
	bhelgaas@google.com, cjubran@nvidia.com, cratiu@nvidia.com,
	edwards@nvidia.com, gal@nvidia.com, jgg@ziepe.ca,
	kees@kernel.org, leon@kernel.org, linux-kernel@vger.kernel.org,
	linux-pci@vger.kernel.org, linux-rdma@vger.kernel.org,
	msanalla@nvidia.com, mbloch@nvidia.com, saeedm@nvidia.com,
	shayd@nvidia.com
Subject: Re: [PATCH net V2 4/4] net/mlx5: Lag, save and restore vport max_tx_speed across LAG
Date: Thu, 29 Oct 2026 21:18:46 +0200	[thread overview]
Message-ID: <28a237ce-f3db-4081-95bd-cef4b9bde369@nvidia.com> (raw)
In-Reply-To: <179304673356.3134.5707726348290129321@kernel.org>



On 26/10/2026 22:32, netdev-bot+sashiko@kernel.org wrote:
> External email: Use caution opening links or attachments
> 
> 
> Thank you for your contribution! Sashiko AI review found 7 potential
> issue(s) to consider.
> 
> Critical: 0 · High: 1 · Medium: 5 · Low: 1
> 
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
> 
> - [High] mlx5_esw_vport_save_max_tx_speed() (eswitch.c:948) treats a non-
>    zero agg_max_tx_speed during an active LAG as meaning the aggregate is…
> - [Medium] The new restore logic only runs on one LAG teardown path.
> - [Medium] vport->max_tx_speed is read from FW only once per mlx5_vport
>    object, because of the `|| vport->max_tx_speed) return;` early return…
> - [Medium] In mlx5_lag_reset_vports_speed(), when
>    mlx5_modify_vport_max_tx_speed() fails for an enabled vport, the loop…
> - [Medium] mlx5_lag_reset_vports_speed() records agg_max_tx_speed = 0 in
>    two cases where FW still holds the LAG aggregate.
> - [Medium] The save gate in mlx5_esw_vport_save_max_tx_speed() uses
>    mlx5_lag_is_active(), which reads ldev->mode under lag_lock.
> - [Low] The fallback in mlx5_lag_get_restore_speed() clamps with the raw
>    per-device pcie_bandwidth_mbps(mdev->pdev).
> 
> --
> 
> net/mlx5: Lag, save and restore vport max_tx_speed across LAG
> 
> This commit reads each vport's max_tx_speed from FW when the vport is
> enabled. At LAG teardown it writes that saved value back per vport,
> instead of writing the oper link speed. If nothing was saved, it falls
> back to a link-derived speed clamped by PCIe bandwidth, and disabled
> vports get their saved value as agg_max_tx_speed for the next enable.
> 
> 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;
> 
> [Severity: Medium]
> Once vport->max_tx_speed is non-zero, this returns early. Nothing in the
> patch ever sets it back to 0. Can the saved value go stale?
> 
> The mlx5_vport objects are allocated once, in mlx5_esw_vports_init()
> from mlx5_init_once(). They survive devlink reload, fw_activate, sync
> reset and health recovery, because mlx5_load_one_devl_locked() only
> runs mlx5_function_setup() and mlx5_load().
> 
> After such a reset, FW may recompute its per-vport default (the lower
> of port speed and PCIe bandwidth). If the PCIe link or port speed has
> changed, mlx5_lag_get_restore_speed() still uses the old saved value at
> the next teardown.
> 
> For disabled vports, the old value is copied into agg_max_tx_speed.
> Since that field is never cleared, mlx5_esw_vport_enable() writes it on
> every later enable, even when no LAG is configured.
> 
> Should max_tx_speed be reset on reload/reset, or read again from FW
> when no LAG is active?
> 
>> +
>> +     if (mlx5_lag_is_active(esw->dev) && !vport->agg_max_tx_speed)
>> +             return;
> 
> [Severity: High]
> This check reads a non-zero agg_max_tx_speed during an active LAG as
> "the aggregate is pending and FW still holds the original". Is that
> always the case?
> 
> mlx5_lag_modify_device_vports_speed() sets agg_max_tx_speed for every
> vport. For enabled vports it also writes the aggregate to FW right away:
> 
>      vport->agg_max_tx_speed = speed;
> 
>      if (!vport->enabled)
>          continue;
> 
>      ret = mlx5_modify_vport_max_tx_speed(mdev, op_mod, vport->vport, true, speed);
> 
> Nothing clears agg_max_tx_speed once it has been applied.
> mlx5_esw_vport_disable() doesn't touch agg_max_tx_speed or
> max_tx_speed either.
> 
> So take a vport with max_tx_speed == 0 while FW already holds the
> aggregate. That state is reachable in two ways:
> 
> - An earlier save query failed (only logged at debug level).
> - The vport was enabled while mlx5_lag_is_active() was true and
>    agg_max_tx_speed was 0, so the save was skipped. One example is an
>    SD-only shared FDB: __mlx5_lag_is_sd_active() makes
>    mlx5_lag_is_active() return true, but no aggregate is applied. A
>    later aggregate, for example from MPESW enable, then lands on the
>    enabled vport.
> 
> If that vport is disabled and re-enabled while the LAG is still active,
> this check passes. The query then stores the FW aggregate in
> vport->max_tx_speed.
> 
> At teardown, mlx5_lag_get_restore_speed() returns that value and the
> aggregate is written back. Because max_tx_speed is never reset, every
> later LAG cycle restores the same wrong baseline.
> 
> That seems to contradict the commit message:
> 
>      If the max_tx_speed of a vport could not be saved, it stays zero and
>      mlx5_lag_get_restore_speed() falls back to a link-derived speed at
>      teardown.
> 
> Is a separate marker needed here, one that can tell "aggregate pending"
> apart from "aggregate applied"?
> 
> [Severity: Medium]
> Does mlx5_lag_is_active() stay consistent with agg_max_tx_speed here?
> 
> mlx5_lag_is_active() reads ldev->mode under lag_lock. ldev->mode is
> written under ldev->lock only, in mlx5_activate_lag() and
> mlx5_deactivate_lag(). agg_max_tx_speed and the FW aggregate are
> written in a separate esw->state_lock section.
> 
> On activation, mlx5_do_bond()->mlx5_lag_shared_fdb_create() sets the
> mode and drops ldev->lock for rep rescans and reloads. Only after that
> does it call mlx5_lag_set_vports_agg_speed().
> 
> A vport can be enabled in that window. One way is mlx5_eswitch_enable()
> when the FDB already exists: toggle_lag is false, so
> mlx5_lag_disable_change() doesn't serialize it. SF/ECPF function
> changes are another way. Such a vport sees an active LAG with
> agg_max_tx_speed == 0. It skips the save even though FW still holds the
> original, and then receives the aggregate.
> 
> On teardown, mlx5_do_bond() does:
> 
>      mlx5_lag_reset_vports_speed(ldev);
>      mlx5_disable_lag(ldev);
> 
> The mode is still set after mlx5_lag_reset_vports_speed() drops
> state_lock. A vport enabled in that window also skips the save.
> 
> Either way, the vport ends up with max_tx_speed == 0 while FW holds the
> aggregate. Isn't that the precondition for the saved-baseline problem
> described above?
> 
>> +
>> +     ret = mlx5_query_vport_max_tx_speed(esw->dev, op_mod, vport->vport,
>> +                                         true, &vport->max_tx_speed, NULL);
>> +     if (ret)
>> +             mlx5_core_dbg(esw->dev,
>> +                           "Failed to save vport %d max tx speed, err=%d\n",
>> +                           vport->vport, ret);
>> +}
> 
> [ ... ]
> 
>> 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);
> 
> [Severity: Low]
> This might not be an issue, but the fallback clamps with the per-device
> pcie_bandwidth_mbps(mdev->pdev). The aggregate path uses
> mlx5_lag_port_pcie_bw() instead, which sums bandwidth across
> Socket-Direct group members and treats RC_END as unlimited.
> 
> The restore value is per vport, and the FW default is described as
> coming from the endpoint's own link status. So the per-device clamp may
> be intended. For RC_END, pcie_bandwidth_mbps() returns 0 and the
> if (pci_bw) check skips the clamp.
> 
> Could a short comment explain why the per-device value is used here
> rather than the SD-aware helper?
> 
>> +     *speed /= MLX5_MAX_TX_SPEED_UNIT;
>> +     return 0;
>> +}
> 
> [ ... ]
> 
>> @@ -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) {
> 
> [Severity: Medium]
> mlx5_lag_reset_vports_speed() is only called from the
> mlx5_lag_should_disable_lag() branch of mlx5_do_bond(). That predicate
> excludes MPESW:
> 
>      return !do_bond && __mlx5_lag_is_active(ldev) &&
>             ldev->mode != MLX5_LAG_MODE_MPESW;
> 
> Should the other teardown paths restore vport speeds as well?
> 
> mlx5_lag_disable_change() goes straight to mlx5_lag_disable_mpesw() or
> mlx5_disable_lag(). It is called from mlx5_eswitch_enable(),
> mlx5_eswitch_disable() and the devlink eswitch mode change.
> 
> Explicit MPESW disable goes through
> mlx5_mpesw_work()->mlx5_lag_disable_mpesw()->mlx5_lag_shared_fdb_destroy(),
> and doesn't restore either.
> 
> The aggregate is still applied when these LAGs come up, through
> mlx5_lag_shared_fdb_create()->mlx5_lag_set_vports_agg_speed() and
> mlx5_mpesw_speed_update_work().
> 
> After those teardowns, FW and agg_max_tx_speed both still hold the
> aggregate. mlx5_esw_vport_enable() writes it again on every later
> enable.
> 
> With the new save step, a vport whose max_tx_speed is still 0 then
> reads the leftover aggregate from FW and keeps it as its saved
> original. Every later mlx5_do_bond() teardown then restores the
> aggregate.
> 
> The missing restore calls on these paths were already there before
> this patch. But the commit message says the saved value is restored
> "at teardown", and the interaction with the new save step is new.

Will be fixed in a different patch that will be added to the V3 of this 
series (calling mlx5_lag_reset_vports_speed in MPESW teardown.

> 
>> +                     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;
>> +                     }
> 
> [Severity: Medium]
> Both of these branches set agg_max_tx_speed to 0 without writing FW.
> Could FW still hold the aggregate in either case?
> 
> For a disabled vport with max_tx_speed == 0, the pending restore is
> dropped. That vport may have been enabled partway through the LAG
> without a saved value (the mlx5_lag_is_active() check skipped the save,
> or the query failed). It then received the aggregate and was disabled
> before teardown.
> 
> The patch already assumes FW keeps max_tx_speed across a vport disable.
> That is why disabled vports get a replay value.
> 
> For an enabled vport, mlx5_lag_get_restore_speed() returns -ENODATA
> when nothing was saved and the PTYS query fails.
> 
> In both cases, the next mlx5_esw_vport_enable() after the LAG is gone
> sees max_tx_speed == 0 and an inactive LAG. It queries FW and saves the
> leftover aggregate as the original.
> 
> Before this patch, disabled vports got agg_max_tx_speed set to the oper
> speed, so re-enabling them corrected FW.
> 
> The commit message covers "a vport disabled for the entire LAG period".
> Does the same reasoning hold for a vport that was disabled partway
> through the LAG?
> 
>> +
>> +                     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;
>> +                     }
> 
> [Severity: Medium]
> If mlx5_modify_vport_max_tx_speed() fails here, agg_max_tx_speed keeps
> the LAG aggregate. Is that intended?
> 
> Before, mlx5_lag_modify_device_vports_speed() set agg_max_tx_speed to
> the teardown speed before trying the FW write. A later enable would
> then retry with a non-LAG value.
> 
> Now, on a later enable with no LAG active, mlx5_esw_vport_enable()
> runs:
> 
>      if (vport->agg_max_tx_speed)
>          mlx5_esw_vport_set_max_tx_speed(esw, vport);
> 
> which writes the stale aggregate to FW again. If max_tx_speed is still
> 0, the save step runs first and records the aggregate still in FW as
> the baseline.
> 
> That looks like the state the commit message says is fixed:
> 
>      leaving the aggregate in FW and in agg_max_tx_speed
> 
> Would setting vport->agg_max_tx_speed = restore_speed in this failure
> branch let the next enable retry the restore?
> 
>> +                     vport->agg_max_tx_speed = 0;
>> +             }
>> +             mutex_unlock(&esw->state_lock);
> 
> [ ... ]
> 
> --
> Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261022193053.3815866-1-tariqt%40nvidia.com


  reply	other threads:[~2026-10-29 19:18 UTC|newest]

Thread overview: 20+ 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
2026-10-26 20:32   ` netdev-bot+sashiko
2026-10-29 19:18     ` Or Har-Toov [this message]
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=28a237ce-f3db-4081-95bd-cef4b9bde369@nvidia.com \
    --to=ohartoov@nvidia.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=bhelgaas@google.com \
    --cc=cjubran@nvidia.com \
    --cc=cratiu@nvidia.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=edwards@nvidia.com \
    --cc=gal@nvidia.com \
    --cc=jgg@ziepe.ca \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=leon@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=mbloch@nvidia.com \
    --cc=msanalla@nvidia.com \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=saeedm@nvidia.com \
    --cc=shayd@nvidia.com \
    --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