* [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch
@ 2026-09-23 10:38 Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address Tariq Toukan
` (12 more replies)
0 siblings, 13 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
Hi,
This series by Shay has two intertwined goals. The first is to remove
the compile-time MLX5_MAX_PORTS sizing from the LAG and eswitch/TC
datapaths so that structures scale with the actual number of ports/peers
rather than a fixed maximum. The second is a set of small,
self-contained fixes that prepare the eswitch and LAG code for a VF/SF
acting as a nested e-switch manager - a mode new FW will allow, where a
VF/SF has its own eswitch and the VFs/SFs can be grouped by MPESW.
No functional change is intended for existing configurations.
General fixes, patches 1-2
- assign a random MAC to any netdev with a zero MAC address, dropping
the vport-group-manager exception that left a manager with an
invalid all-zero address.
- do not leave an unpaired devcom component registered when the
initial PAIR event fails.
LAG - drop MLX5_MAX_PORTS-sized storage, patches 3-6
- allocate v2p_map dynamically: the fixed MLX5_MAX_PORTS *
MLX5_LAG_MAX_HASH_BUCKETS array embedded in struct mlx5_lag becomes
a pointer sized by ldev->ports.
- allocate the port-indexed scratch buffers in print_mapping() and
infer_tx_affinity_mapping() with kcalloc(ldev->ports, ...).
- drop the per-port scratch arrays in drop-rule setup by walking the
tracker state inline.
- size the mapping debugfs buffer by the actual port count.
E-switch/TC peer flows - lift the MLX5_MAX_PORTS peer cap, patches 7-8
- anchor the peer-flow reverse index on the duplicated flow (a single
node) instead of a per-peer array on the origin flow.
- replace the fixed peer_flows[] array with an xarray keyed by peer
vhca_id, so the number of peers is bounded only by distinct vhca_ids
rather than MLX5_MAX_PORTS.
Nested e-switch manager / LAG-of-VFs preparation, patches 9-13
- derive the e-switch manager vport from esw_manager_vport_number
instead of hardcoding ECPF/host-PF, so the manager vport can be a
VF.
- don't print the LAG virtual-to-physical port mapping to debugfs in
MPESW mode, where the LAG does not own that mapping.
- drop the stale esw_shared_ingress_acl gate from the shared-FDB
check.
- reword the mlx5_esw_allowed() comments to describe the actual
condition (not an e-switch manager), since a nested-eswitch VF
passes that check.
- disable host functions for a non-PF e-switch manager.
Regards,
Tariq
---
Answers to internal sashiko comments:
commit cd4c9a171200573172eac1ad28feb11543a64f1c
Author: Shay Drory <shayd@nvidia.com>
net/mlx5: E-switch, do not leave an unpaired devcom registered
If the initial PAIR event fails, the eswitch offloads devcom component
stays registered until eswitch teardown even though no peer was ever
paired. devcom is optional, so this is not a functional bug - but a
registered component that reflects no actual pairing is incorrect state
to keep around. Release it when the initial pairing fails.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
> index 907ee83a722d..955ca6e9c7cf 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
[ ... ]
> @@ -3568,10 +3569,12 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
> if (!esw->devcom)
> return;
>
> - mlx5_devcom_send_event(esw->devcom,
> - ESW_OFFLOADS_DEVCOM_PAIR,
> - ESW_OFFLOADS_DEVCOM_UNPAIR,
> - esw);
> + err = mlx5_devcom_send_event(esw->devcom,
> + ESW_OFFLOADS_DEVCOM_PAIR,
> + ESW_OFFLOADS_DEVCOM_UNPAIR,
> + esw);
> + if (err)
> + mlx5_esw_offloads_devcom_cleanup(esw);
Does this code introduce a potential NULL pointer dereference in
mlx5e_tc_query_route_vport()?
If the initial pairing fails and we call mlx5_esw_offloads_devcom_cleanup(),
esw->devcom is set to NULL while the device continues to operate. Later, when
TC queries the route vport, mlx5e_tc_query_route_vport() calls:
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c:mlx5e_tc_query_route_vport() {
...
mlx5_devcom_for_each_peer_entry_rcu(esw->devcom, esw, pos) {
...
}
This macro unconditionally passes esw->devcom to
mlx5_devcom_get_next_peer_data_rcu(), which dereferences devcom->comp:
drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c:mlx5_devcom_get_next_peer_data_rcu() {
struct mlx5_devcom_comp *comp = devcom->comp;
...
}
Could this cause a panic when processing traffic on the active path?
[SD] no, this mlx5_devcom_for_each_peer_entry_rcu() is protected by lag_is_active. and LAG can be active only if devcom is alive.
commit d891405a6936cb189f66ab2b70a5f8cfc042235e
Author: Shay Drory <shayd@nvidia.com>
net/mlx5: LAG, allocate v2p_map dynamically
This commit converts the fixed-size v2p_map array in struct mlx5_lag to a
dynamically allocated pointer based on the actual port count. It also makes
a similar conversion for the scratch buffer in mlx5_modify_lag() to remove
the dependency on MLX5_MAX_PORTS.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> index 28d16fdc3f06..3af484fd924b 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> @@ -328,11 +329,16 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
> if (!ldev)
> return NULL;
>
> + ldev->ports = MLX5_CAP_GEN(dev, num_lag_ports);
> + ldev->buckets = 1;
> + ldev->v2p_map = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
> + sizeof(*ldev->v2p_map), GFP_KERNEL);
Could this allocation be too small when Socket Direct (SD) is enabled?
When iterating with mlx5_ldev_for_each(), the index i comes from the sparse
xarray ldev->pfs. With SD enabled, primary and secondary devices are
interleaved, which means i can exceed ldev->ports - 1.
For example, on a 4-port device, the fourth primary device might get an
index of 6. Would sizing this buffer strictly to ldev->ports result in
out-of-bounds accesses during iteration?
[SD] LAG of devices that support SD don't use v2p_map, so the failure above cannot happens.
commit d9d5074e46326236672c6cf3d7fea9d8d6680a60
Author: Shay Drory <shayd@nvidia.com>
net/mlx5: LAG, allocate port-indexed scratch buffers dynamically
The commit dynamically allocates per-port scratch buffers in
mlx5_lag_print_mapping() and mlx5_infer_tx_affinity_mapping() using kcalloc()
instead of relying on fixed-size stack arrays bounded by MLX5_MAX_PORTS.
This removes the dependency on MLX5_MAX_PORTS for these buffers.
> @@ -699,10 +712,18 @@ static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
> }
> }
>
> + mlx5_ldev_for_each(i, 0, ldev) {
> + if (tracker->netdev_state[i].tx_enabled &&
> + tracker->netdev_state[i].link_up)
> + enabled[enabled_ports_num++] = i;
> + else
> + disabled[disabled_ports_num++] = i;
> + }
> +
Can this write out-of-bounds of the newly allocated arrays?
[SD] no, LAG of devices that support SD can only be MPESW, so this code is not reachable.
net/mlx5: LAG, drop per-port scratch arrays in active-port and drop-rule setup
lag_active_port_bits() and mlx5_lag_drop_rule_setup() each only walk the
per-port tracker state, but did so through a per-port scratch array:
lag_active_port_bits() collected the enabled ports to fold into a bitmask,
and mlx5_lag_drop_rule_setup() collected the disabled ports to create a
per-port drop rule.
Walk the tracker state inline in both - the enabled ports
(tx_enabled && link_up), or the disabled ones - so neither needs a scratch
array, removing their MLX5_MAX_PORTS dependency.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> index de515fe3b57a..806c09b2d080 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
[ ... ]
While reviewing this patch series' transition from xarrays to static arrays,
I noticed a regression in the related TC code.
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c:mlx5e_tc_add_fdb_peer_flow() {
...
int i = mlx5_lag_get_dev_seq(peer_esw->dev);
...
mutex_lock(&esw->offloads.peer_mutex);
list_add_tail(&flow->peer[i], &esw->offloads.peer_flows[i]);
set_bit(i, flow->peer_used);
mutex_unlock(&esw->offloads.peer_mutex);
...
}
If mlx5_lag_get_dev_seq() returns -ENOENT (-2), i is used directly as an
array index. This causes out-of-bounds access during list and bit operations
like list_add_tail() and set_bit().
This can occur when adding a TC rule via mlx5e_tc_add_fdb_flow(), which loops
over devcom peer entries for ESW_OFFLOADS and calls this function. A device
can be paired in ESW_OFFLOADS while not being part of a LAG, or before the LAG
master is elected, causing mlx5_lag_get_dev_seq() to return -ENOENT.
Does this code overflow flow->peer[] and esw->offloads.peer_flows[] under
these conditions?
[SD] this code is run under is_peer_flow_needed, which is true only if LAG is active.
Hence, i always returns valid value.
net/mlx5: LAG, size debugfs buffers by port count
This commit modifies the debugfs mapping_show() function to dynamically
allocate the ports array based on the actual number of ports, rather than
using the constant MLX5_MAX_PORTS.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> index e9b8d79d2d21..44296789d656 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> @@ -101,14 +101,18 @@ static int mapping_show(struct seq_file *file, void *priv)
> static int mapping_show(struct seq_file *file, void *priv)
> {
> struct mlx5_core_dev *dev = file->private;
> - u8 ports[MLX5_MAX_PORTS] = {};
> struct mlx5_lag *ldev;
> bool hash = false;
> bool lag_active;
> int i, idx = 0;
> int num_ports;
> + u8 *ports;
>
> ldev = mlx5_lag_dev(dev);
> + ports = kcalloc(ldev->ports, sizeof(*ports), GFP_KERNEL);
> + if (!ports)
> + return -ENOMEM;
> +
Does this code overflow the newly allocated ports array?
In this function, the array is populated either by mlx5_infer_tx_enabled()
or a direct loop:
mlx5_ldev_for_each(i, 0, ldev)
ports[idx++] = ldev->v2p_map[i];
Since mlx5_ldev_for_each() iterates over an xarray whose capacity might
allow more elements than ldev->ports, idx could exceed the allocated size.
The same applies to mlx5_infer_tx_enabled(), which writes to
ports[(*num_enabled)++] = i. If the number of active primary devices exceeds
ldev->ports, won't this write past the end of the buffer?
[SD] number of active primary devices can never exceeds ldev->ports
net/mlx5e: TC, track peer flows in a vhca_id xarray
The per-peer reverse index lived in a fixed esw->offloads.peer_flows[]
array of list heads indexed by the peer's LAG sequence number, capping a
shared FDB at MLX5_MAX_PORTS members.
commit 5fe510c74051ae4d7a1240882d23a3bbd0d738a5
Author: Shay Drory <shayd@nvidia.com>
net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode
This commit treats MPESW mode as if LAG is inactive. It stops printing
the LAG virtual-to-physical port mapping to debugfs since port selection
is driven by user steering rules in this mode.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> index 44296789d656..dd93abc90c14 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> @@ -109,12 +109,14 @@ static int mapping_show(struct seq_file *file, void *priv)
> u8 *ports;
>
> ldev = mlx5_lag_dev(dev);
> +
> ports = kcalloc(ldev->ports, sizeof(*ports), GFP_KERNEL);
This isn't a bug introduced by this patch, but while reviewing the LAG code
related to ldev->ports, I noticed a potential NULL pointer dereference in
mlx5_lag_query_cong_counters() in
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c.
If a LAG device is removed (e.g., dynamically unbinding a PF from the bond
interface), mlx5_lag_remove_mdev() removes the device from the LAG's
internal xarray asynchronously to the actual LAG state deactivation.
During this window, __mlx5_lag_is_active() still returns true, but
mlx5_ldev_for_each() populates fewer than ldev->ports devices into the
mdev array:
[SD] when mlx5_lag_remove_mdev() is called, the LAG is already destroied.
so the race don't exists.
Shay Drory (13):
net/mlx5e: Assign a random MAC to any netdev with a zero MAC address
net/mlx5: E-switch, do not leave an unpaired devcom registered
net/mlx5: LAG, allocate v2p_map dynamically
net/mlx5: LAG, allocate port-indexed scratch buffers dynamically
net/mlx5: LAG, drop per-port scratch array in drop-rule setup
net/mlx5: LAG, size debugfs buffers by port count
net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow
net/mlx5e: TC, track peer flows in a vhca_id xarray
net/mlx5: E-switch, derive manager vport from device capability
net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode
net/mlx5: LAG, drop stale esw_shared_ingress_acl gate from shared FDB
net/mlx5: E-switch, correct stale VF/PF wording in esw-allowed
comments
net/mlx5: E-switch, disable host functions for a non PF e-switch
manager
.../ethernet/mellanox/mlx5/core/en/tc_priv.h | 14 +-
.../net/ethernet/mellanox/mlx5/core/en_main.c | 3 +-
.../net/ethernet/mellanox/mlx5/core/en_tc.c | 75 ++++-----
.../net/ethernet/mellanox/mlx5/core/eswitch.c | 20 ++-
.../net/ethernet/mellanox/mlx5/core/eswitch.h | 2 +-
.../mellanox/mlx5/core/eswitch_offloads.c | 37 ++++-
.../ethernet/mellanox/mlx5/core/lag/debugfs.c | 14 +-
.../net/ethernet/mellanox/mlx5/core/lag/lag.c | 154 +++++++++++++-----
.../net/ethernet/mellanox/mlx5/core/lag/lag.h | 2 +-
.../mellanox/mlx5/core/lag/shared_fdb.c | 3 +-
include/linux/mlx5/eswitch.h | 3 +
11 files changed, 215 insertions(+), 112 deletions(-)
base-commit: 944ae66642b726bd6b25ae71b1e9ff88a0e0bdb0
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 02/13] net/mlx5: E-switch, do not leave an unpaired devcom registered Tariq Toukan
` (11 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
mlx5e_set_netdev_dev_addr() falls back to a random MAC only when the
queried address is all-zeros AND the device is not a vport group
manager. A vport group manager that comes up with a zero MAC is
therefore left with an invalid all-zero address.
The vport_group_manager exception is unnecessary: a zero MAC is always
invalid, regardless of device type. Drop the condition and assign a
random MAC whenever the queried address is zero, so every netdev comes
up with a valid address.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index fc110a7d16e8..b0765f07cc3a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -5430,8 +5430,7 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
u8 addr[ETH_ALEN];
mlx5_query_mac_address(priv->mdev, addr);
- if (is_zero_ether_addr(addr) &&
- !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) {
+ if (is_zero_ether_addr(addr)) {
eth_hw_addr_random(netdev);
mlx5_core_info(priv->mdev, "Assigned random MAC address %pM\n", netdev->dev_addr);
return;
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 02/13] net/mlx5: E-switch, do not leave an unpaired devcom registered
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically Tariq Toukan
` (10 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
If the initial PAIR event fails, the eswitch offloads devcom component
stays registered until eswitch teardown even though no peer was ever
paired. devcom is optional, so this is not a functional bug - but a
registered component that reflects no actual pairing is incorrect state
to keep around.
Release it when the initial pairing fails.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index eb74b6260168..e7d92d9bde16 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -3548,6 +3548,7 @@ static int mlx5_esw_offloads_devcom_event(int event,
void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
const struct mlx5_devcom_match_attr *attr)
{
+ int err;
int i;
for (i = 0; i < MLX5_MAX_PORTS; i++)
@@ -3572,10 +3573,12 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
if (!esw->devcom)
return;
- mlx5_devcom_send_event(esw->devcom,
- ESW_OFFLOADS_DEVCOM_PAIR,
- ESW_OFFLOADS_DEVCOM_UNPAIR,
- esw);
+ err = mlx5_devcom_send_event(esw->devcom,
+ ESW_OFFLOADS_DEVCOM_PAIR,
+ ESW_OFFLOADS_DEVCOM_UNPAIR,
+ esw);
+ if (err)
+ mlx5_esw_offloads_devcom_cleanup(esw);
}
void mlx5_esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw)
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 02/13] net/mlx5: E-switch, do not leave an unpaired devcom registered Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically Tariq Toukan
` (9 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
v2p_map was a fixed array sized MLX5_MAX_PORTS *
MLX5_LAG_MAX_HASH_BUCKETS embedded in struct mlx5_lag. Turn it into a
pointer allocated in mlx5_lag_dev_alloc() once ldev->ports is known,
sized by the actual port count, and free it in mlx5_ldev_free(). The
ldev->ports / ldev->buckets initialization is moved up so the allocation
can use it.
The matching scratch buffer in mlx5_modify_lag() is converted from a
MLX5_MAX_PORTS-sized stack array to a kcalloc() of the same extent, with
the trailing teardown reworked through a goto so the buffer is freed on
every path. The memcpy() that previously relied on sizeof() of the stack
array now uses the explicit ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS
span.
No functional change; this removes v2p_map's dependency on
MLX5_MAX_PORTS.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/lag/lag.c | 38 ++++++++++++++-----
.../net/ethernet/mellanox/mlx5/core/lag/lag.h | 2 +-
2 files changed, 29 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index c655f6e32e9b..00b0159cb422 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -308,6 +308,7 @@ static void mlx5_ldev_free(struct kref *ref)
cancel_work_sync(&ldev->speed_update_work);
destroy_workqueue(ldev->wq);
mutex_destroy(&ldev->lock);
+ kfree(ldev->v2p_map);
kfree(ldev);
}
@@ -330,11 +331,16 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
if (!ldev)
return NULL;
+ ldev->ports = MLX5_CAP_GEN(dev, num_lag_ports);
+ ldev->buckets = 1;
+ ldev->v2p_map = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
+ sizeof(*ldev->v2p_map), GFP_KERNEL);
+ if (!ldev->v2p_map)
+ goto err_v2p_map;
+
ldev->wq = create_singlethread_workqueue("mlx5_lag");
- if (!ldev->wq) {
- kfree(ldev);
- return NULL;
- }
+ if (!ldev->wq)
+ goto err_wq;
kref_init(&ldev->ref);
mutex_init(&ldev->lock);
@@ -358,10 +364,13 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
mlx5_core_err(dev, "Failed to init multipath lag err=%d\n",
err);
- ldev->ports = MLX5_CAP_GEN(dev, num_lag_ports);
- ldev->buckets = 1;
-
return ldev;
+
+err_wq:
+ kfree(ldev->v2p_map);
+err_v2p_map:
+ kfree(ldev);
+ return NULL;
}
int mlx5_lag_dev_get_netdev_idx(struct mlx5_lag *ldev,
@@ -853,8 +862,8 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
struct lag_tracker *tracker)
{
int first_idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);
- u8 ports[MLX5_MAX_PORTS * MLX5_LAG_MAX_HASH_BUCKETS] = {};
struct mlx5_core_dev *dev0;
+ u8 *ports;
int idx;
int err;
int i;
@@ -864,6 +873,12 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
return;
dev0 = mlx5_lag_pf(ldev, first_idx)->dev;
+
+ ports = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
+ sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return;
+
mlx5_infer_tx_affinity_mapping(tracker, ldev, ldev->buckets, ports);
mlx5_ldev_for_each(i, 0, ldev) {
@@ -876,9 +891,10 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
mlx5_core_err(dev0,
"Failed to modify LAG (%d)\n",
err);
- return;
+ goto out;
}
- memcpy(ldev->v2p_map, ports, sizeof(ports));
+ memcpy(ldev->v2p_map, ports,
+ ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS);
mlx5_lag_print_mapping(dev0, ldev, tracker,
ldev->mode_flags);
@@ -899,6 +915,8 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
ndev);
dev_put(ndev);
}
+out:
+ kfree(ports);
}
static int mlx5_lag_set_port_sel_mode(struct mlx5_lag *ldev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
index e9f0ef83ce1d..8552792b9035 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
@@ -85,7 +85,7 @@ struct mlx5_lag {
u8 ports;
u8 buckets;
int mode_changes_in_progress;
- u8 v2p_map[MLX5_MAX_PORTS * MLX5_LAG_MAX_HASH_BUCKETS];
+ u8 *v2p_map;
struct kref ref;
struct xarray pfs;
struct lag_tracker tracker;
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (2 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 05/13] net/mlx5: LAG, drop per-port scratch array in drop-rule setup Tariq Toukan
` (8 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
mlx5_lag_print_mapping(), mlx5_infer_tx_affinity_mapping() and
lag_active_port_bits() each used a MLX5_MAX_PORTS-sized stack buffer to
hold per-port working state. Allocate these buffers with
kcalloc(ldev->ports, ...) instead so they track the actual port count
and no longer depend on MLX5_MAX_PORTS.
lag_active_port_bits() can now fail on allocation, so it returns int and
propagates -ENOMEM; a non-negative return is the active-port bitmask as
before. Its two callers, mlx5_cmd_create_lag() and _mlx5_modify_lag(),
check for a negative return and bail out, then narrow the value back
into the u8 they hand to the device.
This removes these buffers' dependency on MLX5_MAX_PORTS.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/lag/lag.c | 100 ++++++++++++++----
1 file changed, 80 insertions(+), 20 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index 00b0159cb422..93ead5664152 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -63,19 +63,25 @@ static int get_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags)
return MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY;
}
-static u8 lag_active_port_bits(struct mlx5_lag *ldev,
- struct lag_tracker *tracker)
+static int lag_active_port_bits(struct mlx5_lag *ldev,
+ struct lag_tracker *tracker)
{
- u8 enabled_ports[MLX5_MAX_PORTS] = {};
u8 active_port = 0;
+ u8 *enabled_ports;
int num_enabled;
int idx;
+ enabled_ports = kcalloc(ldev->ports, sizeof(*enabled_ports),
+ GFP_KERNEL);
+ if (!enabled_ports)
+ return -ENOMEM;
+
mlx5_infer_tx_enabled(tracker, ldev, enabled_ports,
&num_enabled);
for (idx = 0; idx < num_enabled; idx++)
active_port |= BIT_MASK(enabled_ports[idx]);
+ kfree(enabled_ports);
return active_port;
}
@@ -105,13 +111,21 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,
MLX5_SET(lagc, lag_ctx, tx_remap_affinity_1, ports[idx0]);
MLX5_SET(lagc, lag_ctx, tx_remap_affinity_2, ports[idx1]);
break;
- case MLX5_LAG_PORT_SELECT_MODE_PORT_SELECT_FT:
+ case MLX5_LAG_PORT_SELECT_MODE_PORT_SELECT_FT: {
+ u8 active_port;
+ int ret;
+
if (!MLX5_CAP_PORT_SELECTION(dev, port_select_flow_table_bypass))
break;
- MLX5_SET(lagc, lag_ctx, active_port,
- lag_active_port_bits(ldev, tracker));
+ ret = lag_active_port_bits(ldev, tracker);
+ if (ret < 0)
+ return ret;
+
+ active_port = ret;
+ MLX5_SET(lagc, lag_ctx, active_port, active_port);
break;
+ }
default:
break;
}
@@ -240,22 +254,31 @@ static void mlx5_lag_print_mapping(struct mlx5_core_dev *dev,
struct lag_tracker *tracker,
unsigned long flags)
{
- char buf[MLX5_MAX_PORTS * 10 + 1] = {};
- u8 enabled_ports[MLX5_MAX_PORTS] = {};
+ u8 *enabled_ports = NULL;
int written = 0;
int num_enabled;
+ char *buf;
int idx;
int err;
int i;
int j;
+ buf = kcalloc(ldev->ports * 10 + 1, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return;
+
if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &flags)) {
+ enabled_ports = kcalloc(ldev->ports, sizeof(*enabled_ports),
+ GFP_KERNEL);
+ if (!enabled_ports)
+ goto free_buf;
+
mlx5_infer_tx_enabled(tracker, ldev, enabled_ports,
&num_enabled);
for (i = 0; i < num_enabled; i++) {
err = scnprintf(buf + written, 4, "%d, ", enabled_ports[i] + 1);
if (err != 3)
- return;
+ goto free_enabled;
written += err;
}
buf[written - 2] = 0;
@@ -267,12 +290,17 @@ static void mlx5_lag_print_mapping(struct mlx5_core_dev *dev,
err = scnprintf(buf + written, 10,
" port %d:%d", i + 1, ldev->v2p_map[idx]);
if (err != 9)
- return;
+ goto free_enabled;
written += err;
}
}
mlx5_core_info(dev, "lag map:%s\n", buf);
}
+
+free_enabled:
+ kfree(enabled_ports);
+free_buf:
+ kfree(buf);
}
static int mlx5_lag_netdev_event(struct notifier_block *this,
@@ -668,20 +696,28 @@ static bool __mlx5_lag_is_sd_active(struct mlx5_lag *ldev,
* If there are ports that are disabled fill the relevant slots
* with mapping that points to active ports.
*/
-static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
- struct mlx5_lag *ldev,
- u8 buckets,
- u8 *ports)
+static int mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
+ struct mlx5_lag *ldev,
+ u8 buckets,
+ u8 *ports)
{
- int disabled[MLX5_MAX_PORTS] = {};
- int enabled[MLX5_MAX_PORTS] = {};
int disabled_ports_num = 0;
int enabled_ports_num = 0;
+ int *disabled;
+ int *enabled;
+ int err = 0;
int idx;
u32 rand;
int i;
int j;
+ enabled = kcalloc(ldev->ports, sizeof(*enabled), GFP_KERNEL);
+ disabled = kcalloc(ldev->ports, sizeof(*disabled), GFP_KERNEL);
+ if (!enabled || !disabled) {
+ err = -ENOMEM;
+ goto out;
+ }
+
mlx5_ldev_for_each(i, 0, ldev) {
if (tracker->netdev_state[i].tx_enabled &&
tracker->netdev_state[i].link_up)
@@ -704,7 +740,7 @@ static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
/* If all ports are disabled/enabled keep native mapping */
if (enabled_ports_num == ldev->ports ||
disabled_ports_num == ldev->ports)
- return;
+ goto out;
/* Go over the disabled ports and for each assign a random active port */
for (i = 0; i < disabled_ports_num; i++) {
@@ -717,6 +753,11 @@ static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
mlx5_lag_xa_to_dev_idx(ldev, rand_xa_idx) + 1;
}
}
+
+out:
+ kfree(enabled);
+ kfree(disabled);
+ return err;
}
static bool mlx5_lag_has_drop_rule(struct mlx5_lag *ldev)
@@ -816,7 +857,10 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev,
!MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table_bypass))
return ret;
- active_ports = lag_active_port_bits(ldev, tracker);
+ ret = lag_active_port_bits(ldev, tracker);
+ if (ret < 0)
+ return ret;
+ active_ports = ret;
return mlx5_cmd_modify_active_port(dev0, active_ports);
}
@@ -879,7 +923,14 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
if (!ports)
return;
- mlx5_infer_tx_affinity_mapping(tracker, ldev, ldev->buckets, ports);
+ err = mlx5_infer_tx_affinity_mapping(tracker, ldev, ldev->buckets,
+ ports);
+ if (err) {
+ mlx5_core_err(dev0,
+ "mlx5_infer_tx_affinity_mapping failed, err = %d\n",
+ err);
+ goto out;
+ }
mlx5_ldev_for_each(i, 0, ldev) {
for (j = 0; j < ldev->buckets; j++) {
@@ -1045,7 +1096,16 @@ int mlx5_activate_lag(struct mlx5_lag *ldev,
return err;
if (mode != MLX5_LAG_MODE_MPESW) {
- mlx5_infer_tx_affinity_mapping(tracker, ldev, ldev->buckets, ldev->v2p_map);
+ err = mlx5_infer_tx_affinity_mapping(tracker, ldev,
+ ldev->buckets,
+ ldev->v2p_map);
+ if (err) {
+ mlx5_core_err(dev0,
+ "mlx5_infer_tx_affinity_mapping failed, err = %d\n",
+ err);
+ return err;
+ }
+
if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &flags)) {
err = mlx5_lag_port_sel_create(ldev, tracker->hash_type,
ldev->v2p_map);
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 05/13] net/mlx5: LAG, drop per-port scratch array in drop-rule setup
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (3 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 06/13] net/mlx5: LAG, size debugfs buffers by port count Tariq Toukan
` (7 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
mlx5_lag_drop_rule_setup() walks the per-port tracker state, but does so
through a per-port scratch array, mlx5_lag_drop_rule_setup() collects
the disabled ports to create a per-port drop rule.
Walk the tracker state inline removing its MLX5_MAX_PORTS dependency.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/lag/lag.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index 93ead5664152..4648371f9914 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -792,11 +792,8 @@ static void mlx5_lag_drop_rule_cleanup(struct mlx5_lag *ldev)
static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,
struct lag_tracker *tracker)
{
- u8 disabled_ports[MLX5_MAX_PORTS] = {};
struct mlx5_core_dev *dev;
struct lag_func *pf;
- int disabled_index;
- int num_disabled;
int err;
int i;
@@ -808,11 +805,11 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,
if (!ldev->tracker.has_inactive)
return;
- mlx5_infer_tx_disabled(tracker, ldev, disabled_ports, &num_disabled);
-
- for (i = 0; i < num_disabled; i++) {
- disabled_index = disabled_ports[i];
- pf = mlx5_lag_pf(ldev, disabled_index);
+ mlx5_ldev_for_each(i, 0, ldev) {
+ if (tracker->netdev_state[i].tx_enabled &&
+ tracker->netdev_state[i].link_up)
+ continue;
+ pf = mlx5_lag_pf(ldev, i);
dev = pf->dev;
err = mlx5_esw_acl_ingress_vport_drop_rule_create(dev->priv.eswitch,
MLX5_VPORT_UPLINK);
@@ -820,7 +817,8 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,
pf->has_drop = true;
else
mlx5_core_err(dev,
- "Failed to create lag drop rule, error: %d", err);
+ "Failed to create lag drop rule, error: %d",
+ err);
}
}
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 06/13] net/mlx5: LAG, size debugfs buffers by port count
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (4 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 05/13] net/mlx5: LAG, drop per-port scratch array in drop-rule setup Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow Tariq Toukan
` (6 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
debugfs mapping_show() uses constant MLX5_MAX_PORTS to size ports array.
Instead, allocate dynamically using actual number of ports.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
index e9b8d79d2d21..44296789d656 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
@@ -101,14 +101,18 @@ static int flags_show(struct seq_file *file, void *priv)
static int mapping_show(struct seq_file *file, void *priv)
{
struct mlx5_core_dev *dev = file->private;
- u8 ports[MLX5_MAX_PORTS] = {};
struct mlx5_lag *ldev;
bool hash = false;
bool lag_active;
int i, idx = 0;
int num_ports;
+ u8 *ports;
ldev = mlx5_lag_dev(dev);
+ ports = kcalloc(ldev->ports, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
mutex_lock(&ldev->lock);
lag_active = __mlx5_lag_is_active(ldev);
if (lag_active) {
@@ -123,8 +127,10 @@ static int mapping_show(struct seq_file *file, void *priv)
}
}
mutex_unlock(&ldev->lock);
- if (!lag_active)
+ if (!lag_active) {
+ kfree(ports);
return -EINVAL;
+ }
for (i = 0; i < num_ports; i++) {
if (hash)
@@ -133,6 +139,7 @@ static int mapping_show(struct seq_file *file, void *priv)
seq_printf(file, "%d:%d\n", i + 1, ports[i]);
}
+ kfree(ports);
return 0;
}
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (5 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 06/13] net/mlx5: LAG, size debugfs buffers by port count Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray Tariq Toukan
` (5 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
A TC eswitch flow offloaded to a shared FDB is duplicated onto every
peer eswitch. Besides the forward list of duplicates on the origin flow
(mlx5e_tc_flow.peer_flows), the eswitch keeps a reverse index,
esw->offloads.peer_flows[], an array of per-peer list heads keyed by the
peer's LAG sequence number, used to tear down a peer's duplicates when
it is removed.
That reverse index was anchored on the origin: since one origin can have
a duplicate on every peer, the origin carried per-peer storage sized to
the max port count - a peer[MLX5_MAX_PORTS] array of list nodes plus a
peer_used bitmap - and each duplicate stored its peer_index.
Anchor it on the duplicate instead, which belongs to a single peer. Give
each flow one list node, mlx5e_tc_flow.peer, and place the duplicate -
not the origin - on its peer's list through it; add a peer_orig back-ref
so del can reach the origin from the duplicate. One node then replaces
the origin's per-peer array, and del unlinks the duplicate directly, so
peer[], peer_used and peer_index are dropped.
The per-peer list heads remain the fixed esw->offloads.peer_flows[]
array keyed by the LAG sequence number - that array is replaced in the
next patch. No functional change.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../ethernet/mellanox/mlx5/core/en/tc_priv.h | 14 ++---
.../net/ethernet/mellanox/mlx5/core/en_tc.c | 59 ++++++++-----------
2 files changed, 29 insertions(+), 44 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
index 7bfe7cdc5770..1ceebab4ce08 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h
@@ -97,18 +97,14 @@ struct mlx5e_tc_flow {
struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS];
struct mlx5e_hairpin_entry *hpe; /* attached hairpin instance */
struct list_head hairpin; /* flows sharing the same hairpin */
- struct list_head peer[MLX5_MAX_PORTS]; /* flows with peer flow */
- DECLARE_BITMAP(peer_used, MLX5_MAX_PORTS); /* tracks populated peer
- * slots
- */
+ struct list_head peer; /* dup: node in origin esw's peer_flows */
struct list_head unready; /* flows not ready to be offloaded (e.g
* due to missing route)
*/
- struct list_head peer_flows; /* flows on peer */
- int peer_index; /* peer-flow index pinned at add time, used at del
- * time so removal is independent of LAG state
- * changes between add and del.
- */
+
+ /* origin: its dups; dup: node on that list */
+ struct list_head peer_flows;
+ struct mlx5e_tc_flow *peer_orig; /* dup: back-ref to origin flow */
struct net_device *orig_dev; /* netdev adding flow first */
int tmp_entry_index;
struct list_head tmp_list; /* temporary flow list used by neigh update */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index b290beb4369a..fae4f8625da4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -2116,43 +2116,35 @@ void mlx5e_put_flow_list(struct mlx5e_priv *priv, struct list_head *flow_list)
mlx5e_flow_put(priv, flow);
}
-static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow,
- int peer_index)
+static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *peer_flow)
{
- struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch;
- struct mlx5e_tc_flow *peer_flow;
- struct mlx5e_tc_flow *tmp;
-
- if (!flow_flag_test(flow, ESWITCH) ||
- !flow_flag_test(flow, DUP))
- return;
+ struct mlx5e_tc_flow *flow = peer_flow->peer_orig;
+ struct mlx5_eswitch *esw;
+ esw = flow->priv->mdev->priv.eswitch;
mutex_lock(&esw->offloads.peer_mutex);
- list_del(&flow->peer[peer_index]);
- clear_bit(peer_index, flow->peer_used);
+ list_del(&peer_flow->peer);
mutex_unlock(&esw->offloads.peer_mutex);
- list_for_each_entry_safe(peer_flow, tmp, &flow->peer_flows, peer_flows) {
- if (peer_index != peer_flow->peer_index)
- continue;
-
- list_del(&peer_flow->peer_flows);
- if (refcount_dec_and_test(&peer_flow->refcnt)) {
- mlx5e_tc_del_fdb_flow(peer_flow->priv, peer_flow);
- kfree(peer_flow);
- }
- }
-
+ list_del(&peer_flow->peer_flows);
if (list_empty(&flow->peer_flows))
flow_flag_clear(flow, DUP);
+
+ if (refcount_dec_and_test(&peer_flow->refcnt)) {
+ mlx5e_tc_del_fdb_flow(peer_flow->priv, peer_flow);
+ kfree(peer_flow);
+ }
}
static void mlx5e_tc_del_fdb_peers_flow(struct mlx5e_tc_flow *flow)
{
- int i;
+ struct mlx5e_tc_flow *peer_flow, *tmp;
- for_each_set_bit(i, flow->peer_used, MLX5_MAX_PORTS)
- mlx5e_tc_del_fdb_peer_flow(flow, i);
+ if (!flow_flag_test(flow, ESWITCH) || !flow_flag_test(flow, DUP))
+ return;
+
+ list_for_each_entry_safe(peer_flow, tmp, &flow->peer_flows, peer_flows)
+ mlx5e_tc_del_fdb_peer_flow(peer_flow);
}
static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
@@ -4639,12 +4631,11 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
goto out;
}
- peer_flow->peer_index = i;
+ peer_flow->peer_orig = flow;
list_add_tail(&peer_flow->peer_flows, &flow->peer_flows);
flow_flag_set(flow, DUP);
mutex_lock(&esw->offloads.peer_mutex);
- list_add_tail(&flow->peer[i], &esw->offloads.peer_flows[i]);
- set_bit(i, flow->peer_used);
+ list_add_tail(&peer_flow->peer, &esw->offloads.peer_flows[i]);
mutex_unlock(&esw->offloads.peer_mutex);
out:
@@ -5539,21 +5530,19 @@ int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags)
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
{
- struct mlx5_devcom_comp_dev *devcom;
- struct mlx5_devcom_comp_dev *pos;
- struct mlx5e_tc_flow *flow, *tmp;
+ struct mlx5_devcom_comp_dev *devcom = esw->devcom, *pos;
+ struct mlx5e_tc_flow *peer_flow, *tmp_peer_flow;
struct mlx5_eswitch *peer_esw;
int i;
- devcom = esw->devcom;
-
mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
i = mlx5_lag_get_dev_seq(peer_esw->dev);
if (i < 0)
continue;
- list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i])
- mlx5e_tc_del_fdb_peers_flow(flow);
+ list_for_each_entry_safe(peer_flow, tmp_peer_flow,
+ &esw->offloads.peer_flows[i], peer)
+ mlx5e_tc_del_fdb_peer_flow(peer_flow);
}
}
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (6 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:46 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 09/13] net/mlx5: E-switch, derive manager vport from device capability Tariq Toukan
` (4 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
The per-peer reverse index lived in a fixed esw->offloads.peer_flows[]
array of list heads indexed by the peer's LAG sequence number, capping a
shared FDB at MLX5_MAX_PORTS members.
Replace the array with an xarray keyed by the peer vhca_id. Each entry
is a heap-allocated list head that chains the flows duplicated to that
peer via their peer node. This lifts the MLX5_MAX_PORTS cap: the number
of peers is bounded only by the number of distinct vhca_ids.
The lifetime of the duplicated peer flows list_head follows the devcom
pairing of the two eswitches:
- pair (mlx5_esw_offloads_pair): kzalloc_obj + INIT_LIST_HEAD +
xa_store the dup_peer_flows under the peer's vhca_id.
- add (mlx5e_tc_add_fdb_peer_flow): xa_load the dup_peer_flows and
list_add the duplicated flow's peer node.
- del (mlx5e_tc_del_fdb_peer_flow): list_del that node.
- flush (mlx5e_tc_clean_fdb_peer_flows): xa_for_each dup_peer_flows,
drop every duplicated flow on it; called from unpair.
- unpair (mlx5_esw_offloads_unpair): xa_erase + kfree the
dup_peer_flows.
So a dup_peer_flows is created when the local eswitch pairs with a peer
over devcom and freed when they unpair.
No functional change.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
.../net/ethernet/mellanox/mlx5/core/en_tc.c | 22 +++++++---------
.../net/ethernet/mellanox/mlx5/core/eswitch.h | 2 +-
.../mellanox/mlx5/core/eswitch_offloads.c | 26 ++++++++++++++++---
3 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index fae4f8625da4..c99e824b4c6d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -4596,12 +4596,13 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
unsigned long flow_flags,
struct mlx5_eswitch *peer_esw)
{
+ u16 peer_vhca_id = MLX5_CAP_GEN(peer_esw->dev, vhca_id);
struct mlx5e_priv *priv = flow->priv, *peer_priv;
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
struct mlx5_esw_flow_attr *attr = flow->attr->esw_attr;
struct mlx5e_tc_flow_parse_attr *parse_attr;
- int i = mlx5_lag_get_dev_seq(peer_esw->dev);
struct mlx5e_rep_priv *peer_urpriv;
+ struct list_head *dup_peer_flows;
struct mlx5e_tc_flow *peer_flow;
struct mlx5_core_dev *in_mdev;
int err = 0;
@@ -4634,8 +4635,11 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
peer_flow->peer_orig = flow;
list_add_tail(&peer_flow->peer_flows, &flow->peer_flows);
flow_flag_set(flow, DUP);
+ dup_peer_flows = xa_load(&esw->offloads.peer_flows, peer_vhca_id);
+ if (!dup_peer_flows)
+ return -ENODEV;
mutex_lock(&esw->offloads.peer_mutex);
- list_add_tail(&peer_flow->peer, &esw->offloads.peer_flows[i]);
+ list_add_tail(&peer_flow->peer, dup_peer_flows);
mutex_unlock(&esw->offloads.peer_mutex);
out:
@@ -5530,20 +5534,14 @@ int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags)
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
{
- struct mlx5_devcom_comp_dev *devcom = esw->devcom, *pos;
struct mlx5e_tc_flow *peer_flow, *tmp_peer_flow;
- struct mlx5_eswitch *peer_esw;
- int i;
-
- mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
- i = mlx5_lag_get_dev_seq(peer_esw->dev);
- if (i < 0)
- continue;
+ struct list_head *dup_peer_flows;
+ unsigned long index;
+ xa_for_each(&esw->offloads.peer_flows, index, dup_peer_flows)
list_for_each_entry_safe(peer_flow, tmp_peer_flow,
- &esw->offloads.peer_flows[i], peer)
+ dup_peer_flows, peer)
mlx5e_tc_del_fdb_peer_flow(peer_flow);
- }
}
void mlx5e_tc_reoffload_flows_work(struct work_struct *work)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 8b1f93b13ea9..e9cbcd73b23f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -319,7 +319,7 @@ struct mlx5_esw_offload {
struct mlx5_flow_handle *vport_rx_drop_rule;
struct mlx5_flow_table *ft_ipsec_tx_pol;
struct xarray vport_reps;
- struct list_head peer_flows[MLX5_MAX_PORTS];
+ struct xarray peer_flows;
struct mutex peer_mutex;
struct mutex encap_tbl_lock; /* protects encap_tbl */
DECLARE_HASHTABLE(encap_tbl, 8);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index e7d92d9bde16..c712848b202d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -3383,10 +3383,15 @@ static void mlx5_esw_offloads_rep_event_unpair(struct mlx5_eswitch *esw,
static void mlx5_esw_offloads_unpair(struct mlx5_eswitch *esw,
struct mlx5_eswitch *peer_esw)
{
+ struct list_head *dup_peer_flows;
+
#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
mlx5e_tc_clean_fdb_peer_flows(esw);
#endif
mlx5_esw_offloads_rep_event_unpair(esw, peer_esw);
+ dup_peer_flows = xa_erase(&esw->offloads.peer_flows,
+ MLX5_CAP_GEN(peer_esw->dev, vhca_id));
+ kfree(dup_peer_flows);
esw_del_fdb_peer_miss_rules(esw, peer_esw->dev);
}
@@ -3394,6 +3399,7 @@ static int mlx5_esw_offloads_pair(struct mlx5_eswitch *esw,
struct mlx5_eswitch *peer_esw)
{
const struct mlx5_eswitch_rep_ops *ops;
+ struct list_head *dup_peer_flows;
struct mlx5_eswitch_rep *rep;
unsigned long i;
u8 rep_type;
@@ -3403,6 +3409,20 @@ static int mlx5_esw_offloads_pair(struct mlx5_eswitch *esw,
if (err)
return err;
+ dup_peer_flows = kzalloc_obj(*dup_peer_flows);
+ if (!dup_peer_flows) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ INIT_LIST_HEAD(dup_peer_flows);
+ err = xa_err(xa_store(&esw->offloads.peer_flows,
+ MLX5_CAP_GEN(peer_esw->dev, vhca_id),
+ dup_peer_flows, GFP_KERNEL));
+ if (err) {
+ kfree(dup_peer_flows);
+ goto err_out;
+ }
+
mlx5_esw_for_each_rep(esw, i, rep) {
for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) {
ops = esw->offloads.rep_ops[rep_type];
@@ -3549,10 +3569,8 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
const struct mlx5_devcom_match_attr *attr)
{
int err;
- int i;
- for (i = 0; i < MLX5_MAX_PORTS; i++)
- INIT_LIST_HEAD(&esw->offloads.peer_flows[i]);
+ xa_init(&esw->offloads.peer_flows);
mutex_init(&esw->offloads.peer_mutex);
if (!MLX5_CAP_ESW(esw->dev, merged_eswitch))
@@ -3595,6 +3613,8 @@ void mlx5_esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw)
xa_destroy(&esw->paired);
xa_destroy(&esw->fdb_table.offloads.peer_miss_rules);
esw->devcom = NULL;
+ WARN_ON_ONCE(!xa_empty(&esw->offloads.peer_flows));
+ xa_destroy(&esw->offloads.peer_flows);
}
bool mlx5_esw_offloads_devcom_is_ready(struct mlx5_eswitch *esw)
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 09/13] net/mlx5: E-switch, derive manager vport from device capability
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (7 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-24 17:46 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 10/13] net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode Tariq Toukan
` (3 subsequent siblings)
12 siblings, 1 reply; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
mlx5_eswitch_manager_vport() hardcoded the e-switch manager vport as
either the ECPF or the host PF. Read esw_manager_vport_number from the
e-switch capability when esw_manager_vport_number_valid is set, so the
manager vport can be a VF - a prerequisite for VFs acting as e-switch
managers. Keep the previous mapping as fallback when the capability does
not report a number.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 11 +++++++++--
include/linux/mlx5/eswitch.h | 3 +++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index fc197d1dc9df..e7561f0f89b0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -2460,13 +2460,20 @@ static int mlx5_esw_vports_init(struct mlx5_eswitch *esw)
}
}
- if (mlx5_ecpf_vport_exists(dev) ||
- mlx5_core_is_ecpf_esw_manager(dev)) {
+ if (mlx5_ecpf_vport_exists(dev)) {
err = mlx5_esw_vport_alloc(esw, idx, MLX5_VPORT_ECPF);
if (err)
goto err;
idx++;
}
+
+ if (!xa_load(&esw->vports, esw->manager_vport)) {
+ err = mlx5_esw_vport_alloc(esw, idx, esw->manager_vport);
+ if (err)
+ goto err;
+ idx++;
+ }
+
err = mlx5_esw_vport_alloc(esw, idx, MLX5_VPORT_UPLINK);
if (err)
goto err;
diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h
index a0dd162baa78..03d3620141c8 100644
--- a/include/linux/mlx5/eswitch.h
+++ b/include/linux/mlx5/eswitch.h
@@ -222,6 +222,9 @@ static inline bool is_mdev_switchdev_mode(struct mlx5_core_dev *dev)
/* The returned number is valid only when the dev is eswitch manager. */
static inline u16 mlx5_eswitch_manager_vport(struct mlx5_core_dev *dev)
{
+ if (MLX5_CAP_ESW(dev, esw_manager_vport_number_valid))
+ return MLX5_CAP_ESW(dev, esw_manager_vport_number);
+
return mlx5_core_is_ecpf_esw_manager(dev) ?
MLX5_VPORT_ECPF : MLX5_VPORT_HOST_PF;
}
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 10/13] net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (8 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 09/13] net/mlx5: E-switch, derive manager vport from device capability Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 11/13] net/mlx5: LAG, drop stale esw_shared_ingress_acl gate from shared FDB Tariq Toukan
` (2 subsequent siblings)
12 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
The mapping debugfs file dumps the LAG virtual-to-physical port mapping.
In MPESW mode the LAG does not own that mapping - port selection is
driven by user steering rules, not by the LAG - so the dumped values are
meaningless.
Treat MPESW as if LAG is inactive and don't print anything to the user.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
index 44296789d656..b6125c7bd91b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
@@ -114,7 +114,8 @@ static int mapping_show(struct seq_file *file, void *priv)
return -ENOMEM;
mutex_lock(&ldev->lock);
- lag_active = __mlx5_lag_is_active(ldev);
+ lag_active = __mlx5_lag_is_active(ldev) &&
+ ldev->mode != MLX5_LAG_MODE_MPESW;
if (lag_active) {
if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags)) {
mlx5_infer_tx_enabled(&ldev->tracker, ldev, ports,
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 11/13] net/mlx5: LAG, drop stale esw_shared_ingress_acl gate from shared FDB
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (9 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 10/13] net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 12/13] net/mlx5: E-switch, correct stale VF/PF wording in esw-allowed comments Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 13/13] net/mlx5: E-switch, disable host functions for a non PF e-switch manager Tariq Toukan
12 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
The shared-FDB support check still requires the esw_shared_ingress_acl
capability. That capability backed the merged uplink ingress ACL, where
the slave uplink's ingress-ACL root was pointed at the master's
(esw_set_uplink_slave_ingress_root()). That handling was removed in
commit 82e86a6c7109 ("net/mlx5: E-switch, remove special uplink ingress
ACL handling") once both uplinks were tagged with the same reserved
metadata (MLX5_ESW_METADATA_RSVD_UPLINK) - a single FDB rule then
matches traffic from either uplink, with no shared ingress ACL involved.
The capability check was left behind and has been dead since: the driver
programs no shared ingress ACL, yet the check still keeps otherwise
shared-FDB-capable devices out of shared FDB. Drop it.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c
index 6b4ad3c53f2f..c2185c3d7f6f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c
@@ -35,8 +35,7 @@ bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter)
if (is_mdev_switchdev_mode(dev0) &&
mlx5_eswitch_vport_match_metadata_enabled(dev0->priv.eswitch) &&
- mlx5_esw_offloads_devcom_is_ready(dev0->priv.eswitch) &&
- MLX5_CAP_ESW(dev0, esw_shared_ingress_acl))
+ mlx5_esw_offloads_devcom_is_ready(dev0->priv.eswitch))
ret = true;
return ret;
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 12/13] net/mlx5: E-switch, correct stale VF/PF wording in esw-allowed comments
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (10 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 11/13] net/mlx5: LAG, drop stale esw_shared_ingress_acl gate from shared FDB Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 13/13] net/mlx5: E-switch, disable host functions for a non PF e-switch manager Tariq Toukan
12 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
mlx5_esw_hold() and mlx5_eswitch_unblock_ipsec() bail out on
!mlx5_esw_allowed(), and their comments explained that as "a VF has no
eswitch" / "core dev is not a PF". New FW will let a VF act as a nested
e-switch manager - such a VF has an eswitch and passes
mlx5_esw_allowed() - so that equivalence no longer holds. Reword the
comments to describe the actual condition, the device is not an e-switch
manager, with no functional change.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index e7561f0f89b0..6f3676c86d3d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -2958,7 +2958,7 @@ bool mlx5_esw_hold(struct mlx5_core_dev *mdev)
{
struct mlx5_eswitch *esw = mdev->priv.eswitch;
- /* e.g. VF doesn't have eswitch so nothing to do */
+ /* Not an eswitch manager, so there is no mode lock to take */
if (!mlx5_esw_allowed(esw))
return true;
@@ -3106,7 +3106,7 @@ void mlx5_eswitch_unblock_ipsec(struct mlx5_core_dev *dev)
struct mlx5_eswitch *esw = dev->priv.eswitch;
if (!mlx5_esw_allowed(esw))
- /* Failure means no eswitch => core dev is not a PF */
+ /* Not an eswitch manager, so nothing was blocked */
return;
mutex_lock(&esw->state_lock);
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net-next 13/13] net/mlx5: E-switch, disable host functions for a non PF e-switch manager
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
` (11 preceding siblings ...)
2026-09-23 10:38 ` [PATCH net-next 12/13] net/mlx5: E-switch, correct stale VF/PF wording in esw-allowed comments Tariq Toukan
@ 2026-09-23 10:38 ` Tariq Toukan
12 siblings, 0 replies; 22+ messages in thread
From: Tariq Toukan @ 2026-09-23 10:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni
Cc: Akiva Goldberger, Cosmin Ratiu, Gal Pressman, Leon Romanovsky,
open list, linux-rdma, Mark Bloch, Moshe Shemesh, Or Har-Toov,
Saeed Mahameed, Shay Drory, Tariq Toukan
From: Shay Drory <shayd@nvidia.com>
mlx5_esw_host_functions_enabled() only treated host functions as
disabled for an ECPF manager.
To support a non PF acting as an e-switch manager, add a check for a non
PF e-switch manager that treats host functions as disabled. This will
avoid allocating a host-PF vport in mlx5_esw_vports_init().
With host functions disabled for non PF, its e-switch is the expected
manager + uplink only.
Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
Reviewed-by: Akiva Goldberger <agoldberger@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
---
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index 6f3676c86d3d..2d289fb1353b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1241,6 +1241,11 @@ static int mlx5_esw_host_functions_enabled_query(struct mlx5_eswitch *esw)
struct mlx5_esw_pf_info host_pf_info;
const u32 *query_host_out;
+ if (!mlx5_core_is_pf(esw->dev)) {
+ esw->esw_funcs.host_funcs_disabled = true;
+ return 0;
+ }
+
if (!mlx5_core_is_ecpf_esw_manager(esw->dev))
return 0;
--
2.44.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address
2026-09-23 10:38 ` [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address Tariq Toukan
@ 2026-09-24 17:45 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:45 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] The commit message for "net/mlx5e: Assign a random MAC to any
netdev with a zero MAC address" describes a bug ('A vport group…
Pre-existing issues:
- [Medium] In mlx5e_set_netdev_dev_addr()
(drivers/net/ethernet/mellanox/mlx5/core/en_main.c), the local `u8…
--
net/mlx5e: Assign a random MAC to any netdev with a zero MAC address
This commit removes the vport_group_manager exception from
mlx5e_set_netdev_dev_addr(). As a result, any mlx5e netdev whose queried
MAC is all zeros gets a random MAC instead of keeping the zero address.
> mlx5e_set_netdev_dev_addr() falls back to a random MAC only when the
> queried address is all-zeros AND the device is not a vport group
> manager. A vport group manager that comes up with a zero MAC is
> therefore left with an invalid all-zero address.
>
> The vport_group_manager exception is unnecessary: a zero MAC is always
> invalid, regardless of device type. Drop the condition and assign a
> random MAC whenever the queried address is zero, so every netdev comes
> up with a valid address.
[Severity: Low]
The message treats the old behaviour as a bug. However, it has no Fixes:
tag and doesn't say which configuration triggers it.
The check being removed was added on purpose in commit 108805fc196e
("net/mlx5e: Assign random MAC address if needed"). That commit only
covers SR-IOV VFs loaded without an admin-assigned MAC. It used
vport_group_manager (PF/ECPF, which is expected to have a burned-in MAC)
as a stand-in for "not a VF". The new message calls the exception
"unnecessary" but doesn't explain why it was there.
Another commit in the series, "net/mlx5: E-switch, correct stale VF/PF
wording in esw-allowed comments", suggests the real motivation is new FW
that lets a VF act as a nested e-switch manager. If so, this is a new
case rather than a regression.
Could the message do one of these?
- If a zero-MAC group manager can happen with existing FW, add:
Fixes: 108805fc196e ("net/mlx5e: Assign random MAC address if needed")
- Otherwise, say that this is preparation for VF e-switch managers.
The message also leaves out the visible effect on affected devices.
addr_assign_type changes from NET_ADDR_PERM to NET_ADDR_RANDOM, and the
MAC changes on every driver load. Other users of mlx5_query_mac_address()
still see the firmware's zero address, for example the getpermhwaddr path
in en_dcbnl.c and the default GID in rdma.c.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> index fc110a7d16e8d..b0765f07cc3a7 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> @@ -5430,8 +5430,7 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
> u8 addr[ETH_ALEN];
>
> mlx5_query_mac_address(priv->mdev, addr);
> - if (is_zero_ether_addr(addr) &&
> - !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) {
> + if (is_zero_ether_addr(addr)) {
[Severity: Medium]
This is a pre-existing issue and was not introduced by this patch.
Since the patch rewrites this condition, though: what does addr hold if
mlx5_query_mac_address() fails?
addr has no initializer, and the return value of the query is ignored.
The path is:
_mlx5e_probe()->mlx5e_build_nic_netdev()->mlx5e_set_netdev_dev_addr()
->mlx5_query_mac_address()->mlx5_query_nic_vport_mac_address()
mlx5_query_nic_vport_mac_address() in vport.c writes the caller's buffer
only when the command succeeds:
err = mlx5_cmd_exec_inout(mdev, query_nic_vport_context, in, out);
if (!err)
ether_addr_copy(addr, &out_addr[2]);
return err;
The query can fail with a bad FW status. It can also fail with -ENOLINK
during FW internal error, because mlx5_internal_err_ret_value() includes
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT in the opcodes it handles that way.
In that case is_zero_ether_addr() reads whatever was left on the stack.
Unless those bytes are all zero, the random fallback is skipped and
eth_hw_addr_set(netdev, addr) installs them as the MAC.
Can this give the netdev an invalid address, for example one with the
multicast bit set? Can it also expose 6 bytes of kernel stack through
RTM_GETLINK, the sysfs address file or SIOCGIFHWADDR?
With CONFIG_INIT_STACK_ALL_ZERO the buffer starts zeroed, so after this
patch the failure case gets a random MAC. With INIT_STACK_NONE, the
leftover bytes are installed instead.
The unchecked return value and the uninitialized buffer go back to
537e4d2e6fe3c5 and earlier code. No later patch in the series appears to
touch this function.
To reach the goal that "every netdev comes up with a valid address",
would it help to zero-initialize addr, check the return value, and use
something like this?
if (err || !is_valid_ether_addr(addr))
eth_hw_addr_random(netdev);
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 02/13] net/mlx5: E-switch, do not leave an unpaired devcom registered
2026-09-23 10:38 ` [PATCH net-next 02/13] net/mlx5: E-switch, do not leave an unpaired devcom registered Tariq Toukan
@ 2026-09-24 17:45 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:45 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 2 · Low: 0
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] After a failed initial PAIR, the new branch in
mlx5_esw_offloads_devcom_init() leaves esw->devcom == NULL, yet…
- [Medium] When the initial PAIR fails, mlx5_esw_offloads_devcom_init()
now calls mlx5_esw_offloads_devcom_cleanup(), which unregisters the…
- [Medium] The new cleanup call runs at the end of esw_offloads_enable().
--
net/mlx5: E-switch, do not leave an unpaired devcom registered
This commit checks the return value of the initial ESW_OFFLOADS_DEVCOM_PAIR
event in mlx5_esw_offloads_devcom_init(). If pairing fails, it calls
mlx5_esw_offloads_devcom_cleanup(), which unregisters the devcom component
instead of leaving it registered until eswitch teardown.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
> index eb74b62601682..e7d92d9bde16d 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
[ ... ]
> @@ -3572,10 +3573,12 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
> if (!esw->devcom)
> return;
>
> - mlx5_devcom_send_event(esw->devcom,
> - ESW_OFFLOADS_DEVCOM_PAIR,
> - ESW_OFFLOADS_DEVCOM_UNPAIR,
> - esw);
> + err = mlx5_devcom_send_event(esw->devcom,
> + ESW_OFFLOADS_DEVCOM_PAIR,
> + ESW_OFFLOADS_DEVCOM_UNPAIR,
> + esw);
> + if (err)
> + mlx5_esw_offloads_devcom_cleanup(esw);
[Severity: Medium]
Does this mean the eswitch can never pair with a peer later?
Only mlx5_esw_offloads_devcom_init() sends ESW_OFFLOADS_DEVCOM_PAIR.
mlx5_devcom_locked_send_event() delivers it only to comp_devs that are
still on comp->comp_dev_list_head:
mlx5_devcom_locked_send_event() {
...
list_for_each_entry(pos, &comp->comp_dev_list_head, list) {
data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
if (pos != devcom && data) {
err = comp->handler(event, data, event_data);
...
}
Before this patch, a failed initial PAIR was rolled back, but the eswitch
stayed on the list. A peer could later re-register, for example after its
own esw_offloads_disable()/esw_offloads_enable() cycle on a mode change or
reload. A new peer could also appear. Either one would call
mlx5_esw_offloads_devcom_event(PAIR) for this eswitch, and pairing could
succeed at that point.
Now the component is unregistered and esw->devcom is NULL. The only way to
recover is to re-enable offloads on this eswitch itself.
esw_offloads_enable() ignores the outcome, since devcom_init returns void,
and still returns 0. Nothing is reported or retried.
This seems to have these effects:
- mlx5_eswitch_is_peer() stays false for this eswitch.
- mlx5_sd_all_paired() cannot pass for socket-direct groups.
- mlx5_lag_shared_fdb_supported_filter() keeps rejecting shared FDB,
because mlx5_esw_offloads_devcom_is_ready() is false with a NULL
devcom.
The commit message says:
a registered component that reflects no actual pairing is incorrect
state to keep around.
Is that accurate? Registered but unpaired is already the normal state in
two cases:
- The first registrant: there are no peers, so send_event returns 0.
- The metadata mismatch path in mlx5_esw_offloads_devcom_event(): it
breaks out and returns 0.
Could the commit message mention that later peers can no longer retry the
pairing?
[Severity: High]
Can this lead to a NULL pointer dereference in
mlx5e_tc_query_route_vport()?
After this branch runs, esw->devcom is NULL but esw_offloads_enable() still
succeeds. In en_tc.c:
mlx5e_tc_query_route_vport() {
...
if (!mlx5_lag_is_active(out_priv->mdev))
return err;
rcu_read_lock();
err = -ENODEV;
mlx5_devcom_for_each_peer_entry_rcu(esw->devcom, esw, pos) {
...
}
The iterator dereferences devcom without a NULL check:
mlx5_devcom_get_next_peer_data_rcu() {
struct mlx5_devcom_comp *comp = devcom->comp;
...
}
LAG can be active without devcom pairing:
- mlx5_lag_check_prereq() does not look at devcom.
- mlx5_do_bond() falls back to non-shared-FDB MLX5_LAG_MODE_SRIOV when
mlx5_lag_shared_fdb_supported() is false. That is the case when devcom
is not ready.
The callers are mlx5e_set_vf_tunnel(), mlx5e_update_vf_tunnel() and
mlx5e_tc_tun_route_lookup(). They only check mlx5e_tc_is_vf_tunnel(),
which does not look at pairing state.
A possible sequence:
1. The initial PAIR fails.
2. The bond forms in non-shared-FDB SRIOV LAG.
3. A TC tunnel encap or decap rule is added whose route device is a
same-HW VF/SF that is not a vport of out_dev's eswitch.
Before this patch, esw->devcom was non-NULL in this scenario, with
comp->ready false. The iterator returned NULL and the function returned
-ENODEV.
[Severity: Medium]
Could this race with a representor open and cause a use-after-free of the
comp_dev?
This cleanup now runs at the end of esw_offloads_enable(). By then the
uplink rep and the PF/VF representor netdevs are already registered, via
mlx5_esw_offloads_rep_load() and mlx5_eswitch_enable_pf_vf_vports().
Userspace can open them while devcom_init is still running.
mlx5_esw_offloads_devcom_cleanup() calls
mlx5_devcom_unregister_component(). That does list_del under comp->sem,
then kfree() of the comp_dev. After that, esw->devcom = NULL is a plain
store, with no RCU grace period.
On the open side, the chain is:
mlx5e_rep_open() -> mlx5e_open_locked() -> mlx5e_rep_activate_channels()
-> mlx5e_add_sqs_fwd_rules() -> mlx5e_sqs2vport_start()
mlx5e_sqs2vport_start() reads esw->devcom holding only rtnl and
priv->state_lock:
mlx5e_sqs2vport_start() {
...
if (mlx5_devcom_comp_is_ready(esw->devcom) &&
mlx5_devcom_for_each_peer_begin(esw->devcom))
devcom_locked = true;
...
}
Suppose the opener loads esw->devcom and is preempted before it takes
down_read() on comp->sem. The other CPU can then finish the UNPAIR, the
list_del and the kfree(). When the opener resumes, it reads ->comp from
the freed comp_dev.
esw_offloads_disable() already has the same lockless reader versus kfree
pattern at teardown. This patch adds a new trigger during enable, right
after the reps have been registered.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically
2026-09-23 10:38 ` [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically Tariq Toukan
@ 2026-09-24 17:45 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:45 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 1 · Low: 0
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] v2p_map and the scratch `ports` buffer in mlx5_modify_lag() are
now sized ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS, where ldev->ports…
- [Medium] Before this patch, mlx5_modify_lag()
(drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c:861) used a zeroed…
--
net/mlx5: LAG, allocate v2p_map dynamically
This commit turns the fixed-size v2p_map array in struct mlx5_lag into a
pointer allocated in mlx5_lag_dev_alloc(). The new size uses ldev->ports
instead of MLX5_MAX_PORTS. The scratch ports[] buffer in mlx5_modify_lag()
moves from the stack to a kcalloc() of the same size.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> index c655f6e32e9b0..00b0159cb422f 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
[ ... ]
> @@ -330,11 +331,16 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
> if (!ldev)
> return NULL;
>
> + ldev->ports = MLX5_CAP_GEN(dev, num_lag_ports);
> + ldev->buckets = 1;
> + ldev->v2p_map = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
> + sizeof(*ldev->v2p_map), GFP_KERNEL);
[Severity: High]
Is ldev->ports the right bound for this allocation?
The users of v2p_map index it by the PF's slot in the ldev->pfs xarray, not
by a dense port number. For example, in mlx5_infer_tx_affinity_mapping():
mlx5_ldev_for_each(i, 0, ldev) {
for (j = 0; j < buckets; j++) {
idx = i * buckets + j;
ports[idx] = mlx5_lag_xa_to_dev_idx(ldev, i) + 1;
}
}
The slot comes from mlx5_ldev_add_mdev(), which allocates it from the full
MLX5_MAX_PORTS range:
err = xa_alloc(&ldev->pfs, &idx, pf, XA_LIMIT(0, MLX5_MAX_PORTS - 1),
GFP_KERNEL);
...
if (group_id)
return 0;
xa_set_mark(&ldev->pfs, idx, MLX5_LAG_XA_MARK_PORT);
sd_lag_init() adds Socket-Direct secondaries to the same xarray. They take
slots but never get the PORT mark. Given that, can a port PF end up at a
slot >= ldev->ports?
Here is an example. With num_lag_ports = 4, both buffers are now 64 bytes.
Say the SD primary takes slot 0 and its secondary takes slot 1. The fourth
port PF then lands in slot 4. Because ports > 2,
mlx5_lag_set_port_sel_mode() sets ldev->buckets to
MLX5_LAG_MAX_HASH_BUCKETS, so that PF would use indices 64..79.
Would this write past the end of v2p_map through
mlx5_activate_lag()->mlx5_infer_tx_affinity_mapping()? Would it also write
past the new ports[] buffer through mlx5_modify_lag()?
These readers look like they would go past the end the same way:
mlx5_modify_lag(), mlx5_lag_print_mapping(),
__mlx5_lag_modify_definers_destinations(),
mlx5_lag_create_port_sel_table() and mlx5_lag_get_slave_port(). The
out-of-bounds bytes would then be passed to mlx5_lag_pf_by_dev_idx() as
1-based device indices, and also used to program the port-selection table
and tx_remap_affinity.
Before this patch, both buffers were
MLX5_MAX_PORTS * MLX5_LAG_MAX_HASH_BUCKETS bytes, which covers every
possible slot. The rules[] array in struct mlx5_lag_definer (port_sel.h) is
indexed the same way and is still that size.
With this in mind, is the "No functional change" statement in the commit
message accurate? Should the buffers be sized by the slot range, or should
the indexing use the dense port sequence instead? Later patches in the
series still size both buffers by ldev->ports, so this does not appear to
be fixed later on.
> + if (!ldev->v2p_map)
> + goto err_v2p_map;
[ ... ]
> @@ -864,6 +873,12 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
> return;
>
> dev0 = mlx5_lag_pf(ldev, first_idx)->dev;
> +
> + ports = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
> + sizeof(*ports), GFP_KERNEL);
> + if (!ports)
> + return;
[Severity: Medium]
Before this patch, mlx5_modify_lag() could not fail once first_idx was
valid. Now, if this kcalloc() fails, the function returns without a
message and skips all of the following:
_mlx5_modify_lag()
the v2p_map update
mlx5_lag_drop_rule_setup()
the MLX5_DRIVER_EVENT_ACTIVE_BACKUP_LAG_CHANGE_LOWERSTATE notifier
The drop-rule setup and the notifier are skipped even when the mapping has
not changed.
mlx5_modify_lag() returns void, so its callers cannot see the failure or
retry. That covers mlx5_do_bond() (called from mlx5_do_bond_work()) and
mlx5_lag_set_port_affinity() in mp.c. By this point ldev->tracker already
holds the new lower state.
Could the hardware TX affinity then keep pointing at a failed port until
some unrelated bond event arrives?
The buffer is at most MLX5_MAX_PORTS * MLX5_LAG_MAX_HASH_BUCKETS = 128
bytes. Could it stay on the stack, or be preallocated with v2p_map, so this
path does not gain a new failure mode?
> +
> mlx5_infer_tx_affinity_mapping(tracker, ldev, ldev->buckets, ports);
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically
2026-09-23 10:38 ` [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically Tariq Toukan
@ 2026-09-24 17:45 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:45 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 2 · Low: 0 (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).
- [Medium] _mlx5_modify_lag()
(drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c) can now leave a…
- [Medium] net/mlx5: heap buffer overflow in MLX5 LAG configuration
arrays
Pre-existing issues:
- [Low] Concern: lag_active_port_bits() builds a bitmask of up to 8 bits
(MLX5_MAX_PORTS = 8), but mlx5_ifc_lagc_bits.active_port is only 4…
--
net/mlx5: LAG, allocate port-indexed scratch buffers dynamically
This patch replaces the MLX5_MAX_PORTS-sized stack arrays in
lag_active_port_bits(), mlx5_lag_print_mapping() and
mlx5_infer_tx_affinity_mapping() with kcalloc(ldev->ports, ...)
allocations. lag_active_port_bits() and mlx5_infer_tx_affinity_mapping()
now return int, and their callers get -ENOMEM on allocation failure.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> index 00b0159cb422f..93ead56641528 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
[ ... ]
> @@ -105,13 +111,21 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,
> MLX5_SET(lagc, lag_ctx, tx_remap_affinity_1, ports[idx0]);
> MLX5_SET(lagc, lag_ctx, tx_remap_affinity_2, ports[idx1]);
> break;
> - case MLX5_LAG_PORT_SELECT_MODE_PORT_SELECT_FT:
> + case MLX5_LAG_PORT_SELECT_MODE_PORT_SELECT_FT: {
> + u8 active_port;
> + int ret;
> +
> if (!MLX5_CAP_PORT_SELECTION(dev, port_select_flow_table_bypass))
> break;
>
> - MLX5_SET(lagc, lag_ctx, active_port,
> - lag_active_port_bits(ldev, tracker));
> + ret = lag_active_port_bits(ldev, tracker);
> + if (ret < 0)
> + return ret;
> +
> + active_port = ret;
> + MLX5_SET(lagc, lag_ctx, active_port, active_port);
[Severity: Low]
This is a pre-existing issue and was not introduced by this patch.
lag_active_port_bits() can build a mask up to 8 bits wide
(MLX5_MAX_PORTS is 8), but the lagc active_port field is only 4 bits wide:
include/linux/mlx5/mlx5_ifc.h:mlx5_ifc_lagc_bits {
...
u8 reserved_at_20[0xc];
u8 active_port[0x4];
...
}
MLX5_SET() masks the value to the field width. Bits 4..7 would be
silently dropped here, and also in mlx5_cmd_modify_active_port().
In hash LAG with port_select_flow_table_bypass, would ports 5..8 ever be
reported to FW as active? It's not clear whether FW offers bypass on
devices with more than 4 LAG ports.
> break;
> + }
> default:
> break;
> }
[ ... ]
> @@ -816,7 +857,10 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev,
> !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table_bypass))
> return ret;
>
> - active_ports = lag_active_port_bits(ldev, tracker);
> + ret = lag_active_port_bits(ldev, tracker);
> + if (ret < 0)
> + return ret;
[Severity: Medium]
Can a failed kcalloc() in lag_active_port_bits() leave a hash-based LAG
only partly reprogrammed?
By the time lag_active_port_bits() runs, mlx5_lag_port_sel_modify() has
already rewritten the FT rule destinations in hardware:
_mlx5_modify_lag()
mlx5_lag_port_sel_modify(ldev, ports)
mlx5_lag_modify_definers_destinations()
__mlx5_lag_modify_definers_destinations()
mlx5_modify_rule_destination()
lag_active_port_bits()
kcalloc() fails -> return -ENOMEM
If that happens, mlx5_cmd_modify_active_port() is never sent. The FW
active_port bitmask used by bypass traffic still lists the old active
uplinks, which could include a port whose link just went down.
mlx5_modify_lag() then takes the error path and skips the v2p_map update:
err = _mlx5_modify_lag(ldev, tracker, ports);
if (err) {
...
goto out;
}
memcpy(ldev->v2p_map, ports,
ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS);
Both mlx5_modify_lag() and __mlx5_lag_modify_definers_destinations()
skip entries where ports[idx] == v2p_map[idx]. Suppose the tracker later
returns to the state that matches the stale v2p_map. Would the FT rules
then stay on the intermediate mapping, with nothing to reprogram them?
A FW failure in mlx5_cmd_modify_active_port() could already cause this
before the patch. This patch adds a software failure point between the
two hardware steps.
The bitmask doesn't seem to need a scratch buffer. Could it be built
directly while walking mlx5_ldev_for_each()? Alternatively, could it be
computed before mlx5_lag_port_sel_modify() touches the hardware?
> + active_ports = ret;
>
> return mlx5_cmd_modify_active_port(dev0, active_ports);
> }
[ ... ]
> @@ -668,20 +696,28 @@ static bool __mlx5_lag_is_sd_active(struct mlx5_lag *ldev,
> * If there are ports that are disabled fill the relevant slots
> * with mapping that points to active ports.
> */
> -static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
> - struct mlx5_lag *ldev,
> - u8 buckets,
> - u8 *ports)
> +static int mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
> + struct mlx5_lag *ldev,
> + u8 buckets,
> + u8 *ports)
> {
> - int disabled[MLX5_MAX_PORTS] = {};
> - int enabled[MLX5_MAX_PORTS] = {};
> int disabled_ports_num = 0;
> int enabled_ports_num = 0;
> + int *disabled;
> + int *enabled;
> + int err = 0;
> int idx;
> u32 rand;
> int i;
> int j;
>
> + enabled = kcalloc(ldev->ports, sizeof(*enabled), GFP_KERNEL);
> + disabled = kcalloc(ldev->ports, sizeof(*disabled), GFP_KERNEL);
> + if (!enabled || !disabled) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> mlx5_ldev_for_each(i, 0, ldev) {
> if (tracker->netdev_state[i].tx_enabled &&
> tracker->netdev_state[i].link_up)
[Severity: Medium]
The old stack arrays were sized by MLX5_MAX_PORTS, which is also the
limit on how many entries ldev->pfs can hold. The new buffers are sized
by ldev->ports instead. However, the loops that fill them still walk
the xarray with mlx5_ldev_for_each() and index by a running count:
mlx5_infer_tx_affinity_mapping()
mlx5_ldev_for_each(i, 0, ldev)
enabled[enabled_ports_num++] = i;
or disabled[disabled_ports_num++] = i;
mlx5_infer_tx_enabled() / mlx5_infer_tx_disabled()
ports[(*num_enabled)++] = i;
None of these helpers checks the count against ldev->ports.
Is there anything that guarantees the number of PFs added to ldev->pfs
never exceeds ldev->ports (num_lag_ports)? Suppose devcom pairs more
functions sharing the same system image GUID than FW reported in
num_lag_ports. Then these buffers would be written past the end of
their kcalloc() allocations:
enabled[] and disabled[] in mlx5_infer_tx_affinity_mapping()
enabled_ports in lag_active_port_bits()
enabled_ports in mlx5_lag_print_mapping()
Before this patch the same situation stayed inside the MLX5_MAX_PORTS
stack arrays.
The same applies to buf in mlx5_lag_print_mapping(). It is now sized
ldev->ports * 10 + 1, but the non-hash branch emits one " port %d:%d"
per iterated port and bucket, so an extra PF would push the
scnprintf() writes past the allocation.
If the invariant holds, could it be enforced where a PF is added to
ldev->pfs? Alternatively, could these loops bound the count by
ldev->ports, so an unexpected extra PF can't turn into a heap
overwrite?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 06/13] net/mlx5: LAG, size debugfs buffers by port count
2026-09-23 10:38 ` [PATCH net-next 06/13] net/mlx5: LAG, size debugfs buffers by port count Tariq Toukan
@ 2026-09-24 17:45 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:45 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] mapping_show() in
drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c used a stack…
--
net/mlx5: LAG, size debugfs buffers by port count
This commit replaces the fixed-size ports[MLX5_MAX_PORTS] stack array in
mapping_show() with a buffer allocated by kcalloc(), sized by
ldev->ports.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> index e9b8d79d2d21e..44296789d6567 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
> @@ -101,14 +101,18 @@ static int flags_show(struct seq_file *file, void *priv)
> static int mapping_show(struct seq_file *file, void *priv)
> {
> struct mlx5_core_dev *dev = file->private;
> - u8 ports[MLX5_MAX_PORTS] = {};
> struct mlx5_lag *ldev;
> bool hash = false;
> bool lag_active;
> int i, idx = 0;
> int num_ports;
> + u8 *ports;
>
> ldev = mlx5_lag_dev(dev);
> + ports = kcalloc(ldev->ports, sizeof(*ports), GFP_KERNEL);
[Severity: Low]
Can the loops that fill this buffer write past ldev->ports entries?
Neither fill path is limited by ldev->ports. The non-hash path is:
mlx5_ldev_for_each(i, 0, ldev)
ports[idx++] = ldev->v2p_map[i];
and idx is never compared to the allocation size. The hash path passes
the buffer to mlx5_infer_tx_enabled(), which takes no length:
mlx5_ldev_for_each(i, 0, ldev)
if (tracker->netdev_state[i].tx_enabled &&
tracker->netdev_state[i].link_up)
ports[(*num_enabled)++] = i;
If no port is enabled, it falls back to mlx5_infer_tx_disabled(), which
also writes one entry per port. mlx5_ldev_for_each() goes through
mlx5_lag_for_each(), and that loop stops at tmp < MLX5_MAX_PORTS, not
at ldev->ports.
The two bounds come from different places:
mlx5_lag_dev_alloc():
ldev->ports = MLX5_CAP_GEN(dev, num_lag_ports);
mlx5_ldev_add_mdev():
err = xa_alloc(&ldev->pfs, &idx, pf, XA_LIMIT(0, MLX5_MAX_PORTS - 1),
GFP_KERNEL);
...
xa_set_mark(&ldev->pfs, idx, MLX5_LAG_XA_MARK_PORT);
ldev->ports is read from the first device's capability. The number of
PORT-marked entries in ldev->pfs is capped only by MLX5_MAX_PORTS. As far
as I can see, the only comparison against ldev->ports is
mlx5_lag_num_devs(ldev) != ldev->ports in mlx5_lag_check_prereq(), and
that runs only when LAG is activated.
The old stack array was safe by construction, because it and the xarray
limit were both MLX5_MAX_PORTS. The new buffer relies on an unstated
invariant: ldev->pfs never holds more port PFs than num_lag_ports.
If more port PFs than ldev->ports join the same ldev while LAG is
active, reading the debugfs mapping file would write past the requested
size. One such window is after a new PF is added and before bond work
tears LAG down. Since the count is capped at 8 and the smallest kmalloc
object is 8 bytes, the extra bytes stay inside the slab object. On
debug kernels this would still show up as a KASAN slab-out-of-bounds or
slub_debug redzone report.
The sibling commit "net/mlx5: LAG, allocate port-indexed scratch buffers
dynamically" makes the same assumption in lag_active_port_bits() and
mlx5_lag_print_mapping(). At the end of the series, mapping_show()
still uses kcalloc(ldev->ports) with the same fill loops.
Would it make sense to state the invariant, clamp the writes to
ldev->ports, or keep sizing the buffer by MLX5_MAX_PORTS?
> + if (!ports)
> + return -ENOMEM;
> +
> mutex_lock(&ldev->lock);
> lag_active = __mlx5_lag_is_active(ldev);
> if (lag_active) {
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow
2026-09-23 10:38 ` [PATCH net-next 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow Tariq Toukan
@ 2026-09-24 17:45 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:45 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 (2) · Low: 0
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).
- [Medium] This commit changes unpair-time cleanup in
mlx5e_tc_clean_fdb_peer_flows()…
Pre-existing issues:
- [Medium] mlx5e_tc_add_fdb_peer_flow()
(drivers/net/ethernet/mellanox/mlx5/core/en_tc.c) sets `int i =…
- [Medium] In mlx5e_add_fdb_flow()
(drivers/net/ethernet/mellanox/mlx5/core/en_tc.c),…
--
net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow
This patch moves the eswitch peer-flow reverse index from the origin flow
onto each duplicate. Each flow now has one peer list node and a peer_orig
back-reference. These replace the origin's peer[] array, the peer_used
bitmap and peer_index, while the per-peer list heads stay keyed by the LAG
sequence number until the next patch.
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> index b290beb4369ad..fae4f8625da4d 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
[ ... ]
> @@ -4639,12 +4631,11 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
> goto out;
> }
>
> - peer_flow->peer_index = i;
> + peer_flow->peer_orig = flow;
> list_add_tail(&peer_flow->peer_flows, &flow->peer_flows);
> flow_flag_set(flow, DUP);
> mutex_lock(&esw->offloads.peer_mutex);
> - list_add_tail(&flow->peer[i], &esw->offloads.peer_flows[i]);
> - set_bit(i, flow->peer_used);
> + list_add_tail(&peer_flow->peer, &esw->offloads.peer_flows[i]);
[Severity: Medium]
This isn't a bug introduced by this patch, but can i be negative here?
i is set at the top of mlx5e_tc_add_fdb_peer_flow():
int i = mlx5_lag_get_dev_seq(peer_esw->dev);
mlx5_lag_get_dev_seq() returns -ENOENT in three cases: there is no ldev,
no master matches the filter, or the device is not in the filtered LAG
set.
Nothing checks the range of i before it indexes the fixed
peer_flows[MLX5_MAX_PORTS] array in struct mlx5_esw_offload. With a
negative value, list_add_tail() would write list pointers into the fields
in front of that array.
mlx5e_tc_clean_fdb_peer_flows() does handle a negative return:
i = mlx5_lag_get_dev_seq(peer_esw->dev);
if (i < 0)
continue;
The old flow->peer[i] line used the same unchecked index. The code does
not make it clear whether a devcom-paired peer can be outside the filtered
LAG set when a flow is added.
The next patch in the series, "net/mlx5e: TC, track peer flows in a
vhca_id xarray", replaces this lookup with xa_load(&esw->offloads.peer_flows,
peer_vhca_id), so the negative index goes away.
> mutex_unlock(&esw->offloads.peer_mutex);
>
> out:
[Severity: Medium]
This is a pre-existing issue, but does the error path in the caller,
mlx5e_add_fdb_flow(), leak the origin struct mlx5e_tc_flow?
__mlx5e_add_fdb_flow() has already allocated the flow with refcnt 1. If
mlx5_devcom_for_each_peer_begin() fails or mlx5e_tc_add_fdb_peer_flow()
returns an error, the flow ends up here:
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c:mlx5e_add_fdb_flow() {
...
peer_clean:
mlx5e_tc_del_fdb_peers_flow(flow);
mlx5_devcom_for_each_peer_end(devcom);
clean_flow:
mlx5e_tc_del_fdb_flow(priv, flow);
return err;
}
mlx5e_tc_del_fdb_flow() frees the attrs and rules but not the flow
itself. Since *__flow is never set, mlx5e_configure_flower() does not own
the flow either.
For comparison, the err_free path in __mlx5e_add_fdb_flow() calls
mlx5e_flow_put().
This leak has existed since commit b1661efa4dbbd7 and is still there at
the end of this series.
[ ... ]
> @@ -5539,21 +5530,19 @@ int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags)
>
> void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
> {
[ ... ]
> mlx5_devcom_for_each_peer_entry(devcom, peer_esw, pos) {
> i = mlx5_lag_get_dev_seq(peer_esw->dev);
> if (i < 0)
> continue;
>
> - list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows[i], peer[i])
> - mlx5e_tc_del_fdb_peers_flow(flow);
> + list_for_each_entry_safe(peer_flow, tmp_peer_flow,
> + &esw->offloads.peer_flows[i], peer)
> + mlx5e_tc_del_fdb_peer_flow(peer_flow);
[Severity: Medium]
The commit message says "No functional change". Doesn't this change what
mlx5e_tc_clean_fdb_peer_flows() removes at unpair time?
Before this patch, every entry on esw->offloads.peer_flows[i] was an
origin flow. mlx5e_tc_del_fdb_peers_flow(flow) walked that origin's
peer_used bitmap and removed its duplicates for every peer index. That
included lists this loop never visited.
After this patch, each entry is a single duplicate linked through
peer_flow->peer, and mlx5e_tc_del_fdb_peer_flow(peer_flow) removes only
that duplicate.
The loop still picks the list from the peer's current
mlx5_lag_get_dev_seq(). The duplicate, however, was filed under the seq
returned when mlx5e_tc_add_fdb_peer_flow() ran.
Suppose that index no longer matches any current peer seq when
mlx5_esw_offloads_unpair() calls this function. The device may have been
renumbered, or the lookup may now return -ENOENT. Would those duplicates
be left behind? With three or more devices, the old per-origin sweep
would still have removed them.
A leftover duplicate would keep its FDB rules on the peer after pairing is
gone, and it would stay linked on flow->peer_flows. A later deletion of
the origin would then do:
mlx5e_tc_del_flow()
mlx5e_tc_del_fdb_peers_flow()
mlx5e_tc_del_fdb_peer_flow()
mlx5e_tc_del_fdb_flow(peer_flow->priv, peer_flow)
At that point the peer priv may already be unloaded.
The code does not make it clear whether the LAG seq can change before the
devcom unpair event, so this may not be reachable.
The next patch in the series, "net/mlx5e: TC, track peer flows in a
vhca_id xarray", keys the index by vhca_id and walks every entry with
xa_for_each(). That fixes the problem, so it only affects this
intermediate commit.
> }
> }
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray
2026-09-23 10:38 ` [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray Tariq Toukan
@ 2026-09-24 17:46 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:46 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.
Critical: 0 · High: 1 · Medium: 0 · 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] The patch adds an early return to mlx5e_tc_add_fdb_peer_flow()
(drivers/net/ethernet/mellanox/mlx5/core/en_tc.c).
- [Low] The commit message says "No functional change", but the patch
changes runtime behaviour in mlx5e_tc_add_fdb_peer_flow() in two ways.
--
net/mlx5e: TC, track peer flows in a vhca_id xarray
This commit replaces the fixed esw->offloads.peer_flows[MLX5_MAX_PORTS]
array of list heads with an xarray keyed by the peer vhca_id. Each entry
is a heap-allocated list head that is created at devcom pair time and
freed at unpair time. mlx5e_tc_add_fdb_peer_flow() now looks the entry up
with xa_load() and returns -ENODEV if it is missing.
> This lifts the MLX5_MAX_PORTS cap: the number
> of peers is bounded only by the number of distinct vhca_ids.
[ ... ]
> No functional change.
[Severity: Low]
This isn't a bug, but is "No functional change" accurate here?
The same message says the patch lifts the MLX5_MAX_PORTS cap. The patch
also adds a new -ENODEV failure path to mlx5e_tc_add_fdb_peer_flow() for a
peer that has no peer_flows xarray entry (see the comment below). Before
this patch, once __mlx5e_add_fdb_flow() succeeded, the duplicated flow was
always linked.
The old add path also used an unchecked index:
int i = mlx5_lag_get_dev_seq(peer_esw->dev);
...
list_add_tail(&peer_flow->peer, &esw->offloads.peer_flows[i]);
mlx5_lag_get_dev_seq() can return -ENOENT, and only
mlx5e_tc_clean_fdb_peer_flows() checked for i < 0. Removing that latent
out-of-bounds access also changes behavior.
Could the commit message describe these changes instead?
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> index fae4f8625da4d..c99e824b4c6dc 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
[ ... ]
> @@ -4634,8 +4635,11 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
> peer_flow->peer_orig = flow;
> list_add_tail(&peer_flow->peer_flows, &flow->peer_flows);
> flow_flag_set(flow, DUP);
> + dup_peer_flows = xa_load(&esw->offloads.peer_flows, peer_vhca_id);
> + if (!dup_peer_flows)
> + return -ENODEV;
[Severity: High]
What happens to peer_flow when xa_load() returns NULL here?
By this point __mlx5e_add_fdb_flow() has already offloaded the duplicate
rule to the peer FDB. peer_flow is on flow->peer_flows and DUP is set.
peer_flow->peer has not been linked, and it was never initialized either.
mlx5e_alloc_flow() allocates the flow with kzalloc_obj() and calls
INIT_LIST_HEAD() on peer_flows, hairpin, l3_to_l2_reformat, attrs and
encaps[], but not on peer. So peer.next and peer.prev are NULL.
The caller then unwinds like this:
mlx5e_add_fdb_flow()
mlx5e_tc_add_fdb_peer_flow() returns -ENODEV
goto peer_clean
mlx5e_tc_del_fdb_peers_flow(flow)
mlx5e_tc_del_fdb_peer_flow(peer_flow)
mutex_lock(&esw->offloads.peer_mutex);
list_del(&peer_flow->peer);
Can this list_del() dereference NULL? If it does, the oops happens while
peer_mutex and the devcom read semaphore are held. It also happens before
mlx5e_tc_del_fdb_flow() and clean_flow remove the peer and original FTEs
from hardware.
Can a NULL xa_load() actually happen? mlx5_devcom_for_each_peer_entry()
calls mlx5_devcom_get_next_peer_data(), which returns every registered
comp_dev with non-NULL data. It does not check esw->paired. A peer can be
registered without having a peer_flows entry in at least three ways:
- devcom_alloc_comp_dev() adds the new member to comp_dev_list_head before
mlx5_esw_offloads_devcom_init() sends ESW_OFFLOADS_DEVCOM_PAIR in a
separate locked section. An eswitch that is already paired can add a
rule in between.
- mlx5_esw_offloads_unpair() erases and frees the entry, but the member
stays on the list until mlx5_devcom_unregister_component(). comp->ready
also stays true while other pairs remain.
- mlx5_esw_offloads_devcom_event() skips pairing when the metadata setting
differs:
case ESW_OFFLOADS_DEVCOM_PAIR:
if (mlx5_eswitch_vport_match_metadata_enabled(esw) !=
mlx5_eswitch_vport_match_metadata_enabled(peer_esw))
break;
An admin can change that setting through the esw_port_metadata devlink
param. The peer then stays iterable with no xarray entry.
Before this patch the target was a statically initialized peer_flows[]
list head, so nothing could fail after the hardware write.
Would it make sense to do the xa_load() before __mlx5e_add_fdb_flow(), or
otherwise make sure peer_flow->peer is valid on this error path?
> mutex_lock(&esw->offloads.peer_mutex);
> - list_add_tail(&peer_flow->peer, &esw->offloads.peer_flows[i]);
> + list_add_tail(&peer_flow->peer, dup_peer_flows);
> mutex_unlock(&esw->offloads.peer_mutex);
>
> out:
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net-next 09/13] net/mlx5: E-switch, derive manager vport from device capability
2026-09-23 10:38 ` [PATCH net-next 09/13] net/mlx5: E-switch, derive manager vport from device capability Tariq Toukan
@ 2026-09-24 17:46 ` netdev-bot+sashiko
0 siblings, 0 replies; 22+ messages in thread
From: netdev-bot+sashiko @ 2026-09-24 17:46 UTC (permalink / raw)
To: tariqt
Cc: andrew+netdev, davem, edumazet, kuba, netdev, pabeni,
agoldberger, cratiu, gal, leon, linux-kernel, linux-rdma, mbloch,
moshe, ohartoov, saeedm, shayd
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 0
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).
- [Medium] mlx5_eswitch_manager_vport() (include/linux/mlx5/eswitch.h)
now returns MLX5_CAP_ESW(dev, esw_manager_vport_number) when…
--
net/mlx5: E-switch, derive manager vport from device capability
The commit changes mlx5_eswitch_manager_vport() to return
esw_manager_vport_number from the e-switch capability when
esw_manager_vport_number_valid is set, so the manager vport can be a VF.
The old ECPF/host PF mapping stays as the fallback. mlx5_esw_vports_init()
now allocates the manager vport if it is not already in esw->vports.
> diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h
> index a0dd162baa787..03d3620141c83 100644
> --- a/include/linux/mlx5/eswitch.h
> +++ b/include/linux/mlx5/eswitch.h
> @@ -222,6 +222,9 @@ static inline bool is_mdev_switchdev_mode(struct mlx5_core_dev *dev)
> /* The returned number is valid only when the dev is eswitch manager. */
> static inline u16 mlx5_eswitch_manager_vport(struct mlx5_core_dev *dev)
> {
> + if (MLX5_CAP_ESW(dev, esw_manager_vport_number_valid))
> + return MLX5_CAP_ESW(dev, esw_manager_vport_number);
> +
[Severity: Medium]
Do the SW and HW steering backends agree with this new manager vport
number?
esw->manager_vport can now be a vport N that is neither ECPF nor 0. Both
steering backends still hardcode the old rule to decide which vport is
the manager.
In steering/hws/vport.c:
static bool hws_vport_is_esw_mgr_vport(struct mlx5hws_context *ctx, u16 vport)
{
return ctx->caps->is_ecpf ? vport == MLX5_VPORT_ECPF :
vport == MLX5_VPORT_HOST_PF;
}
In steering/sws/dr_domain.c, dr_domain_is_esw_mgr_vport():
return (caps->is_ecpf && vport == MLX5_VPORT_ECPF) ||
(!caps->is_ecpf && vport == 0);
The e-switch core passes esw->manager_vport to these backends as a
destination or source port, with no vhca_id. Some examples:
eswitch_offloads.c (slow-path miss):
dest.vport.num = esw->manager_vport;
rdma.c:
mlx5_esw_set_spec_source_port(esw, esw->manager_vport, spec);
The peer and slave miss rules in eswitch_offloads.c and en/tc/sample.c
also use it.
If firmware reports a manager vport N other than ECPF/0, it looks like
mlx5dr_domain_get_vport_cap() and mlx5hws_vport_get_gvmi() would treat N
as a foreign vport. They would not return the cached esw_manager_caps or
esw_manager_gvmi. Instead they would go through dr_domain_add_vport_cap()
or hws_vport_add_gvmi(), which query N with other_vport or other_function
set to true. Vport 0 would also keep mapping to the local manager's
caps/gvmi.
Could this lead to failed rule creation, or to rules that resolve to the
wrong vport context, when SMFS or HMFS steering is used on such a device?
HWS already reads the capability in mlx5hws_cmd_query_caps() into
caps->eswitch_manager_vport_number, and hws/table.c and hws/action.c use
it. hws/vport.c does not. Should hws_vport_is_esw_mgr_vport() and
dr_domain_is_esw_mgr_vport() be updated to use the capability-reported
number as well?
The later patches in this series don't appear to touch the steering
backends. Current firmware probably reports numbers that match the
hardcoded mapping, so this would only show up with firmware that reports
a VF as the e-switch manager.
> return mlx5_core_is_ecpf_esw_manager(dev) ?
> MLX5_VPORT_ECPF : MLX5_VPORT_HOST_PF;
> }
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260923103830.1183-1-tariqt%40nvidia.com
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-09-24 17:46 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 10:38 [PATCH net-next 00/13] net/mlx5: Preparations for nested E-switch Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC address Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 02/13] net/mlx5: E-switch, do not leave an unpaired devcom registered Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 05/13] net/mlx5: LAG, drop per-port scratch array in drop-rule setup Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 06/13] net/mlx5: LAG, size debugfs buffers by port count Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow Tariq Toukan
2026-09-24 17:45 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray Tariq Toukan
2026-09-24 17:46 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 09/13] net/mlx5: E-switch, derive manager vport from device capability Tariq Toukan
2026-09-24 17:46 ` netdev-bot+sashiko
2026-09-23 10:38 ` [PATCH net-next 10/13] net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 11/13] net/mlx5: LAG, drop stale esw_shared_ingress_acl gate from shared FDB Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 12/13] net/mlx5: E-switch, correct stale VF/PF wording in esw-allowed comments Tariq Toukan
2026-09-23 10:38 ` [PATCH net-next 13/13] net/mlx5: E-switch, disable host functions for a non PF e-switch manager Tariq Toukan
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®