From: Jakub Kicinski <kuba@kernel.org>
To: tariqt@nvidia.com
Cc: Jakub Kicinski <kuba@kernel.org>,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
netdev@vger.kernel.org, pabeni@redhat.com, edwards@nvidia.com,
gal@nvidia.com, jgg@ziepe.ca, leon@kernel.org,
linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org,
msanalla@nvidia.com, mbloch@nvidia.com, ohartoov@nvidia.com,
saeedm@nvidia.com, shayd@nvidia.com
Subject: Re: [PATCH net 4/5] net/mlx5: Lag, limit aggregated TX speed by PCIe bandwidth
Date: Mon, 14 Sep 2026 18:51:25 -0700 [thread overview]
Message-ID: <20260915015125.875497-1-kuba@kernel.org> (raw)
In-Reply-To: <20260910102432.3845360-5-tariqt@nvidia.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net/mlx5: Lag, limit aggregated TX speed by PCIe bandwidth
This adds a new helper mlx5_pcie_bandwidth() that derives a PCIe bandwidth
from PCI_EXP_LNKSTA, and uses it to clamp each uplink's contribution to the
aggregated LAG TX speed in mlx5_lag_get_devices_oper_speed(),
mlx5_lag_get_devices_max_speed() and mlx5_lag_reset_vports_speed(). A few
questions below about how the bandwidth is computed and where the clamp is
applied.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
> index df2e3ad01819f..81f3dc3b8034f 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
[ ... ]
> @@ -40,6 +41,25 @@
>
> static DEFINE_IDA(mlx5_adev_ida);
>
> +#define MLX5_PCIE_MIN_SPEED_MBPS (2500)
> +
> +u32 mlx5_pcie_bandwidth(struct mlx5_core_dev *dev)
> +{
> + u16 lnksta;
> + int speed;
> + u32 width;
> +
> + if (pcie_capability_read_word(dev->pdev, PCI_EXP_LNKSTA, &lnksta))
> + return 0;
> +
> + width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
> + speed = pcie_link_speed_mbps(dev->pdev);
> + if (speed < MLX5_PCIE_MIN_SPEED_MBPS)
> + return 0;
> +
> + return speed * width;
> +}
[Severity: Medium]
Is speed * width the same quantity the PCI core calls PCIe bandwidth?
pcie_link_speed_mbps() returns the raw per-lane transfer rate, while
pcie_bandwidth_available() in drivers/pci/pci.c applies the line encoding
correction and walks upstream taking the minimum:
drivers/pci/pci.h:
#define PCIE_SPEED2MBS_ENC(speed) \
((speed) == PCIE_SPEED_64_0GT ? 64000*1/1 : \
(speed) == PCIE_SPEED_32_0GT ? 32000*128/130 : \
...
(speed) == PCIE_SPEED_5_0GT ? 5000*8/10 : \
(speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \
drivers/pci/pci.c:pcie_bandwidth_available() {
...
next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
/* Check if current device limits the total bandwidth */
if (!bw || next_bw <= bw) {
...
dev = pci_upstream_bridge(dev);
}
For a Gen2 x8 link the helper here yields 5000 * 8 = 40000 Mb/s, whereas the
core helper yields 4000 * 8 = 32000 Mb/s. Does that make
min(speed, pci_bw) a no-op for a 40GbE uplink on a bus that cannot sustain
it? On Gen3 and later the difference is about 1.5%, which is still larger
than the MLX5_MAX_TX_SPEED_UNIT (100 Mb/s) granularity the value is
programmed at.
The same driver already computes this quantity for the same pdev with the
core helper:
drivers/net/ethernet/mellanox/mlx5/core/en/params.c:slow_pci_heuristic() {
...
pci_bw = pcie_bandwidth_available(mdev->pdev, NULL, NULL, NULL);
mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", link_speed,
pci_bw);
...
}
Should mlx5_pcie_bandwidth() use pcie_bandwidth_available() as well, so the
driver does not carry two different definitions of PCI bandwidth for one
device?
[Severity: Low]
Can the width and speed here come from two different link states? The width
is taken from the local snapshot in lnksta, but the speed comes from a second
independent read of the very same register:
drivers/pci/pci.c:pcie_link_speed_mbps() {
err = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
if (err)
return err;
return pcie_dev_speed_mbps(to_pcie_link_speed(lnksta));
}
PCI_EXP_LNKSTA_NLW and PCI_EXP_LNKSTA_CLS both live in the word already held
in lnksta, and the callers hold only ldev->lock, which does not serialise
link retraining, autonomous width/speed changes, bandwidth control speed
changes or a function reset during health recovery. If the link changes
between the two reads, speed * width is a bandwidth the link never had.
Would deriving the speed from the existing snapshot with
pcie_dev_speed_mbps(to_pcie_link_speed(lnksta)) be both cheaper and
consistent?
> +
> static bool is_eth_rep_supported(struct mlx5_core_dev *dev)
> {
> if (!IS_ENABLED(CONFIG_MLX5_ESWITCH))
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> index 76b574fa0d7a6..fd91becd6848f 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
[ ... ]
> @@ -1452,6 +1453,9 @@ static int mlx5_lag_get_devices_oper_speed(struct mlx5_lag *ldev,
> return ret;
> }
>
> + pci_bw = mlx5_pcie_bandwidth(pf_mdev);
> + if (pci_bw)
> + speed = min(speed, pci_bw);
> *sum_speed += speed;
> }
[Severity: Medium]
Does clamping per uplink and then summing actually bound the aggregate for
the two PFs of a dual-port adapter? Both PFs sit on the same physical PCIe
link and read the same PCI_EXP_LNKSTA, so each contributes up to that single
link's bandwidth.
For a dual-port 200GbE adapter on Gen4 x16, mlx5_pcie_bandwidth() returns
16000 * 16 = 256000 for both PFs, each contributes min(200000, 256000) =
200000, and the aggregate becomes 400000 Mb/s, roughly 1.6x the shared link.
That aggregate is stored in ldev->agg_speed_mbps and then programmed into
every vport:
mlx5_lag_set_vports_agg_speed() {
...
speed = speed / MLX5_MAX_TX_SPEED_UNIT;
mlx5_ldev_for_each(pf_idx, 0, ldev) {
...
mlx5_lag_modify_device_vports_speed(mdev, speed);
}
Should the PCIe bandwidth instead be applied to the summed value, i.e. after
aggregation, so PFs sharing one link are not counted twice? Only the
take_max (active-backup) path stays bounded today.
>
> @@ -1488,6 +1493,9 @@ static int mlx5_lag_get_devices_max_speed(struct mlx5_lag *ldev, u32 *max_speed)
> return ret;
> }
>
> + pci_bw = mlx5_pcie_bandwidth(pf_mdev);
> + if (pci_bw)
> + speed = min(speed, pci_bw);
> *max_speed = take_max ?
> max(*max_speed, speed) : *max_speed + speed;
> }
[Severity: Medium]
The same double counting applies in this fallback when take_max is false,
which is the case for LACP and XOR and is forced for MPESW.
[Severity: Low]
Unrelated to the clamp itself, but the caller's comment no longer matches
this helper:
mlx5_lag_update_agg_speed() {
...
/* If speed is not set, use the sum of max speeds of all PFs */
if (!speed && mlx5_lag_get_devices_max_speed(ldev, &speed))
return;
}
With take_max = ldev->tracker.tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,
the fallback returns max(*max_speed, speed) rather than a sum in
active-backup mode. The comment was accurate in the baseline, where
mlx5_lag_sum_devices_max_speed() unconditionally did *sum_speed += speed;
should it be updated to describe the policy dependent behaviour?
>
> @@ -1609,6 +1618,9 @@ void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev)
> continue;
> }
>
> + pci_bw = mlx5_pcie_bandwidth(mdev);
> + if (pci_bw)
> + speed = min(speed, pci_bw);
> speed = speed / MLX5_MAX_TX_SPEED_UNIT;
> mlx5_lag_modify_device_vports_speed(mdev, speed);
> }
[Severity: Medium]
This is the value restored into vport max_tx_speed on LAG teardown, so if
mlx5_pcie_bandwidth() over-estimates the bus bandwidth (raw rate, no
encoding correction, endpoint link only), the restored value is the
over-estimated one rather than what FW originally programmed. Does that
still match the commit message claim that FW initializes max_tx_speed to the
minimum of the port speed and the PCI bandwidth?
next prev parent reply other threads:[~2026-09-15 1:51 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 10:24 [PATCH net 0/5] net/mlx5: fixes for LAG max_tx_speed update flow Tariq Toukan
2026-09-10 10:24 ` [PATCH net 1/5] net/mlx5: Lag, split aggregate speed into oper and max helpers Tariq Toukan
2026-09-15 1:51 ` Jakub Kicinski
2026-09-10 10:24 ` [PATCH net 2/5] net/mlx5: Lag, reset vport speed on teardown Tariq Toukan
2026-09-15 1:51 ` Jakub Kicinski
2026-09-10 10:24 ` [PATCH net 3/5] {RDMA,net}/mlx5: cache and use TX-enabled aggregate speed for vports Tariq Toukan
2026-09-15 1:51 ` Jakub Kicinski
2026-09-10 10:24 ` [PATCH net 4/5] net/mlx5: Lag, limit aggregated TX speed by PCIe bandwidth Tariq Toukan
2026-09-15 1:51 ` Jakub Kicinski [this message]
2026-09-10 10:24 ` [PATCH net 5/5] {RDMA,net}/mlx5: notify RoCE LAG speed change via driver event Tariq Toukan
2026-09-15 1:51 ` Jakub Kicinski
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=20260915015125.875497-1-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=edwards@nvidia.com \
--cc=gal@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=mbloch@nvidia.com \
--cc=msanalla@nvidia.com \
--cc=netdev@vger.kernel.org \
--cc=ohartoov@nvidia.com \
--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
all inboxes | Powered by JetHome®