From cfda5de640534855cc0a602f0eeb91a7e0c75764 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:18 +0300 Subject: [PATCH 01/13] net/mlx5e: Assign a random MAC to any netdev with a zero MAC 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. Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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 4c6060d54fbe..f90b5a34e097 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; From 11dac22ca06323148a387f71aef2a9885d5efa10 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:19 +0300 Subject: [PATCH 02/13] 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. Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-3-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../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) From d9f3227f673b7d5aa0bdb822a2cc6be17e10f3e0 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:20 +0300 Subject: [PATCH 03/13] net/mlx5: LAG, allocate v2p_map dynamically 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-4-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../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 dd14cdc378de..6321f985c071 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; From 4138820bdddaf61c260952c49da692c9456f3d37 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:21 +0300 Subject: [PATCH 04/13] net/mlx5: LAG, allocate port-indexed scratch buffers dynamically 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-5-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../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 6321f985c071..a7c316121c26 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); From fbc4f422c5b86cffed759890f31b335adae9a27e Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:22 +0300 Subject: [PATCH 05/13] net/mlx5: LAG, drop per-port scratch array in drop-rule setup 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-6-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../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 a7c316121c26..78505e59bf9d 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); } } From 6c244dd5ba6b3d7ee55a4e85d81ad27083de3d6b Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:23 +0300 Subject: [PATCH 06/13] net/mlx5: LAG, size debugfs buffers by port count 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-7-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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; } From 675956237b3818c3a9deeabf7be678cf31699e72 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:24 +0300 Subject: [PATCH 07/13] net/mlx5e: TC, anchor peer-flow reverse index on the duplicated flow 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-8-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../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); } } From 9d608a40c2d400ef7529e13c165278053ea0d51c Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:25 +0300 Subject: [PATCH 08/13] 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. 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-9-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../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) From d451adde0203fa62519eb224be87046b27c76b99 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:26 +0300 Subject: [PATCH 09/13] net/mlx5: E-switch, derive manager vport from device capability 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-10-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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; } From c51ea6db00e6b872143a10aecf8449f23865965c Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:27 +0300 Subject: [PATCH 10/13] net/mlx5: LAG, don't print port mapping to debugfs in MPESW mode 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-11-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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, From 66b25da0cfed831be0ec1536dc7499e232030236 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:28 +0300 Subject: [PATCH 11/13] net/mlx5: LAG, drop stale esw_shared_ingress_acl gate from shared FDB 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-12-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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 424040918fa3..7c7e6c832a93 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; From dd655b1e93c149bc0a42c1432870898475f8c4cf Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:29 +0300 Subject: [PATCH 12/13] net/mlx5: E-switch, correct stale VF/PF wording in esw-allowed comments 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-13-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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); From a5bfbdfebd5353a4113539342e800610606a8153 Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 23 Sep 2026 13:38:30 +0300 Subject: [PATCH 13/13] net/mlx5: E-switch, disable host functions for a non PF e-switch manager 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 Reviewed-by: Moshe Shemesh Reviewed-by: Akiva Goldberger Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260923103830.1183-14-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- 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;