mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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; 14+ 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] 14+ 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-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, 0 replies; 14+ 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] 14+ 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-23 10:38 ` [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically Tariq Toukan
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ 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] 14+ 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-23 10:38 ` [PATCH net-next 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically Tariq Toukan
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ 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] 14+ 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-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, 0 replies; 14+ 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] 14+ 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; 14+ 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] 14+ 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-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, 0 replies; 14+ 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] 14+ 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-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, 0 replies; 14+ 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] 14+ 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-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, 0 replies; 14+ 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] 14+ 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-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, 0 replies; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ 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; 14+ 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] 14+ messages in thread

end of thread, other threads:[~2026-09-23 10:41 UTC | newest]

Thread overview: 14+ 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-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 ` [PATCH net-next 03/13] net/mlx5: LAG, allocate v2p_map dynamically Tariq Toukan
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 ` [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-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 ` [PATCH net-next 08/13] net/mlx5e: TC, track peer flows in a vhca_id xarray Tariq Toukan
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 ` [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®