AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/11/05 18:30 flow patch-triage
7m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies the Mellanox mlx5 core driver (specifically virt LAG and eswitch offload logic). Mellanox ConnectX/BlueField PCIe network devices require vendor-specific physical hardware that is not emulated in standard virtualized QEMU or GCE fuzzing environments. Consequently, the driver cannot bind to any device, making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

1/1 2026/11/05 18:30 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 1406b515a69b4efca577c7d8754f446fd765e58a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Nov 5 18:30:20 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c\nindex 8818f65d1fbc4c..79adb1ecc4506f 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c\n@@ -63,6 +63,11 @@ int mlx5e_devlink_port_register(struct mlx5e_dev *mlx5e_dev,\n \t\tdl_port_index = mlx5_esw_vport_to_devlink_port_index(mdev,\n \t\t\t\t\t\t\t\t     MLX5_VPORT_UPLINK);\n \t} else {\n+\t\tif (MLX5_ESWITCH_MANAGER(mdev)) {\n+\t\t\tmlx5e_devlink_get_port_parent_id(mdev, \u0026ppid);\n+\t\t\tmemcpy(attrs.switch_id.id, ppid.id, ppid.id_len);\n+\t\t\tattrs.switch_id.id_len = ppid.id_len;\n+\t\t}\n \t\tattrs.flavour = DEVLINK_PORT_FLAVOUR_VIRTUAL;\n \t\tdl_port_index = mlx5_esw_vport_to_devlink_port_index(mdev, 0);\n \t}\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h\nindex e9cbcd73b23ffa..55f5ea522d4655 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h\n@@ -957,7 +957,8 @@ bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 cont\n int mlx5_eswitch_offloads_vport_lag_add_one(struct mlx5_eswitch *master_esw,\n \t\t\t\t\t    struct mlx5_eswitch *slave_esw);\n void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw,\n-\t\t\t\t\t     struct mlx5_eswitch *slave_esw);\n+\t\t\t\t\t     struct mlx5_eswitch *slave_esw,\n+\t\t\t\t\t     int max_slaves);\n int mlx5_eswitch_offloads_single_fdb_add_one(struct mlx5_eswitch *master_esw,\n \t\t\t\t\t     struct mlx5_eswitch *slave_esw, int max_slaves);\n void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw,\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c\nindex c712848b202db4..f1a5d7d036cb5c 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c\n@@ -3000,6 +3000,26 @@ static int esw_set_slave_root_fdb(struct mlx5_core_dev *master,\n \treturn err;\n }\n \n+static void mlx5_esw_set_spec_source_port_vhca(struct mlx5_flow_spec *spec,\n+\t\t\t\t\t       u16 vport, u16 vhca_id)\n+{\n+\tvoid *misc;\n+\n+\tspec-\u003ematch_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;\n+\n+\tmisc = MLX5_ADDR_OF(fte_match_param, spec-\u003ematch_value,\n+\t\t\t    misc_parameters);\n+\tMLX5_SET(fte_match_set_misc, misc, source_port, vport);\n+\tMLX5_SET(fte_match_set_misc, misc, source_eswitch_owner_vhca_id,\n+\t\t vhca_id);\n+\n+\tmisc = MLX5_ADDR_OF(fte_match_param, spec-\u003ematch_criteria,\n+\t\t\t    misc_parameters);\n+\tMLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);\n+\tMLX5_SET_TO_ONES(fte_match_set_misc, misc,\n+\t\t\t source_eswitch_owner_vhca_id);\n+}\n+\n static int __esw_set_master_egress_rule(struct mlx5_core_dev *master,\n \t\t\t\t\tstruct mlx5_core_dev *slave,\n \t\t\t\t\tstruct mlx5_vport *vport,\n@@ -3011,22 +3031,22 @@ static int __esw_set_master_egress_rule(struct mlx5_core_dev *master,\n \tstruct mlx5_flow_act flow_act = {};\n \tstruct mlx5_flow_spec *spec;\n \tint err = 0;\n-\tvoid *misc;\n+\n+\tif (mlx5_virt_lag_is_supported(master) \u0026\u0026\n+\t    !mlx5_eswitch_vport_match_metadata_enabled(slave-\u003epriv.eswitch))\n+\t\treturn -EINVAL;\n \n \tspec = kvzalloc_obj(*spec);\n \tif (!spec)\n \t\treturn -ENOMEM;\n \n-\tspec-\u003ematch_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;\n-\tmisc = MLX5_ADDR_OF(fte_match_param, spec-\u003ematch_value,\n-\t\t\t    misc_parameters);\n-\tMLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_UPLINK);\n-\tMLX5_SET(fte_match_set_misc, misc, source_eswitch_owner_vhca_id, slave_index);\n-\n-\tmisc = MLX5_ADDR_OF(fte_match_param, spec-\u003ematch_criteria, misc_parameters);\n-\tMLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);\n-\tMLX5_SET_TO_ONES(fte_match_set_misc, misc,\n-\t\t\t source_eswitch_owner_vhca_id);\n+\tif (mlx5_virt_lag_is_supported(master) \u0026\u0026\n+\t    mlx5_eswitch_vport_match_metadata_enabled(slave-\u003epriv.eswitch))\n+\t\tmlx5_esw_set_spec_source_port(slave-\u003epriv.eswitch,\n+\t\t\t\t\t      MLX5_VPORT_UPLINK, spec);\n+\telse\n+\t\tmlx5_esw_set_spec_source_port_vhca(spec, MLX5_VPORT_UPLINK,\n+\t\t\t\t\t\t   slave_index);\n \n \tflow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;\n \tdest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;\n@@ -3179,6 +3199,21 @@ static void esw_unset_slave_egress_rule(struct mlx5_core_dev *master,\n \tesw_slave_egress_destroy_resources(slave_vport);\n }\n \n+static void mlx5_esw_set_flow_group_source_port_vhca(u32 *flow_group_in)\n+{\n+\tvoid *match_criteria = MLX5_ADDR_OF(create_flow_group_in,\n+\t\t\t\t\t    flow_group_in, match_criteria);\n+\n+\tMLX5_SET_TO_ONES(fte_match_param, match_criteria,\n+\t\t\t misc_parameters.source_port);\n+\tMLX5_SET_TO_ONES(fte_match_param, match_criteria,\n+\t\t\t misc_parameters.source_eswitch_owner_vhca_id);\n+\tMLX5_SET(create_flow_group_in, flow_group_in,\n+\t\t match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS);\n+\tMLX5_SET(create_flow_group_in, flow_group_in,\n+\t\t source_eswitch_owner_vhca_id_valid, 1);\n+}\n+\n static int esw_master_egress_create_resources(struct mlx5_eswitch *esw,\n \t\t\t\t\t      struct mlx5_flow_namespace *egress_ns,\n \t\t\t\t\t      struct mlx5_vport *vport, size_t count)\n@@ -3189,13 +3224,16 @@ static int esw_master_egress_create_resources(struct mlx5_eswitch *esw,\n \t};\n \tstruct mlx5_flow_table *acl;\n \tstruct mlx5_flow_group *g;\n-\tvoid *match_criteria;\n \tu32 *flow_group_in;\n \tint err;\n \n \tif (vport-\u003eegress.acl)\n \t\treturn 0;\n \n+\tif (mlx5_virt_lag_is_supported(esw-\u003edev) \u0026\u0026\n+\t    !mlx5_eswitch_vport_match_metadata_enabled(esw))\n+\t\treturn -EINVAL;\n+\n \tflow_group_in = kvzalloc(inlen, GFP_KERNEL);\n \tif (!flow_group_in)\n \t\treturn -ENOMEM;\n@@ -3209,17 +3247,11 @@ static int esw_master_egress_create_resources(struct mlx5_eswitch *esw,\n \t\tgoto out;\n \t}\n \n-\tmatch_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in,\n-\t\t\t\t      match_criteria);\n-\tMLX5_SET_TO_ONES(fte_match_param, match_criteria,\n-\t\t\t misc_parameters.source_port);\n-\tMLX5_SET_TO_ONES(fte_match_param, match_criteria,\n-\t\t\t misc_parameters.source_eswitch_owner_vhca_id);\n-\tMLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,\n-\t\t MLX5_MATCH_MISC_PARAMETERS);\n-\n-\tMLX5_SET(create_flow_group_in, flow_group_in,\n-\t\t source_eswitch_owner_vhca_id_valid, 1);\n+\tif (mlx5_virt_lag_is_supported(esw-\u003edev) \u0026\u0026\n+\t    mlx5_eswitch_vport_match_metadata_enabled(esw))\n+\t\tmlx5_esw_set_flow_group_source_port(esw, flow_group_in, 0);\n+\telse\n+\t\tmlx5_esw_set_flow_group_source_port_vhca(flow_group_in);\n \tMLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);\n \tMLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, count);\n \n@@ -3349,12 +3381,32 @@ void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw,\n int mlx5_eswitch_offloads_vport_lag_add_one(struct mlx5_eswitch *master_esw,\n \t\t\t\t\t    struct mlx5_eswitch *slave_esw)\n {\n-\treturn esw_set_slave_egress_rule(master_esw-\u003edev, slave_esw-\u003edev);\n+\tint err;\n+\n+\terr = esw_set_slave_egress_rule(master_esw-\u003edev, slave_esw-\u003edev);\n+\tif (err)\n+\t\treturn err;\n+\n+\tesw_unset_master_egress_rule(master_esw-\u003edev, slave_esw-\u003edev);\n+\n+\treturn 0;\n }\n \n void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw,\n-\t\t\t\t\t     struct mlx5_eswitch *slave_esw)\n+\t\t\t\t\t     struct mlx5_eswitch *slave_esw,\n+\t\t\t\t\t     int max_slaves)\n {\n+\tint err;\n+\n+\tif (mlx5_sd_is_primary(slave_esw-\u003edev)) {\n+\t\terr = esw_set_master_egress_rule(master_esw-\u003edev,\n+\t\t\t\t\t\t slave_esw-\u003edev, max_slaves);\n+\t\tif (err)\n+\t\t\tesw_warn(master_esw-\u003edev,\n+\t\t\t\t \"Failed to restore master egress rule (%d)\\n\",\n+\t\t\t\t err);\n+\t}\n+\n \tesw_unset_slave_egress_rule(master_esw-\u003edev, slave_esw-\u003edev);\n }\n \n@@ -3573,6 +3625,10 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,\n \txa_init(\u0026esw-\u003eoffloads.peer_flows);\n \tmutex_init(\u0026esw-\u003eoffloads.peer_mutex);\n \n+\t/* do not pair a virt LAG capable VF/SF that is not part of a LAG */\n+\tif (!mlx5_core_is_pf(esw-\u003edev) \u0026\u0026 mlx5_virt_lag_nonmember(esw-\u003edev))\n+\t\treturn;\n+\n \tif (!MLX5_CAP_ESW(esw-\u003edev, merged_eswitch))\n \t\treturn;\n \n@@ -3647,6 +3703,48 @@ static u32 mlx5_esw_match_metadata_reserved(struct mlx5_eswitch *esw)\n \treturn MLX5_ESW_METADATA_RSVD_UPLINK;\n }\n \n+/* Nested (virt LAG) source-port metadata layout: the upper ESW_NESTED_IDX_BITS\n+ * hold the VF/SF's slot in the LAG and the lower ESW_NESTED_VPORT_BITS hold a\n+ * vport id unique within its eswitch. The two fields still sum to\n+ * ESW_SOURCE_PORT_METADATA_BITS. The LAG slot (0-254, per the xa_alloc() limit\n+ * in mlx5_ldev_add_mdev()) fits in ESW_NESTED_IDX_BITS; slots 0xf0 and up would\n+ * drive the PFNUM bits to the 0xf reserved by internal port offload, so only\n+ * 0-239 are used.\n+ */\n+#define ESW_NESTED_VPORT_BITS 8\n+#define ESW_NESTED_IDX_BITS (ESW_SOURCE_PORT_METADATA_BITS - ESW_NESTED_VPORT_BITS)\n+\n+static u32 mlx5_nested_esw_match_metadata_alloc(struct mlx5_eswitch *esw)\n+{\n+\tu32 vport_end_ida = (1 \u003c\u003c ESW_NESTED_VPORT_BITS) - 1;\n+\tu32 max_idx = (1 \u003c\u003c ESW_NESTED_IDX_BITS) -\n+\t\t      (1 \u003c\u003c (ESW_NESTED_IDX_BITS - ESW_PFNUM_BITS)) - 1;\n+\tint idx;\n+\tint id;\n+\n+\tidx = mlx5_lag_get_member_idx(esw-\u003edev);\n+\tif (idx \u003c 0 || idx \u003e max_idx)\n+\t\treturn 0;\n+\n+\t/* Use only non-zero id's (2-255). */\n+\tid = ida_alloc_range(\u0026esw-\u003eoffloads.vport_metadata_ida,\n+\t\t\t     MLX5_ESW_METADATA_RSVD_UPLINK + 1,\n+\t\t\t     vport_end_ida, GFP_KERNEL);\n+\tif (id \u003c 0)\n+\t\treturn 0;\n+\tid = (idx \u003c\u003c ESW_NESTED_VPORT_BITS) | id;\n+\treturn id;\n+}\n+\n+static void mlx5_nested_esw_match_metadata_free(struct mlx5_eswitch *esw,\n+\t\t\t\t\t\tu32 metadata)\n+{\n+\tu32 vport_bit_mask = (1 \u003c\u003c ESW_NESTED_VPORT_BITS) - 1;\n+\n+\t/* Metadata holds only ESW_NESTED_VPORT_BITS of actual ida id. */\n+\tida_free(\u0026esw-\u003eoffloads.vport_metadata_ida, metadata \u0026 vport_bit_mask);\n+}\n+\n u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw)\n {\n \tu32 vport_end_ida = (1 \u003c\u003c ESW_VPORT_BITS) - 1;\n@@ -3655,6 +3753,9 @@ u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw)\n \tint pf_num;\n \tint id;\n \n+\tif (!mlx5_core_is_pf(esw-\u003edev) \u0026\u0026 !mlx5_virt_lag_nonmember(esw-\u003edev))\n+\t\treturn mlx5_nested_esw_match_metadata_alloc(esw);\n+\n \t/* Only 4 bits of pf_num */\n \tpf_num = mlx5_sd_pf_num_get(esw-\u003edev);\n \tif (pf_num \u003c 0 || pf_num \u003e max_pf_num)\n@@ -3675,6 +3776,9 @@ void mlx5_esw_match_metadata_free(struct mlx5_eswitch *esw, u32 metadata)\n {\n \tu32 vport_bit_mask = (1 \u003c\u003c ESW_VPORT_BITS) - 1;\n \n+\tif (!mlx5_core_is_pf(esw-\u003edev) \u0026\u0026 !mlx5_virt_lag_nonmember(esw-\u003edev))\n+\t\treturn mlx5_nested_esw_match_metadata_free(esw, metadata);\n+\n \t/* Metadata contains only 12 bits of actual ida id */\n \tida_free(\u0026esw-\u003eoffloads.vport_metadata_ida, metadata \u0026 vport_bit_mask);\n }\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c\nindex b6125c7bd91bb6..116432cb7ce53c 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c\n@@ -154,7 +154,7 @@ static int members_show(struct seq_file *file, void *priv)\n \tmutex_lock(\u0026ldev-\u003elock);\n \tmlx5_ldev_for_each(i, 0, ldev)\n \t\tseq_printf(file, \"%s\\n\",\n-\t\t\t   dev_name(mlx5_lag_pf(ldev, i)-\u003edev-\u003edevice));\n+\t\t\t   dev_name(mlx5_lag_fn(ldev, i)-\u003edev-\u003edevice));\n \tmutex_unlock(\u0026ldev-\u003elock);\n \n \treturn 0;\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c\nindex 24086267b4a7f5..5532b2505161c7 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c\n@@ -39,6 +39,7 @@\n #include \"lib/mlx5.h\"\n #include \"lib/devcom.h\"\n #include \"mlx5_core.h\"\n+#include \"sf/dev/dev.h\"\n #include \"eswitch.h\"\n #include \"esw/acl/ofld.h\"\n #include \"lag.h\"\n@@ -94,23 +95,25 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,\n \tint port_sel_mode = get_port_sel_mode(mode, flags);\n \tu32 in[MLX5_ST_SZ_DW(create_lag_in)] = {};\n \tu8 *ports = ldev-\u003ev2p_map;\n-\tint idx0, idx1;\n \tvoid *lag_ctx;\n \n \tlag_ctx = MLX5_ADDR_OF(create_lag_in, in, ctx);\n \tMLX5_SET(create_lag_in, in, opcode, MLX5_CMD_OP_CREATE_LAG);\n \tMLX5_SET(lagc, lag_ctx, fdb_selection_mode, fdb_sel_mode);\n-\tidx0 = mlx5_lag_get_dev_index_by_seq(ldev, 0);\n-\tidx1 = mlx5_lag_get_dev_index_by_seq(ldev, 1);\n+\tswitch (port_sel_mode) {\n+\tcase MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY: {\n+\t\tint idx0, idx1;\n \n-\tif (idx0 \u003c 0 || idx1 \u003c 0)\n-\t\treturn -EINVAL;\n+\t\tidx0 = mlx5_lag_get_dev_index_by_seq(ldev, 0);\n+\t\tidx1 = mlx5_lag_get_dev_index_by_seq(ldev, 1);\n+\n+\t\tif (idx0 \u003c 0 || idx1 \u003c 0)\n+\t\t\treturn -EINVAL;\n \n-\tswitch (port_sel_mode) {\n-\tcase MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY:\n \t\tMLX5_SET(lagc, lag_ctx, tx_remap_affinity_1, ports[idx0]);\n \t\tMLX5_SET(lagc, lag_ctx, tx_remap_affinity_2, ports[idx1]);\n \t\tbreak;\n+\t}\n \tcase MLX5_LAG_PORT_SELECT_MODE_PORT_SELECT_FT: {\n \t\tu8 active_port;\n \t\tint ret;\n@@ -158,23 +161,25 @@ static int mlx5_cmd_modify_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,\n static u32 mlx5_lag_dev_group_id(struct mlx5_core_dev *dev)\n {\n \tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tif (!ldev)\n \t\treturn 0;\n \n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev == dev)\n-\t\t\treturn pf-\u003esd_fdb_active ? pf-\u003egroup_id : 0;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev == dev)\n+\t\t\treturn fn-\u003esd_fdb_active ? fn-\u003egroup_id : 0;\n \t}\n \treturn 0;\n }\n \n-static int mlx5_lag_is_sw_lag(struct mlx5_core_dev *dev)\n+bool mlx5_lag_is_sw_managed(struct mlx5_core_dev *dev)\n {\n-\treturn mlx5_lag_is_sd(dev);\n+\tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n+\n+\treturn mlx5_lag_is_sd(dev) || (ldev \u0026\u0026 ldev-\u003evirt_lag);\n }\n \n int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev)\n@@ -183,7 +188,7 @@ int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev)\n \tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n \tint ret;\n \n-\tif (mlx5_lag_is_sw_lag(dev)) {\n+\tif (mlx5_lag_is_sw_managed(dev)) {\n \t\tif (!ldev)\n \t\t\treturn -ENODEV;\n \n@@ -205,7 +210,7 @@ int mlx5_cmd_destroy_vport_lag(struct mlx5_core_dev *dev)\n \tu32 in[MLX5_ST_SZ_DW(destroy_vport_lag_in)] = {};\n \tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n \n-\tif (mlx5_lag_is_sw_lag(dev)) {\n+\tif (mlx5_lag_is_sw_managed(dev)) {\n \t\tif (!ldev)\n \t\t\treturn 0;\n \n@@ -310,7 +315,7 @@ static void mlx5_do_bond_work(struct work_struct *work);\n static void mlx5_ldev_free(struct kref *ref)\n {\n \tstruct mlx5_lag *ldev = container_of(ref, struct mlx5_lag, ref);\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tstruct net *net;\n \tint i;\n \n@@ -320,16 +325,16 @@ static void mlx5_ldev_free(struct kref *ref)\n \t}\n \n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003eport_change_nb.nb.notifier_call) {\n-\t\t\tstruct mlx5_nb *nb = \u0026pf-\u003eport_change_nb;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003eport_change_nb.nb.notifier_call) {\n+\t\t\tstruct mlx5_nb *nb = \u0026fn-\u003eport_change_nb;\n \n-\t\t\tmlx5_eq_notifier_unregister(pf-\u003edev, nb);\n+\t\t\tmlx5_eq_notifier_unregister(fn-\u003edev, nb);\n \t\t}\n-\t\txa_erase(\u0026ldev-\u003epfs, i);\n-\t\tkfree(pf);\n+\t\txa_erase(\u0026ldev-\u003efns, i);\n+\t\tkfree(fn);\n \t}\n-\txa_destroy(\u0026ldev-\u003epfs);\n+\txa_destroy(\u0026ldev-\u003efns);\n \n \tmlx5_lag_mp_cleanup(ldev);\n \tcancel_delayed_work_sync(\u0026ldev-\u003ebond_work);\n@@ -361,6 +366,7 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)\n \n \tldev-\u003eports = MLX5_CAP_GEN(dev, num_lag_ports);\n \tldev-\u003ebuckets = 1;\n+\tldev-\u003evirt_lag = mlx5_virt_lag_is_supported(dev);\n \tldev-\u003ev2p_map = kcalloc(ldev-\u003eports * MLX5_LAG_MAX_HASH_BUCKETS,\n \t\t\t\tsizeof(*ldev-\u003ev2p_map), GFP_KERNEL);\n \tif (!ldev-\u003ev2p_map)\n@@ -372,11 +378,11 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)\n \n \tkref_init(\u0026ldev-\u003eref);\n \tmutex_init(\u0026ldev-\u003elock);\n-\txa_init_flags(\u0026ldev-\u003epfs, XA_FLAGS_ALLOC);\n+\txa_init_flags(\u0026ldev-\u003efns, XA_FLAGS_ALLOC);\n \tINIT_DELAYED_WORK(\u0026ldev-\u003ebond_work, mlx5_do_bond_work);\n \tINIT_WORK(\u0026ldev-\u003espeed_update_work, mlx5_mpesw_speed_update_work);\n \n-\tif (!mlx5_sd_is_supported(dev)) {\n+\tif (!mlx5_sd_is_supported(dev) \u0026\u0026 !ldev-\u003evirt_lag) {\n \t\tldev-\u003enb.notifier_call = mlx5_lag_netdev_event;\n \t\twrite_pnet(\u0026ldev-\u003enet, mlx5_core_net(dev));\n \t\tif (register_netdevice_notifier_net(read_pnet(\u0026ldev-\u003enet),\n@@ -392,6 +398,7 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)\n \t\tmlx5_core_err(dev, \"Failed to init multipath lag err=%d\\n\",\n \t\t\t      err);\n \n+\tldev-\u003emax_fns = ldev-\u003eports;\n \treturn ldev;\n \n err_wq:\n@@ -404,12 +411,12 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)\n int mlx5_lag_dev_get_netdev_idx(struct mlx5_lag *ldev,\n \t\t\t\tstruct net_device *ndev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003enetdev == ndev)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003enetdev == ndev)\n \t\t\treturn i;\n \t}\n \n@@ -424,7 +431,7 @@ static int mlx5_lag_get_master_idx(struct mlx5_lag *ldev)\n \tif (!ldev)\n \t\treturn -ENOENT;\n \n-\tentry = xa_find(\u0026ldev-\u003epfs, \u0026idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);\n+\tentry = xa_find(\u0026ldev-\u003efns, \u0026idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);\n \tif (!entry)\n \t\treturn -ENOENT;\n \n@@ -462,15 +469,17 @@ int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq)\n /* Return the appropriate iterator filter for a device in LAG:\n  * - SD shared FDB active: iterate only the device's SD group\n  * - SD group exists but shared FDB not active: iterate all devices\n- * - No SD: iterate ports only\n+ * - Virt LAG: iterate all devices, since its members are not marked as\n+ *   ports, as they do not represent a physical port\n+ * - Otherwise: iterate ports only\n  */\n static u32 mlx5_lag_get_filter(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)\n {\n-\tstruct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);\n+\tstruct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);\n \n-\tif (pf \u0026\u0026 pf-\u003esd_fdb_active)\n-\t\treturn pf-\u003egroup_id;\n-\tif (pf \u0026\u0026 pf-\u003egroup_id)\n+\tif (fn \u0026\u0026 fn-\u003esd_fdb_active)\n+\t\treturn fn-\u003egroup_id;\n+\tif (fn \u0026\u0026 (fn-\u003egroup_id || ldev-\u003evirt_lag))\n \t\treturn MLX5_LAG_FILTER_ALL;\n \treturn MLX5_LAG_FILTER_PORTS;\n }\n@@ -483,7 +492,7 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev)\n {\n \tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n \tint master_idx, i, num = 1;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tu32 filter;\n \n \tif (!ldev)\n@@ -494,15 +503,15 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev)\n \tif (master_idx \u003c 0)\n \t\treturn -ENOENT;\n \n-\tpf = mlx5_lag_pf(ldev, master_idx);\n-\tif (pf \u0026\u0026 pf-\u003edev == dev)\n+\tfn = mlx5_lag_fn(ldev, master_idx);\n+\tif (fn \u0026\u0026 fn-\u003edev == dev)\n \t\treturn 0;\n \n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n \t\tif (i == master_idx)\n \t\t\tcontinue;\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev == dev)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev == dev)\n \t\t\treturn num;\n \t\tnum++;\n \t}\n@@ -510,6 +519,23 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev)\n }\n EXPORT_SYMBOL(mlx5_lag_get_dev_seq);\n \n+int mlx5_lag_get_member_idx(struct mlx5_core_dev *dev)\n+{\n+\tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n+\tstruct lag_fn *fn;\n+\tint idx;\n+\n+\tif (!ldev)\n+\t\treturn -ENOENT;\n+\n+\txa_lock(\u0026ldev-\u003efns);\n+\tfn = mlx5_lag_fn_by_dev(ldev, dev);\n+\tidx = fn ? fn-\u003eidx : -ENOENT;\n+\txa_unlock(\u0026ldev-\u003efns);\n+\n+\treturn idx;\n+}\n+\n /* seq 0 = master, then all remaining devices */\n static int mlx5_lag_get_dev_index_by_seq_all(struct mlx5_lag *ldev, int seq)\n {\n@@ -540,7 +566,7 @@ static int mlx5_lag_get_dev_index_by_seq_group(struct mlx5_lag *ldev, int seq,\n \tint i, num = 0;\n \n \tmlx5_lag_for_each(i, 0, ldev, group_id) {\n-\t\tif (xa_get_mark(\u0026ldev-\u003epfs, i, MLX5_LAG_XA_MARK_PORT)) {\n+\t\tif (xa_get_mark(\u0026ldev-\u003efns, i, MLX5_LAG_XA_MARK_PORT)) {\n \t\t\tif (seq == 0)\n \t\t\t\treturn i;\n \t\t\tnum++;\n@@ -549,7 +575,7 @@ static int mlx5_lag_get_dev_index_by_seq_group(struct mlx5_lag *ldev, int seq,\n \t}\n \n \tmlx5_lag_for_each(i, 0, ldev, group_id) {\n-\t\tif (xa_get_mark(\u0026ldev-\u003epfs, i, MLX5_LAG_XA_MARK_PORT))\n+\t\tif (xa_get_mark(\u0026ldev-\u003efns, i, MLX5_LAG_XA_MARK_PORT))\n \t\t\tcontinue;\n \t\tif (num == seq)\n \t\t\treturn i;\n@@ -580,14 +606,14 @@ int mlx5_lag_get_dev_index_by_seq_filter(struct mlx5_lag *ldev, int seq,\n static void mlx5_lag_mark_master(struct mlx5_lag *ldev)\n {\n \tint lowest_dev_idx = INT_MAX;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint master_xa_idx = -1;\n \tint dev_idx;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tdev_idx = mlx5_get_dev_index(pf-\u003edev);\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tdev_idx = mlx5_get_dev_index(fn-\u003edev);\n \t\tif (dev_idx \u003c lowest_dev_idx) {\n \t\t\tlowest_dev_idx = dev_idx;\n \t\t\tmaster_xa_idx = i;\n@@ -595,19 +621,55 @@ static void mlx5_lag_mark_master(struct mlx5_lag *ldev)\n \t}\n \n \tif (master_xa_idx \u003e= 0)\n-\t\txa_set_mark(\u0026ldev-\u003epfs, master_xa_idx, MLX5_LAG_XA_MARK_MASTER);\n+\t\txa_set_mark(\u0026ldev-\u003efns, master_xa_idx, MLX5_LAG_XA_MARK_MASTER);\n }\n \n-static void mlx5_lag_clear_master(struct mlx5_lag *ldev)\n+static u64 mlx5_lag_member_tiebreak(struct mlx5_core_dev *dev)\n+{\n+\tstruct pci_dev *pdev = dev-\u003epdev;\n+\n+\tif (mlx5_core_is_sf(dev))\n+\t\treturn mlx5_sf_coredev_sfnum(dev);\n+\n+\treturn ((u64)pci_domain_nr(pdev-\u003ebus) \u003c\u003c 16) | pci_dev_id(pdev);\n+}\n+\n+void mlx5_virt_lag_mark_master(struct mlx5_lag *ldev)\n+{\n+\tu64 lowest_tiebreak = U64_MAX;\n+\tint lowest_dev_idx = INT_MAX;\n+\tint master_xa_idx = -1;\n+\tstruct lag_fn *fn;\n+\tu64 tiebreak;\n+\tint dev_idx;\n+\tint i;\n+\n+\tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tdev_idx = mlx5_get_dev_index(fn-\u003edev);\n+\t\ttiebreak = mlx5_lag_member_tiebreak(fn-\u003edev);\n+\t\tif (dev_idx \u003c lowest_dev_idx ||\n+\t\t    (dev_idx == lowest_dev_idx \u0026\u0026 tiebreak \u003c lowest_tiebreak)) {\n+\t\t\tlowest_dev_idx = dev_idx;\n+\t\t\tlowest_tiebreak = tiebreak;\n+\t\t\tmaster_xa_idx = i;\n+\t\t}\n+\t}\n+\n+\tif (master_xa_idx \u003e= 0)\n+\t\txa_set_mark(\u0026ldev-\u003efns, master_xa_idx, MLX5_LAG_XA_MARK_MASTER);\n+}\n+\n+void mlx5_lag_clear_master(struct mlx5_lag *ldev)\n {\n \tunsigned long idx = 0;\n \tvoid *entry;\n \n-\tentry = xa_find(\u0026ldev-\u003epfs, \u0026idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);\n+\tentry = xa_find(\u0026ldev-\u003efns, \u0026idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);\n \tif (!entry)\n \t\treturn;\n \n-\txa_clear_mark(\u0026ldev-\u003epfs, idx, MLX5_LAG_XA_MARK_MASTER);\n+\txa_clear_mark(\u0026ldev-\u003efns, idx, MLX5_LAG_XA_MARK_MASTER);\n }\n \n /* Devcom event handler to manage LAG master marking */\n@@ -618,7 +680,7 @@ static int mlx5_lag_devcom_event(int event, void *my_data, void *event_data)\n \tint idx;\n \n \tldev = mlx5_lag_dev(dev);\n-\tif (!ldev)\n+\tif (!ldev || ldev-\u003evirt_lag)\n \t\treturn 0;\n \n \tmutex_lock(\u0026ldev-\u003elock);\n@@ -658,15 +720,15 @@ int mlx5_lag_num_devs(struct mlx5_lag *ldev)\n \n int mlx5_lag_num_netdevs(struct mlx5_lag *ldev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i, num = 0;\n \n \tif (!ldev)\n \t\treturn 0;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003enetdev)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003enetdev)\n \t\t\tnum++;\n \t}\n \treturn num;\n@@ -685,9 +747,9 @@ static bool __mlx5_lag_is_sriov(struct mlx5_lag *ldev)\n static bool __mlx5_lag_is_sd_active(struct mlx5_lag *ldev,\n \t\t\t\t    struct mlx5_core_dev *dev)\n {\n-\tstruct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);\n+\tstruct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);\n \n-\treturn pf \u0026\u0026 pf-\u003esd_fdb_active;\n+\treturn fn \u0026\u0026 fn-\u003esd_fdb_active;\n }\n \n /* Create a mapping between steering slots and active ports.\n@@ -762,12 +824,12 @@ static int mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,\n \n static bool mlx5_lag_has_drop_rule(struct mlx5_lag *ldev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003ehas_drop)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003ehas_drop)\n \t\t\treturn true;\n \t}\n \treturn false;\n@@ -775,17 +837,19 @@ static bool mlx5_lag_has_drop_rule(struct mlx5_lag *ldev)\n \n static void mlx5_lag_drop_rule_cleanup(struct mlx5_lag *ldev)\n {\n-\tstruct lag_func *pf;\n+\tstruct mlx5_eswitch *esw;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (!pf-\u003ehas_drop)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (!fn-\u003ehas_drop)\n \t\t\tcontinue;\n \n-\t\tmlx5_esw_acl_ingress_vport_drop_rule_destroy(pf-\u003edev-\u003epriv.eswitch,\n+\t\tesw = fn-\u003edev-\u003epriv.eswitch;\n+\t\tmlx5_esw_acl_ingress_vport_drop_rule_destroy(esw,\n \t\t\t\t\t\t\t     MLX5_VPORT_UPLINK);\n-\t\tpf-\u003ehas_drop = false;\n+\t\tfn-\u003ehas_drop = false;\n \t}\n }\n \n@@ -793,7 +857,7 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,\n \t\t\t\t     struct lag_tracker *tracker)\n {\n \tstruct mlx5_core_dev *dev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint err;\n \tint i;\n \n@@ -809,12 +873,12 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,\n \t\tif (tracker-\u003enetdev_state[i].tx_enabled \u0026\u0026\n \t\t    tracker-\u003enetdev_state[i].link_up)\n \t\t\tcontinue;\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tdev = pf-\u003edev;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tdev = fn-\u003edev;\n \t\terr = mlx5_esw_acl_ingress_vport_drop_rule_create(dev-\u003epriv.eswitch,\n \t\t\t\t\t\t\t\t  MLX5_VPORT_UPLINK);\n \t\tif (!err)\n-\t\t\tpf-\u003ehas_drop = true;\n+\t\t\tfn-\u003ehas_drop = true;\n \t\telse\n \t\t\tmlx5_core_err(dev,\n \t\t\t\t      \"Failed to create lag drop rule, error: %d\",\n@@ -848,7 +912,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev,\n \tif (idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, idx)-\u003edev;\n \tif (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, \u0026ldev-\u003emode_flags)) {\n \t\tret = mlx5_lag_port_sel_modify(ldev, ports);\n \t\tif (ret ||\n@@ -868,7 +932,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev,\n static struct net_device *mlx5_lag_active_backup_get_netdev(struct mlx5_core_dev *dev)\n {\n \tstruct net_device *ndev = NULL;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tstruct mlx5_lag *ldev;\n \tunsigned long flags;\n \tint i, last_idx;\n@@ -880,16 +944,16 @@ static struct net_device *mlx5_lag_active_backup_get_netdev(struct mlx5_core_dev\n \t\tgoto unlock;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n+\t\tfn = mlx5_lag_fn(ldev, i);\n \t\tif (ldev-\u003etracker.netdev_state[i].tx_enabled)\n-\t\t\tndev = pf-\u003enetdev;\n+\t\t\tndev = fn-\u003enetdev;\n \t}\n \tif (!ndev) {\n \t\tlast_idx = mlx5_lag_get_dev_index_by_seq(ldev, ldev-\u003eports - 1);\n \t\tif (last_idx \u003c 0)\n \t\t\tgoto unlock;\n-\t\tpf = mlx5_lag_pf(ldev, last_idx);\n-\t\tndev = pf-\u003enetdev;\n+\t\tfn = mlx5_lag_fn(ldev, last_idx);\n+\t\tndev = fn-\u003enetdev;\n \t}\n \n \tdev_hold(ndev);\n@@ -914,7 +978,7 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,\n \tif (first_idx \u003c 0)\n \t\treturn;\n \n-\tdev0 = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \n \tports = kcalloc(ldev-\u003eports * MLX5_LAG_MAX_HASH_BUCKETS,\n \t\t\tsizeof(*ports), GFP_KERNEL);\n@@ -982,7 +1046,7 @@ static int mlx5_lag_set_port_sel_mode(struct mlx5_lag *ldev,\n \t    mode == MLX5_LAG_MODE_MULTIPATH)\n \t\treturn 0;\n \n-\tdev0 = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \n \tif (!MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table)) {\n \t\tif (ldev-\u003eports \u003e 2)\n@@ -1040,7 +1104,7 @@ static int mlx5_create_lag(struct mlx5_lag *ldev,\n \tif (first_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \tif (tracker)\n \t\tmlx5_lag_print_mapping(dev0, ldev, tracker, flags);\n \tmlx5_core_info(dev0, \"shared_fdb:%d mode:%s\\n\",\n@@ -1088,7 +1152,7 @@ int mlx5_activate_lag(struct mlx5_lag *ldev,\n \tif (master_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, master_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, master_idx)-\u003edev;\n \terr = mlx5_lag_set_flags(ldev, mode, tracker, shared_fdb, \u0026flags);\n \tif (err)\n \t\treturn err;\n@@ -1151,7 +1215,7 @@ int mlx5_deactivate_lag(struct mlx5_lag *ldev)\n \tif (master_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, master_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, master_idx)-\u003edev;\n \tldev-\u003emode = MLX5_LAG_MODE_NONE;\n \tldev-\u003emode_flags = 0;\n \tmlx5_lag_mp_reset(ldev);\n@@ -1192,7 +1256,7 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)\n \tstruct mlx5_core_dev *dev;\n \tu8 mode;\n #endif\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tbool roce_support;\n \tint i;\n \n@@ -1201,35 +1265,35 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)\n \n #ifdef CONFIG_MLX5_ESWITCH\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tdev = pf-\u003edev;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tdev = fn-\u003edev;\n \t\tif (mlx5_eswitch_num_vfs(dev-\u003epriv.eswitch) \u0026\u0026 !is_mdev_switchdev_mode(dev))\n \t\t\treturn false;\n \t}\n \n-\tpf = mlx5_lag_pf(ldev, master_idx);\n-\tdev = pf-\u003edev;\n+\tfn = mlx5_lag_fn(ldev, master_idx);\n+\tdev = fn-\u003edev;\n \tmode = mlx5_eswitch_mode(dev);\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (mlx5_eswitch_mode(pf-\u003edev) != mode)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (mlx5_eswitch_mode(fn-\u003edev) != mode)\n \t\t\treturn false;\n \t}\n \n #else\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (mlx5_sriov_is_enabled(pf-\u003edev))\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (mlx5_sriov_is_enabled(fn-\u003edev))\n \t\t\treturn false;\n \t}\n #endif\n-\tpf = mlx5_lag_pf(ldev, master_idx);\n-\troce_support = mlx5_get_roce_state(pf-\u003edev);\n+\tfn = mlx5_lag_fn(ldev, master_idx);\n+\troce_support = mlx5_get_roce_state(fn-\u003edev);\n \tmlx5_ldev_for_each(i, 0, ldev) {\n \t\tif (i == master_idx)\n \t\t\tcontinue;\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (mlx5_get_roce_state(pf-\u003edev) != roce_support)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (mlx5_get_roce_state(fn-\u003edev) != roce_support)\n \t\t\treturn false;\n \t}\n \n@@ -1239,19 +1303,19 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)\n static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev, u32 filter)\n {\n \tstruct mlx5_devcom_comp_dev *devcom = NULL;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tlockdep_assert_held(\u0026ldev-\u003elock);\n \n \ti = mlx5_get_next_lag_func(ldev, 0, filter);\n-\tif (i \u003c MLX5_MAX_PORTS) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n+\tif (i \u003c ldev-\u003emax_fns) {\n+\t\tfn = mlx5_lag_fn(ldev, i);\n \t\tif (filter == MLX5_LAG_FILTER_PORTS ||\n \t\t    filter == MLX5_LAG_FILTER_ALL)\n-\t\t\tdevcom = pf-\u003edev-\u003epriv.hca_devcom_comp;\n+\t\t\tdevcom = fn-\u003edev-\u003epriv.hca_devcom_comp;\n \t\telse\n-\t\t\tdevcom = mlx5_sd_get_devcom(pf-\u003edev);\n+\t\t\tdevcom = mlx5_sd_get_devcom(fn-\u003edev);\n \t}\n \tmlx5_devcom_comp_assert_locked(devcom);\n }\n@@ -1297,28 +1361,30 @@ void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev,\n static void mlx5_lag_rescan_devices_locked_filter(struct mlx5_lag *ldev,\n \t\t\t\t\t\t  bool enable, u32 filter)\n {\n-\tstruct mlx5_core_dev *devs[MLX5_MAX_PORTS];\n-\tstruct lag_func *pf;\n-\tint num_devs = 0;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_lag_assert_locked_transition(ldev, filter);\n \n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev-\u003epriv.flags \u0026 MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev-\u003epriv.flags \u0026 MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)\n \t\t\tcontinue;\n \n \t\tif (enable)\n-\t\t\tpf-\u003edev-\u003epriv.flags \u0026= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;\n+\t\t\tfn-\u003edev-\u003epriv.flags \u0026= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;\n \t\telse\n-\t\t\tpf-\u003edev-\u003epriv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;\n-\t\tdevs[num_devs++] = pf-\u003edev;\n+\t\t\tfn-\u003edev-\u003epriv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;\n \t}\n \n \tmlx5_lag_drop_lock_for_reps(ldev, filter);\n-\tfor (i = 0; i \u003c num_devs; i++)\n-\t\tmlx5_rescan_drivers_locked(devs[i]);\n+\tmlx5_lag_for_each(i, 0, ldev, filter) {\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev-\u003epriv.flags \u0026 MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)\n+\t\t\tcontinue;\n+\n+\t\tmlx5_rescan_drivers_locked(fn-\u003edev);\n+\t}\n \tmlx5_lag_retake_lock_after_reps(ldev);\n }\n \n@@ -1345,14 +1411,14 @@ void mlx5_lag_remove_devices(struct mlx5_lag *ldev)\n static int mlx5_lag_reload_ib_reps_idx(struct mlx5_lag *ldev, int idx,\n \t\t\t\t       u32 flags)\n {\n-\tstruct lag_func *pf = mlx5_lag_pf(ldev, idx);\n+\tstruct lag_fn *fn = mlx5_lag_fn(ldev, idx);\n \tstruct mlx5_eswitch *esw;\n \tint ret;\n \n-\tif (pf-\u003edev-\u003epriv.flags \u0026 flags)\n+\tif (fn-\u003edev-\u003epriv.flags \u0026 flags)\n \t\treturn 0;\n \n-\tesw = pf-\u003edev-\u003epriv.eswitch;\n+\tesw = fn-\u003edev-\u003epriv.eswitch;\n \tmlx5_esw_reps_block(esw);\n \tret = mlx5_eswitch_reload_ib_reps(esw);\n \tmlx5_esw_reps_unblock(esw);\n@@ -1413,14 +1479,14 @@ int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags,\n \n static void mlx5_lag_unload_reps_unlocked(struct mlx5_lag *ldev, u32 filter)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n \t\tstruct mlx5_eswitch *esw;\n \n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tesw = pf-\u003edev-\u003epriv.eswitch;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tesw = fn-\u003edev-\u003epriv.eswitch;\n \t\tmlx5_esw_reps_block(esw);\n \t\tmlx5_eswitch_unload_reps(esw);\n \t\tmlx5_esw_reps_unblock(esw);\n@@ -1454,7 +1520,7 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)\n \t\treturn;\n \t}\n \n-\tdev0 = mlx5_lag_pf(ldev, idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, idx)-\u003edev;\n \troce_lag = __mlx5_lag_is_roce(ldev);\n \n \tif (roce_lag) {\n@@ -1462,7 +1528,7 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)\n \t\tmlx5_ldev_for_each(i, 0, ldev) {\n \t\t\tif (i == idx)\n \t\t\t\tcontinue;\n-\t\t\tmlx5_nic_vport_disable_roce(mlx5_lag_pf(ldev, i)-\u003edev);\n+\t\t\tmlx5_nic_vport_disable_roce(mlx5_lag_fn(ldev, i)-\u003edev);\n \t\t}\n \t}\n \n@@ -1477,18 +1543,18 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)\n static bool mlx5_lag_is_roce_lag(struct mlx5_lag *ldev)\n {\n \tbool roce_lag = true;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\troce_lag = roce_lag \u0026\u0026 !mlx5_sriov_is_enabled(pf-\u003edev);\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\troce_lag = roce_lag \u0026\u0026 !mlx5_sriov_is_enabled(fn-\u003edev);\n \t}\n \n #ifdef CONFIG_MLX5_ESWITCH\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\troce_lag = roce_lag \u0026\u0026 is_mdev_legacy_mode(pf-\u003edev);\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\troce_lag = roce_lag \u0026\u0026 is_mdev_legacy_mode(fn-\u003edev);\n \t}\n #endif\n \n@@ -1517,15 +1583,15 @@ static int mlx5_lag_get_devices_oper_speed(struct mlx5_lag *ldev,\n \tmlx5_ldev_for_each(pf_idx, 0, ldev) {\n \t\tu8 opmod = MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT;\n \t\tstruct mlx5_core_dev *pf_mdev;\n-\t\tstruct lag_func *pf;\n+\t\tstruct lag_fn *fn;\n \t\tu32 speed;\n \t\tu8 state;\n \t\tint ret;\n \n-\t\tpf = mlx5_lag_pf(ldev, pf_idx);\n-\t\tif (!pf)\n+\t\tfn = mlx5_lag_fn(ldev, pf_idx);\n+\t\tif (!fn)\n \t\t\tcontinue;\n-\t\tpf_mdev = pf-\u003edev;\n+\t\tpf_mdev = fn-\u003edev;\n \t\tif (!pf_mdev)\n \t\t\tcontinue;\n \t\tret = mlx5_query_vport_max_tx_speed(pf_mdev, opmod, 0, 0,\n@@ -1564,14 +1630,14 @@ static int mlx5_lag_get_devices_max_speed(struct mlx5_lag *ldev, u32 *max_speed)\n \t*max_speed = 0;\n \tmlx5_ldev_for_each(pf_idx, 0, ldev) {\n \t\tstruct mlx5_core_dev *pf_mdev;\n-\t\tstruct lag_func *pf;\n+\t\tstruct lag_fn *fn;\n \t\tu32 speed;\n \t\tint ret;\n \n-\t\tpf = mlx5_lag_pf(ldev, pf_idx);\n-\t\tif (!pf)\n+\t\tfn = mlx5_lag_fn(ldev, pf_idx);\n+\t\tif (!fn)\n \t\t\tcontinue;\n-\t\tpf_mdev = pf-\u003edev;\n+\t\tpf_mdev = fn-\u003edev;\n \t\tif (!pf_mdev)\n \t\t\tcontinue;\n \n@@ -1631,7 +1697,7 @@ static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev,\n void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)\n {\n \tstruct mlx5_core_dev *mdev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tu32 speed;\n \tint pf_idx;\n \n@@ -1651,10 +1717,10 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)\n \tspeed = speed / MLX5_MAX_TX_SPEED_UNIT;\n \n \tmlx5_ldev_for_each(pf_idx, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, pf_idx);\n-\t\tif (!pf)\n+\t\tfn = mlx5_lag_fn(ldev, pf_idx);\n+\t\tif (!fn)\n \t\t\tcontinue;\n-\t\tmdev = pf-\u003edev;\n+\t\tmdev = fn-\u003edev;\n \t\tif (!mdev)\n \t\t\tcontinue;\n \n@@ -1665,16 +1731,16 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)\n void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev)\n {\n \tstruct mlx5_core_dev *mdev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tu32 speed;\n \tint pf_idx;\n \tint ret;\n \n \tmlx5_ldev_for_each(pf_idx, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, pf_idx);\n-\t\tif (!pf)\n+\t\tfn = mlx5_lag_fn(ldev, pf_idx);\n+\t\tif (!fn)\n \t\t\tcontinue;\n-\t\tmdev = pf-\u003edev;\n+\t\tmdev = fn-\u003edev;\n \t\tif (!mdev)\n \t\t\tcontinue;\n \n@@ -1705,7 +1771,7 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)\n \tif (idx \u003c 0)\n \t\treturn;\n \n-\tdev0 = mlx5_lag_pf(ldev, idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, idx)-\u003edev;\n \tif (!mlx5_lag_is_ready(ldev)) {\n \t\tdo_bond = false;\n \t} else {\n@@ -1750,7 +1816,7 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)\n \t\t\t\tmlx5_ldev_for_each(i, 0, ldev) {\n \t\t\t\t\tif (i == idx)\n \t\t\t\t\t\tcontinue;\n-\t\t\t\t\tdev = mlx5_lag_pf(ldev, i)-\u003edev;\n+\t\t\t\t\tdev = mlx5_lag_fn(ldev, i)-\u003edev;\n \t\t\t\t\tif (mlx5_get_roce_state(dev))\n \t\t\t\t\t\tmlx5_nic_vport_enable_roce(dev);\n \t\t\t\t}\n@@ -1784,14 +1850,17 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)\n struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev)\n {\n \tstruct mlx5_devcom_comp_dev *devcom = NULL;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n+\tu32 filter;\n \tint i;\n \n \tmutex_lock(\u0026ldev-\u003elock);\n-\ti = mlx5_get_next_lag_func(ldev, 0, MLX5_LAG_FILTER_PORTS);\n-\tif (i \u003c MLX5_MAX_PORTS) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tdevcom = pf-\u003edev-\u003epriv.hca_devcom_comp;\n+\tfilter = ldev-\u003evirt_lag ? MLX5_LAG_FILTER_ALL :\n+\t\tMLX5_LAG_FILTER_PORTS;\n+\ti = mlx5_get_next_lag_func(ldev, 0, filter);\n+\tif (i \u003c ldev-\u003emax_fns) {\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tdevcom = fn-\u003edev-\u003epriv.hca_devcom_comp;\n \t}\n \tmutex_unlock(\u0026ldev-\u003elock);\n \treturn devcom;\n@@ -1799,7 +1868,7 @@ struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev)\n \n static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev,\n \t\t\t\t     struct mlx5_flow_table_attr *ft_attr,\n-\t\t\t\t     struct lag_func *pf)\n+\t\t\t\t     struct lag_fn *fn)\n {\n #ifdef CONFIG_MLX5_ESWITCH\n \tstruct mlx5_flow_namespace *ns;\n@@ -1810,20 +1879,20 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev,\n \tif (!ns)\n \t\treturn 0;\n \n-\tpf-\u003elag_demux_ft = mlx5_create_flow_table(ns, ft_attr);\n-\tif (IS_ERR(pf-\u003elag_demux_ft))\n-\t\treturn PTR_ERR(pf-\u003elag_demux_ft);\n+\tfn-\u003elag_demux_ft = mlx5_create_flow_table(ns, ft_attr);\n+\tif (IS_ERR(fn-\u003elag_demux_ft))\n+\t\treturn PTR_ERR(fn-\u003elag_demux_ft);\n \n \tfg = mlx5_esw_lag_demux_fg_create(dev-\u003epriv.eswitch,\n-\t\t\t\t\t  pf-\u003elag_demux_ft);\n+\t\t\t\t\t  fn-\u003elag_demux_ft);\n \tif (IS_ERR(fg)) {\n \t\terr = PTR_ERR(fg);\n-\t\tmlx5_destroy_flow_table(pf-\u003elag_demux_ft);\n-\t\tpf-\u003elag_demux_ft = NULL;\n+\t\tmlx5_destroy_flow_table(fn-\u003elag_demux_ft);\n+\t\tfn-\u003elag_demux_ft = NULL;\n \t\treturn err;\n \t}\n \n-\tpf-\u003elag_demux_fg = fg;\n+\tfn-\u003elag_demux_fg = fg;\n \treturn 0;\n #else\n \treturn -EOPNOTSUPP;\n@@ -1832,7 +1901,7 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev,\n \n static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev,\n \t\t\t\t  struct mlx5_flow_table_attr *ft_attr,\n-\t\t\t\t  struct lag_func *pf)\n+\t\t\t\t  struct lag_fn *fn)\n {\n \tstruct mlx5_flow_namespace *ns;\n \tint err;\n@@ -1841,12 +1910,12 @@ static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev,\n \tif (!ns)\n \t\treturn 0;\n \n-\tpf-\u003elag_demux_fg = NULL;\n+\tfn-\u003elag_demux_fg = NULL;\n \tft_attr-\u003emax_fte = 1;\n-\tpf-\u003elag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr);\n-\tif (IS_ERR(pf-\u003elag_demux_ft)) {\n-\t\terr = PTR_ERR(pf-\u003elag_demux_ft);\n-\t\tpf-\u003elag_demux_ft = NULL;\n+\tfn-\u003elag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr);\n+\tif (IS_ERR(fn-\u003elag_demux_ft)) {\n+\t\terr = PTR_ERR(fn-\u003elag_demux_ft);\n+\t\tfn-\u003elag_demux_ft = NULL;\n \t\treturn err;\n \t}\n \n@@ -1857,7 +1926,7 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev,\n \t\t\tstruct mlx5_flow_table_attr *ft_attr)\n {\n \tstruct mlx5_lag *ldev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \n \tif (!ft_attr)\n \t\treturn -EINVAL;\n@@ -1866,16 +1935,16 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev,\n \tif (!ldev)\n \t\treturn -ENODEV;\n \n-\tpf = mlx5_lag_pf_by_dev(ldev, dev);\n-\tif (!pf)\n+\tfn = mlx5_lag_fn_by_dev(ldev, dev);\n+\tif (!fn)\n \t\treturn -ENODEV;\n \n-\txa_init(\u0026pf-\u003elag_demux_rules);\n+\txa_init(\u0026fn-\u003elag_demux_rules);\n \n-\tif (mlx5_lag_is_sw_lag(dev))\n-\t\treturn mlx5_lag_demux_ft_fg_init(dev, ft_attr, pf);\n+\tif (mlx5_lag_is_sw_managed(dev))\n+\t\treturn mlx5_lag_demux_ft_fg_init(dev, ft_attr, fn);\n \n-\treturn mlx5_lag_demux_fw_init(dev, ft_attr, pf);\n+\treturn mlx5_lag_demux_fw_init(dev, ft_attr, fn);\n }\n EXPORT_SYMBOL(mlx5_lag_demux_init);\n \n@@ -1884,31 +1953,31 @@ void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev)\n \tstruct mlx5_flow_handle *rule;\n \tstruct mlx5_lag *ldev;\n \tunsigned long vport_num;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \n \tldev = mlx5_lag_dev(dev);\n \tif (!ldev)\n \t\treturn;\n \n-\tpf = mlx5_lag_pf_by_dev(ldev, dev);\n-\tif (!pf)\n+\tfn = mlx5_lag_fn_by_dev(ldev, dev);\n+\tif (!fn)\n \t\treturn;\n \n-\txa_for_each(\u0026pf-\u003elag_demux_rules, vport_num, rule)\n+\txa_for_each(\u0026fn-\u003elag_demux_rules, vport_num, rule)\n \t\tmlx5_del_flow_rules(rule);\n-\txa_destroy(\u0026pf-\u003elag_demux_rules);\n+\txa_destroy(\u0026fn-\u003elag_demux_rules);\n \n-\tif (pf-\u003elag_demux_fg)\n-\t\tmlx5_destroy_flow_group(pf-\u003elag_demux_fg);\n-\tif (pf-\u003elag_demux_ft)\n-\t\tmlx5_destroy_flow_table(pf-\u003elag_demux_ft);\n-\tpf-\u003elag_demux_fg = NULL;\n-\tpf-\u003elag_demux_ft = NULL;\n+\tif (fn-\u003elag_demux_fg)\n+\t\tmlx5_destroy_flow_group(fn-\u003elag_demux_fg);\n+\tif (fn-\u003elag_demux_ft)\n+\t\tmlx5_destroy_flow_table(fn-\u003elag_demux_ft);\n+\tfn-\u003elag_demux_fg = NULL;\n+\tfn-\u003elag_demux_ft = NULL;\n }\n EXPORT_SYMBOL(mlx5_lag_demux_cleanup);\n \n-static struct lag_func *mlx5_lag_dev_get_master_pf(struct mlx5_lag *ldev,\n-\t\t\t\t\t\t   struct mlx5_core_dev *dev)\n+static struct lag_fn *mlx5_lag_dev_get_master_fn(struct mlx5_lag *ldev,\n+\t\t\t\t\t\t struct mlx5_core_dev *dev)\n {\n \tu32 filter = mlx5_lag_get_filter(ldev, dev);\n \tint idx;\n@@ -1917,14 +1986,14 @@ static struct lag_func *mlx5_lag_dev_get_master_pf(struct mlx5_lag *ldev,\n \tif (idx \u003c 0)\n \t\treturn NULL;\n \n-\treturn mlx5_lag_pf(ldev, idx);\n+\treturn mlx5_lag_fn(ldev, idx);\n }\n \n int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num,\n \t\t\t    int index)\n {\n \tstruct mlx5_flow_handle *rule;\n-\tstruct lag_func *master;\n+\tstruct lag_fn *master;\n \tstruct mlx5_lag *ldev;\n \tint err;\n \n@@ -1932,7 +2001,7 @@ int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num,\n \tif (!ldev)\n \t\treturn 0;\n \n-\tmaster = mlx5_lag_dev_get_master_pf(ldev, vport_dev);\n+\tmaster = mlx5_lag_dev_get_master_fn(ldev, vport_dev);\n \tif (!master || !master-\u003elag_demux_fg)\n \t\treturn 0;\n \n@@ -1965,18 +2034,18 @@ EXPORT_SYMBOL(mlx5_lag_demux_rule_add);\n void mlx5_lag_demux_rule_del(struct mlx5_core_dev *dev, int index)\n {\n \tstruct mlx5_flow_handle *rule;\n-\tstruct lag_func *master_pf;\n+\tstruct lag_fn *master_fn;\n \tstruct mlx5_lag *ldev;\n \n \tldev = mlx5_lag_dev(dev);\n \tif (!ldev)\n \t\treturn;\n \n-\tmaster_pf = mlx5_lag_dev_get_master_pf(ldev, dev);\n-\tif (!master_pf || !master_pf-\u003elag_demux_fg)\n+\tmaster_fn = mlx5_lag_dev_get_master_fn(ldev, dev);\n+\tif (!master_fn || !master_fn-\u003elag_demux_fg)\n \t\treturn;\n \n-\trule = xa_erase(\u0026master_pf-\u003elag_demux_rules, index);\n+\trule = xa_erase(\u0026master_fn-\u003elag_demux_rules, index);\n \tif (rule)\n \t\tmlx5_del_flow_rules(rule);\n }\n@@ -2026,7 +2095,7 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev,\n \tstruct netdev_lag_upper_info *lag_upper_info = NULL;\n \tbool is_bonded, is_in_lag, mode_supported;\n \tbool has_inactive = 0;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tstruct slave *slave;\n \tu8 bond_status = 0;\n \tint num_slaves = 0;\n@@ -2047,13 +2116,13 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev,\n \trcu_read_lock();\n \tfor_each_netdev_in_bond_rcu(upper, ndev_tmp) {\n \t\tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\t\tif (pf-\u003enetdev == ndev_tmp) {\n+\t\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\t\tif (fn-\u003enetdev == ndev_tmp) {\n \t\t\t\tidx++;\n \t\t\t\tbreak;\n \t\t\t}\n \t\t}\n-\t\tif (i \u003c MLX5_MAX_PORTS) {\n+\t\tif (i \u003c ldev-\u003eports) {\n \t\t\tif (netif_is_bond_master(upper)) {\n \t\t\t\tslave = bond_slave_get_rcu(ndev_tmp);\n \t\t\t\tif (slave)\n@@ -2270,16 +2339,16 @@ static void mlx5_ldev_add_netdev(struct mlx5_lag *ldev,\n \t\t\t\tstruct mlx5_core_dev *dev,\n \t\t\t\tstruct net_device *netdev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tunsigned long flags;\n \tint i;\n \n \tspin_lock_irqsave(\u0026lag_lock, flags);\n-\t/* Find pf entry by matching dev pointer */\n+\t/* Find fn entry by matching dev pointer */\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev == dev) {\n-\t\t\tpf-\u003enetdev = netdev;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev == dev) {\n+\t\t\tfn-\u003enetdev = netdev;\n \t\t\tldev-\u003etracker.netdev_state[i].link_up = 0;\n \t\t\tldev-\u003etracker.netdev_state[i].tx_enabled = 0;\n \t\t\tbreak;\n@@ -2291,15 +2360,15 @@ static void mlx5_ldev_add_netdev(struct mlx5_lag *ldev,\n static void mlx5_ldev_remove_netdev(struct mlx5_lag *ldev,\n \t\t\t\t    struct net_device *netdev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tunsigned long flags;\n \tint i;\n \n \tspin_lock_irqsave(\u0026lag_lock, flags);\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003enetdev == netdev) {\n-\t\t\tpf-\u003enetdev = NULL;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003enetdev == netdev) {\n+\t\t\tfn-\u003enetdev = NULL;\n \t\t\tbreak;\n \t\t}\n \t}\n@@ -2310,34 +2379,37 @@ int mlx5_ldev_add_mdev(struct mlx5_lag *ldev,\n \t\t       struct mlx5_core_dev *dev,\n \t\t       u32 group_id)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tu32 idx;\n \tint err;\n \n-\tpf = kzalloc_obj(*pf);\n-\tif (!pf)\n+\tfn = kzalloc_obj(*fn);\n+\tif (!fn)\n \t\treturn -ENOMEM;\n \n-\terr = xa_alloc(\u0026ldev-\u003epfs, \u0026idx, pf, XA_LIMIT(0, MLX5_MAX_PORTS - 1),\n+\terr = xa_alloc(\u0026ldev-\u003efns, \u0026idx, fn, XA_LIMIT(0, U8_MAX - 1),\n \t\t       GFP_KERNEL);\n \tif (err) {\n-\t\tkfree(pf);\n+\t\tkfree(fn);\n \t\treturn err;\n \t}\n \n-\tpf-\u003eidx = idx;\n-\tpf-\u003edev = dev;\n-\tpf-\u003egroup_id = group_id;\n+\t/* max_fns is the iteration ceiling; grow it, never shrink. */\n+\tif (idx \u003e= ldev-\u003emax_fns)\n+\t\tldev-\u003emax_fns = idx + 1;\n+\tfn-\u003eidx = idx;\n+\tfn-\u003edev = dev;\n+\tfn-\u003egroup_id = group_id;\n \tdev-\u003epriv.lag = ldev;\n \n-\tif (group_id)\n+\tif (group_id || ldev-\u003evirt_lag)\n \t\treturn 0;\n \n-\txa_set_mark(\u0026ldev-\u003epfs, idx, MLX5_LAG_XA_MARK_PORT);\n+\txa_set_mark(\u0026ldev-\u003efns, idx, MLX5_LAG_XA_MARK_PORT);\n \n-\tMLX5_NB_INIT(\u0026pf-\u003eport_change_nb,\n+\tMLX5_NB_INIT(\u0026fn-\u003eport_change_nb,\n \t\t     mlx5_lag_mpesw_port_change_event, PORT_CHANGE);\n-\tmlx5_eq_notifier_register(dev, \u0026pf-\u003eport_change_nb);\n+\tmlx5_eq_notifier_register(dev, \u0026fn-\u003eport_change_nb);\n \n \treturn 0;\n }\n@@ -2345,24 +2417,24 @@ int mlx5_ldev_add_mdev(struct mlx5_lag *ldev,\n void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev,\n \t\t\t   struct mlx5_core_dev *dev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev == dev)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev == dev)\n \t\t\tbreak;\n \t}\n-\tif (i \u003e= MLX5_MAX_PORTS)\n+\tif (i \u003e= ldev-\u003emax_fns)\n \t\treturn;\n \n-\tif (pf-\u003eport_change_nb.nb.notifier_call)\n-\t\tmlx5_eq_notifier_unregister(dev, \u0026pf-\u003eport_change_nb);\n+\tif (fn-\u003eport_change_nb.nb.notifier_call)\n+\t\tmlx5_eq_notifier_unregister(dev, \u0026fn-\u003eport_change_nb);\n \n-\tpf-\u003edev = NULL;\n+\tfn-\u003edev = NULL;\n \tdev-\u003epriv.lag = NULL;\n-\txa_erase(\u0026ldev-\u003epfs, pf-\u003eidx);\n-\tkfree(pf);\n+\txa_erase(\u0026ldev-\u003efns, fn-\u003eidx);\n+\tkfree(fn);\n }\n \n /* Must be called with HCA devcom component lock held */\n@@ -2397,6 +2469,11 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev)\n \t\tmutex_unlock(\u0026ldev-\u003elock);\n \t\treturn -EAGAIN;\n \t}\n+\n+\tif (mlx5_virt_lag_is_supported(dev) \u0026\u0026 __mlx5_lag_is_active(ldev)) {\n+\t\tmutex_unlock(\u0026ldev-\u003elock);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n \tmlx5_ldev_get(ldev);\n \terr = mlx5_ldev_add_mdev(ldev, dev, 0);\n \tif (err) {\n@@ -2474,7 +2551,7 @@ void mlx5_lag_add_mdev(struct mlx5_core_dev *dev)\n {\n \tint err;\n \n-\tif (!mlx5_lag_is_supported(dev))\n+\tif (!mlx5_lag_is_supported(dev) \u0026\u0026 !mlx5_virt_lag_is_supported(dev))\n \t\treturn;\n \n \tif (mlx5_lag_register_hca_devcom_comp(dev))\n@@ -2485,6 +2562,10 @@ void mlx5_lag_add_mdev(struct mlx5_core_dev *dev)\n \terr = __mlx5_lag_dev_add_mdev(dev);\n \tmlx5_devcom_comp_unlock(dev-\u003epriv.hca_devcom_comp);\n \n+\tif (err == -EOPNOTSUPP) {\n+\t\tmlx5_lag_unregister_hca_devcom_comp(dev);\n+\t\treturn;\n+\t}\n \tif (err) {\n \t\tmsleep(100);\n \t\tgoto recheck;\n@@ -2505,6 +2586,9 @@ void mlx5_lag_remove_netdev(struct mlx5_core_dev *dev,\n \tif (!ldev)\n \t\treturn;\n \n+\tif (ldev-\u003evirt_lag)\n+\t\treturn;\n+\n \tmutex_lock(\u0026ldev-\u003elock);\n \tmlx5_ldev_remove_netdev(ldev, netdev);\n \tclear_bit(MLX5_LAG_FLAG_NDEVS_READY, \u0026ldev-\u003estate_flags);\n@@ -2526,6 +2610,9 @@ void mlx5_lag_add_netdev(struct mlx5_core_dev *dev,\n \tif (!ldev)\n \t\treturn;\n \n+\tif (ldev-\u003evirt_lag)\n+\t\treturn;\n+\n \tmutex_lock(\u0026ldev-\u003elock);\n \tmlx5_ldev_add_netdev(ldev, dev, netdev);\n \tnum = mlx5_lag_num_netdevs(ldev);\n@@ -2538,18 +2625,18 @@ void mlx5_lag_add_netdev(struct mlx5_core_dev *dev,\n int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx,\n \t\t\t  u32 filter)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tfor (i = start_idx; i \u003e= end_idx; i--) {\n-\t\tpf = xa_load(\u0026ldev-\u003epfs, i);\n-\t\tif (!pf || !pf-\u003edev)\n+\t\tfn = xa_load(\u0026ldev-\u003efns, i);\n+\t\tif (!fn || !fn-\u003edev)\n \t\t\tcontinue;\n \t\tif (filter == MLX5_LAG_FILTER_PORTS) {\n-\t\t\tif (xa_get_mark(\u0026ldev-\u003epfs, i, MLX5_LAG_XA_MARK_PORT))\n+\t\t\tif (xa_get_mark(\u0026ldev-\u003efns, i, MLX5_LAG_XA_MARK_PORT))\n \t\t\t\treturn i;\n \t\t} else if (filter == MLX5_LAG_FILTER_ALL ||\n-\t\t\t   filter == pf-\u003egroup_id) {\n+\t\t\t   filter == fn-\u003egroup_id) {\n \t\t\treturn i;\n \t\t}\n \t}\n@@ -2558,25 +2645,25 @@ int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx,\n \n int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tunsigned long idx;\n \n \tif (filter == MLX5_LAG_FILTER_PORTS) {\n-\t\txa_for_each_marked_start(\u0026ldev-\u003epfs, idx, pf,\n+\t\txa_for_each_marked_start(\u0026ldev-\u003efns, idx, fn,\n \t\t\t\t\t MLX5_LAG_XA_MARK_PORT, start_idx)\n-\t\t\tif (pf-\u003edev)\n+\t\t\tif (fn-\u003edev)\n \t\t\t\treturn idx;\n-\t\treturn MLX5_MAX_PORTS;\n+\t\treturn ldev-\u003emax_fns;\n \t}\n \n-\txa_for_each_start(\u0026ldev-\u003epfs, idx, pf, start_idx) {\n-\t\tif (!pf-\u003edev)\n+\txa_for_each_start(\u0026ldev-\u003efns, idx, fn, start_idx) {\n+\t\tif (!fn-\u003edev)\n \t\t\tcontinue;\n \t\tif (filter == MLX5_LAG_FILTER_ALL ||\n-\t\t    filter == pf-\u003egroup_id)\n+\t\t    filter == fn-\u003egroup_id)\n \t\t\treturn idx;\n \t}\n-\treturn MLX5_MAX_PORTS;\n+\treturn ldev-\u003emax_fns;\n }\n \n bool mlx5_lag_is_roce(struct mlx5_core_dev *dev)\n@@ -2630,7 +2717,7 @@ bool mlx5_lag_is_master(struct mlx5_core_dev *dev)\n {\n \tstruct mlx5_lag *ldev;\n \tunsigned long flags;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tbool res = false;\n \tint idx;\n \n@@ -2644,8 +2731,8 @@ bool mlx5_lag_is_master(struct mlx5_core_dev *dev)\n \t\t\t\t\t\t\t   filter);\n \t\tif ((__mlx5_lag_is_active(ldev) ||\n \t\t     __mlx5_lag_is_sd_active(ldev, dev)) \u0026\u0026 idx \u003e= 0) {\n-\t\t\tpf = mlx5_lag_pf(ldev, idx);\n-\t\t\tres = pf \u0026\u0026 dev == pf-\u003edev;\n+\t\t\tfn = mlx5_lag_fn(ldev, idx);\n+\t\t\tres = fn \u0026\u0026 dev == fn-\u003edev;\n \t\t}\n \t}\n \tspin_unlock_irqrestore(\u0026lag_lock, flags);\n@@ -2708,7 +2795,7 @@ void mlx5_lag_disable_change(struct mlx5_core_dev *dev)\n \tstruct mlx5_devcom_comp_dev *sd_devcom = mlx5_sd_get_devcom(dev);\n \tstruct mlx5_core_dev *primary = dev;\n \tstruct mlx5_lag *ldev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tbool mpesw;\n \tint i;\n \n@@ -2747,9 +2834,9 @@ void mlx5_lag_disable_change(struct mlx5_core_dev *dev)\n \tmlx5_devcom_comp_lock(sd_devcom);\n \tmutex_lock(\u0026ldev-\u003elock);\n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev == dev \u0026\u0026 pf-\u003esd_fdb_active) {\n-\t\t\tmlx5_lag_shared_fdb_destroy(ldev, pf-\u003egroup_id);\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev == dev \u0026\u0026 fn-\u003esd_fdb_active) {\n+\t\t\tmlx5_lag_shared_fdb_destroy(ldev, fn-\u003egroup_id);\n \t\t\tbreak;\n \t\t}\n \t}\n@@ -2776,7 +2863,7 @@ u8 mlx5_lag_get_slave_port(struct mlx5_core_dev *dev,\n {\n \tstruct mlx5_lag *ldev;\n \tunsigned long flags;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tu8 port = 0;\n \tint i;\n \n@@ -2786,8 +2873,8 @@ u8 mlx5_lag_get_slave_port(struct mlx5_core_dev *dev,\n \t\tgoto unlock;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003enetdev == slave) {\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003enetdev == slave) {\n \t\t\tport = i;\n \t\t\tbreak;\n \t\t}\n@@ -2809,7 +2896,7 @@ u8 mlx5_lag_get_num_ports(struct mlx5_core_dev *dev)\n \tif (!ldev)\n \t\treturn 0;\n \n-\treturn ldev-\u003eports;\n+\treturn max_t(u8, ldev-\u003eports, 1);\n }\n EXPORT_SYMBOL(mlx5_lag_get_num_ports);\n \n@@ -2818,7 +2905,7 @@ struct mlx5_core_dev *mlx5_lag_get_next_peer_mdev(struct mlx5_core_dev *dev, int\n \tstruct mlx5_core_dev *peer_dev = NULL;\n \tstruct mlx5_lag *ldev;\n \tunsigned long flags;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint idx;\n \n \tspin_lock_irqsave(\u0026lag_lock, flags);\n@@ -2826,22 +2913,22 @@ struct mlx5_core_dev *mlx5_lag_get_next_peer_mdev(struct mlx5_core_dev *dev, int\n \tif (!ldev)\n \t\tgoto unlock;\n \n-\tif (*i == MLX5_MAX_PORTS)\n+\tif (*i == ldev-\u003emax_fns)\n \t\tgoto unlock;\n \tmlx5_lag_for_each(idx, *i, ldev, mlx5_lag_get_filter(ldev, dev)) {\n-\t\tpf = mlx5_lag_pf(ldev, idx);\n-\t\tif (pf-\u003edev != dev)\n+\t\tfn = mlx5_lag_fn(ldev, idx);\n+\t\tif (fn-\u003edev != dev)\n \t\t\tbreak;\n \t}\n \n-\tif (idx == MLX5_MAX_PORTS) {\n+\tif (idx == ldev-\u003emax_fns) {\n \t\t*i = idx;\n \t\tgoto unlock;\n \t}\n \t*i = idx + 1;\n \n-\tpf = mlx5_lag_pf(ldev, idx);\n-\tpeer_dev = pf-\u003edev;\n+\tfn = mlx5_lag_fn(ldev, idx);\n+\tpeer_dev = fn-\u003edev;\n \n unlock:\n \tspin_unlock_irqrestore(\u0026lag_lock, flags);\n@@ -2859,7 +2946,7 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,\n \tint ret = 0, i, j, idx = 0;\n \tstruct mlx5_lag *ldev;\n \tunsigned long flags;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint num_ports;\n \tvoid *out;\n \n@@ -2867,7 +2954,9 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,\n \tif (!out)\n \t\treturn -ENOMEM;\n \n-\tmdev = kvzalloc(sizeof(mdev[0]) * MLX5_MAX_PORTS, GFP_KERNEL);\n+\tldev = mlx5_lag_dev(dev);\n+\tnum_ports = ldev ? max_t(int, ldev-\u003eports, 1) : 1;\n+\tmdev = kcalloc(num_ports, sizeof(mdev[0]), GFP_KERNEL);\n \tif (!mdev) {\n \t\tret = -ENOMEM;\n \t\tgoto free_out;\n@@ -2876,20 +2965,17 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,\n \tmemset(values, 0, sizeof(*values) * num_counters);\n \n \tspin_lock_irqsave(\u0026lag_lock, flags);\n-\tldev = mlx5_lag_dev(dev);\n-\tif (ldev \u0026\u0026 __mlx5_lag_is_active(ldev)) {\n-\t\tnum_ports = ldev-\u003eports;\n+\tif (ldev \u0026\u0026 __mlx5_lag_is_active(ldev) \u0026\u0026 !ldev-\u003evirt_lag) {\n \t\tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\t\tmdev[idx++] = pf-\u003edev;\n+\t\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\t\tmdev[idx++] = fn-\u003edev;\n \t\t}\n \t} else {\n-\t\tnum_ports = 1;\n-\t\tmdev[MLX5_LAG_P1] = dev;\n+\t\tmdev[idx++] = dev;\n \t}\n \tspin_unlock_irqrestore(\u0026lag_lock, flags);\n \n-\tfor (i = 0; i \u003c num_ports; ++i) {\n+\tfor (i = 0; i \u003c idx; ++i) {\n \t\tu32 in[MLX5_ST_SZ_DW(query_cong_statistics_in)] = {};\n \n \t\tMLX5_SET(query_cong_statistics_in, in, opcode,\n@@ -2904,7 +2990,7 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,\n \t}\n \n free_mdev:\n-\tkvfree(mdev);\n+\tkfree(mdev);\n free_out:\n \tkvfree(out);\n \treturn ret;\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h\nindex 8552792b9035fd..385c767e24ae69 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h\n@@ -51,7 +51,7 @@ enum mlx5_lag_mode {\n \tMLX5_LAG_MODE_MPESW,\n };\n \n-struct lag_func {\n+struct lag_fn {\n \tstruct mlx5_core_dev *dev;\n \tstruct net_device    *netdev;\n \tbool has_drop;\n@@ -83,11 +83,13 @@ struct mlx5_lag {\n \tunsigned long\t\t  mode_flags;\n \tunsigned long\t\t  state_flags;\n \tu8\t\t\t  ports;\n+\tu8\t\t\t  max_fns;\n \tu8\t\t\t  buckets;\n+\tbool\t\t\t  virt_lag;\n \tint\t\t\t  mode_changes_in_progress;\n \tu8\t\t\t  *v2p_map;\n \tstruct kref               ref;\n-\tstruct xarray             pfs;\n+\tstruct xarray             fns;\n \tstruct lag_tracker        tracker;\n \tstruct workqueue_struct   *wq;\n \tstruct delayed_work       bond_work;\n@@ -107,44 +109,44 @@ mlx5_lag_dev(struct mlx5_core_dev *dev)\n \treturn dev-\u003epriv.lag;\n }\n \n-static inline struct lag_func *\n-mlx5_lag_pf(struct mlx5_lag *ldev, unsigned int idx)\n+static inline struct lag_fn *\n+mlx5_lag_fn(struct mlx5_lag *ldev, unsigned int idx)\n {\n-\treturn xa_load(\u0026ldev-\u003epfs, idx);\n+\treturn xa_load(\u0026ldev-\u003efns, idx);\n }\n \n /* Get device index (mlx5_get_dev_index) from xarray index */\n static inline int mlx5_lag_xa_to_dev_idx(struct mlx5_lag *ldev, int xa_idx)\n {\n-\tstruct lag_func *pf = mlx5_lag_pf(ldev, xa_idx);\n+\tstruct lag_fn *fn = mlx5_lag_fn(ldev, xa_idx);\n \n-\treturn pf ? mlx5_get_dev_index(pf-\u003edev) : -ENOENT;\n+\treturn fn ? mlx5_get_dev_index(fn-\u003edev) : -ENOENT;\n }\n \n-/* Find lag_func by device index (reverse lookup from mlx5_get_dev_index) */\n-static inline struct lag_func *\n-mlx5_lag_pf_by_dev_idx(struct mlx5_lag *ldev, int dev_idx)\n+/* Find lag_fn by device index (reverse lookup from mlx5_get_dev_index) */\n+static inline struct lag_fn *\n+mlx5_lag_fn_by_dev_idx(struct mlx5_lag *ldev, int dev_idx)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tunsigned long idx;\n \n-\txa_for_each(\u0026ldev-\u003epfs, idx, pf) {\n-\t\tif (mlx5_get_dev_index(pf-\u003edev) == dev_idx)\n-\t\t\treturn pf;\n+\txa_for_each(\u0026ldev-\u003efns, idx, fn) {\n+\t\tif (mlx5_get_dev_index(fn-\u003edev) == dev_idx)\n+\t\t\treturn fn;\n \t}\n \treturn NULL;\n }\n \n-/* Find lag_func by mlx5_core_dev pointer */\n-static inline struct lag_func *\n-mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)\n+/* Find lag_fn by mlx5_core_dev pointer */\n+static inline struct lag_fn *\n+mlx5_lag_fn_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tunsigned long idx;\n \n-\txa_for_each(\u0026ldev-\u003epfs, idx, pf) {\n-\t\tif (pf-\u003edev == dev)\n-\t\t\treturn pf;\n+\txa_for_each(\u0026ldev-\u003efns, idx, fn) {\n+\t\tif (fn-\u003edev == dev)\n+\t\t\treturn fn;\n \t}\n \treturn NULL;\n }\n@@ -152,17 +154,17 @@ mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)\n static inline bool\n __mlx5_lag_is_sd(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)\n {\n-\tstruct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);\n+\tstruct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);\n \n-\treturn pf \u0026\u0026 pf-\u003egroup_id != 0;\n+\treturn fn \u0026\u0026 fn-\u003egroup_id != 0;\n }\n \n static inline bool\n __mlx5_lag_dev_is_port(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)\n {\n-\tstruct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);\n+\tstruct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);\n \n-\treturn pf \u0026\u0026 xa_get_mark(\u0026ldev-\u003epfs, pf-\u003eidx, MLX5_LAG_XA_MARK_PORT);\n+\treturn fn \u0026\u0026 xa_get_mark(\u0026ldev-\u003efns, fn-\u003eidx, MLX5_LAG_XA_MARK_PORT);\n }\n \n static inline bool\n@@ -225,7 +227,10 @@ static inline bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev)\n }\n #endif\n bool mlx5_lag_check_prereq(struct mlx5_lag *ldev);\n+void mlx5_virt_lag_mark_master(struct mlx5_lag *ldev);\n+void mlx5_lag_clear_master(struct mlx5_lag *ldev);\n bool mlx5_lag_is_sd(struct mlx5_core_dev *dev);\n+bool mlx5_lag_is_sw_managed(struct mlx5_core_dev *dev);\n int mlx5_lag_demux_init(struct mlx5_core_dev *dev,\n \t\t\tstruct mlx5_flow_table_attr *ft_attr);\n void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev);\n@@ -266,6 +271,20 @@ static inline void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev) {}\n static inline void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev) {}\n #endif\n \n+static inline bool mlx5_virt_lag_is_supported(const struct mlx5_core_dev *dev)\n+{\n+\tif (!MLX5_CAP_GEN(dev, multi_uplink_eswitch) ||\n+\t    !MLX5_CAP_GEN(dev, lag_master) ||\n+\t    MLX5_CAP_GEN(dev, num_lag_ports) \u003e= 2)\n+\t\treturn false;\n+\treturn true;\n+}\n+\n+static inline bool mlx5_virt_lag_nonmember(struct mlx5_core_dev *dev)\n+{\n+\treturn mlx5_virt_lag_is_supported(dev) \u0026\u0026 !mlx5_lag_dev(dev);\n+}\n+\n static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev)\n {\n \tif (!MLX5_CAP_GEN(dev, vport_group_manager) ||\n@@ -285,7 +304,7 @@ static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev)\n #define mlx5_lag_for_each(i, start_index, ldev, filter) \\\n \tfor (int tmp = start_index; \\\n \t     tmp = mlx5_get_next_lag_func(ldev, tmp, filter), \\\n-\t     i = tmp, tmp \u003c MLX5_MAX_PORTS; tmp++)\n+\t     i = tmp, tmp \u003c (ldev)-\u003emax_fns; tmp++)\n \n #define mlx5_lag_for_each_reverse(i, start_index, end_index, ldev, filter) \\\n \tfor (int tmp = start_index, tmp1 = end_index; \\\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c\nindex 65c76bd748c6b2..587638278f342b 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c\n@@ -26,15 +26,15 @@ static bool mlx5_lag_multipath_check_prereq(struct mlx5_lag *ldev)\n \tif (__mlx5_lag_is_active(ldev) \u0026\u0026 !__mlx5_lag_is_multipath(ldev))\n \t\treturn false;\n \n-\tif (__mlx5_lag_is_sd(ldev, mlx5_lag_pf(ldev, idx0)-\u003edev) ||\n-\t    __mlx5_lag_is_sd(ldev, mlx5_lag_pf(ldev, idx1)-\u003edev))\n+\tif (mlx5_lag_is_sw_managed(mlx5_lag_fn(ldev, idx0)-\u003edev) ||\n+\t    mlx5_lag_is_sw_managed(mlx5_lag_fn(ldev, idx1)-\u003edev))\n \t\treturn false;\n \n \tif (ldev-\u003eports \u003e MLX5_LAG_MULTIPATH_OFFLOADS_SUPPORTED_PORTS)\n \t\treturn false;\n \n-\treturn mlx5_esw_multipath_prereq(mlx5_lag_pf(ldev, idx0)-\u003edev,\n-\t\t\t\t\t mlx5_lag_pf(ldev, idx1)-\u003edev);\n+\treturn mlx5_esw_multipath_prereq(mlx5_lag_fn(ldev, idx0)-\u003edev,\n+\t\t\t\t\t mlx5_lag_fn(ldev, idx1)-\u003edev);\n }\n \n bool mlx5_lag_is_multipath(struct mlx5_core_dev *dev)\n@@ -59,11 +59,15 @@ static void mlx5_lag_set_port_affinity(struct mlx5_lag *ldev,\n {\n \tint idx0 = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);\n \tint idx1 = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P2);\n+\tstruct mlx5_core_dev *dev0, *dev1;\n \tstruct lag_tracker tracker = {};\n \n \tif (idx0 \u003c 0 || idx1 \u003c 0 || !__mlx5_lag_is_multipath(ldev))\n \t\treturn;\n \n+\tdev0 = mlx5_lag_fn(ldev, idx0)-\u003edev;\n+\tdev1 = mlx5_lag_fn(ldev, idx1)-\u003edev;\n+\n \tswitch (port) {\n \tcase MLX5_LAG_NORMAL_AFFINITY:\n \t\ttracker.netdev_state[idx0].tx_enabled = true;\n@@ -84,18 +88,17 @@ static void mlx5_lag_set_port_affinity(struct mlx5_lag *ldev,\n \t\ttracker.netdev_state[idx1].link_up = true;\n \t\tbreak;\n \tdefault:\n-\t\tmlx5_core_warn(mlx5_lag_pf(ldev, idx0)-\u003edev,\n-\t\t\t       \"Invalid affinity port %d\", port);\n+\t\tmlx5_core_warn(dev0, \"Invalid affinity port %d\", port);\n \t\treturn;\n \t}\n \n \tif (tracker.netdev_state[idx0].tx_enabled)\n-\t\tmlx5_notifier_call_chain(mlx5_lag_pf(ldev, idx0)-\u003edev-\u003epriv.events,\n+\t\tmlx5_notifier_call_chain(dev0-\u003epriv.events,\n \t\t\t\t\t MLX5_DEV_EVENT_PORT_AFFINITY,\n \t\t\t\t\t (void *)0);\n \n \tif (tracker.netdev_state[idx1].tx_enabled)\n-\t\tmlx5_notifier_call_chain(mlx5_lag_pf(ldev, idx1)-\u003edev-\u003epriv.events,\n+\t\tmlx5_notifier_call_chain(dev1-\u003epriv.events,\n \t\t\t\t\t MLX5_DEV_EVENT_PORT_AFFINITY,\n \t\t\t\t\t (void *)0);\n \n@@ -150,7 +153,7 @@ mlx5_lag_get_next_fib_dev(struct mlx5_lag *ldev,\n \t\tfib_dev = fib_info_nh(fi, i)-\u003efib_nh_dev;\n \t\tldev_idx = mlx5_lag_dev_get_netdev_idx(ldev, fib_dev);\n \t\tif (ldev_idx \u003e= 0)\n-\t\t\treturn mlx5_lag_pf(ldev, ldev_idx)-\u003enetdev;\n+\t\t\treturn mlx5_lag_fn(ldev, ldev_idx)-\u003enetdev;\n \t}\n \n \treturn NULL;\n@@ -182,7 +185,7 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event,\n \t    mp-\u003efib.dst_len \u003c= fen_info-\u003edst_len \u0026\u0026\n \t    !(mp-\u003efib.dst_len == fen_info-\u003edst_len \u0026\u0026\n \t      fi-\u003efib_priority \u003c mp-\u003efib.priority)) {\n-\t\tmlx5_core_dbg(mlx5_lag_pf(ldev, idx)-\u003edev,\n+\t\tmlx5_core_dbg(mlx5_lag_fn(ldev, idx)-\u003edev,\n \t\t\t      \"Multipath entry with lower priority was rejected\\n\");\n \t\treturn;\n \t}\n@@ -198,7 +201,7 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event,\n \t}\n \n \tif (nh_dev0 == nh_dev1) {\n-\t\tmlx5_core_warn(mlx5_lag_pf(ldev, idx)-\u003edev,\n+\t\tmlx5_core_warn(mlx5_lag_fn(ldev, idx)-\u003edev,\n \t\t\t       \"Multipath offload doesn't support routes with multiple nexthops of the same device\");\n \t\treturn;\n \t}\n@@ -207,7 +210,7 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event,\n \t\tif (__mlx5_lag_is_active(ldev)) {\n \t\t\tmlx5_ldev_for_each(i, 0, ldev) {\n \t\t\t\tdev_idx++;\n-\t\t\t\tif (mlx5_lag_pf(ldev, i)-\u003enetdev == nh_dev0)\n+\t\t\t\tif (mlx5_lag_fn(ldev, i)-\u003enetdev == nh_dev0)\n \t\t\t\t\tbreak;\n \t\t\t}\n \t\t\tmlx5_lag_set_port_affinity(ldev, dev_idx);\n@@ -244,7 +247,7 @@ static void mlx5_lag_fib_nexthop_event(struct mlx5_lag *ldev,\n \t/* nh added/removed */\n \tif (event == FIB_EVENT_NH_DEL) {\n \t\tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\t\tif (mlx5_lag_pf(ldev, i)-\u003enetdev == fib_nh-\u003efib_nh_dev)\n+\t\t\tif (mlx5_lag_fn(ldev, i)-\u003enetdev == fib_nh-\u003efib_nh_dev)\n \t\t\t\tbreak;\n \t\t\tdev_idx++;\n \t\t}\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c\nindex abf72026c75133..6f575750e403d5 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c\n@@ -15,8 +15,11 @@ static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev)\n \tu32 pf_metadata;\n \tint i;\n \n+\tif (!ldev-\u003elag_mpesw.pf_metadata)\n+\t\treturn;\n+\n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tdev = mlx5_lag_pf(ldev, i)-\u003edev;\n+\t\tdev = mlx5_lag_fn(ldev, i)-\u003edev;\n \t\tesw = dev-\u003epriv.eswitch;\n \t\tpf_metadata = ldev-\u003elag_mpesw.pf_metadata[i];\n \t\tif (!pf_metadata)\n@@ -27,6 +30,9 @@ static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev)\n \t\tmlx5_esw_match_metadata_free(esw, pf_metadata);\n \t\tldev-\u003elag_mpesw.pf_metadata[i] = 0;\n \t}\n+\n+\tkfree(ldev-\u003elag_mpesw.pf_metadata);\n+\tldev-\u003elag_mpesw.pf_metadata = NULL;\n }\n \n static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)\n@@ -36,8 +42,14 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)\n \tu32 pf_metadata;\n \tint i, err;\n \n+\tldev-\u003elag_mpesw.pf_metadata =\n+\t\tkcalloc(ldev-\u003emax_fns, sizeof(*ldev-\u003elag_mpesw.pf_metadata),\n+\t\t\tGFP_KERNEL);\n+\tif (!ldev-\u003elag_mpesw.pf_metadata)\n+\t\treturn -ENOMEM;\n+\n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tdev = mlx5_lag_pf(ldev, i)-\u003edev;\n+\t\tdev = mlx5_lag_fn(ldev, i)-\u003edev;\n \t\tesw = dev-\u003epriv.eswitch;\n \t\tpf_metadata = mlx5_esw_match_metadata_alloc(esw);\n \t\tif (!pf_metadata) {\n@@ -53,7 +65,7 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)\n \t}\n \n \tmlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {\n-\t\tdev = mlx5_lag_pf(ldev, i)-\u003edev;\n+\t\tdev = mlx5_lag_fn(ldev, i)-\u003edev;\n \t\tmlx5_notifier_call_chain(dev-\u003epriv.events, MLX5_DEV_EVENT_MULTIPORT_ESW,\n \t\t\t\t\t (void *)0);\n \t}\n@@ -67,14 +79,14 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)\n \n static void mlx5_mpesw_restore_sd_fdb(struct mlx5_lag *ldev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint err, i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\terr = mlx5_lag_shared_fdb_create(ldev, NULL, 0, pf-\u003egroup_id);\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\terr = mlx5_lag_shared_fdb_create(ldev, NULL, 0, fn-\u003egroup_id);\n \t\tif (err)\n-\t\t\tmlx5_core_warn(pf-\u003edev,\n+\t\t\tmlx5_core_warn(fn-\u003edev,\n \t\t\t\t       \"Failed to restore SD shared FDB (%d)\\n\",\n \t\t\t\t       err);\n \t}\n@@ -82,35 +94,58 @@ static void mlx5_mpesw_restore_sd_fdb(struct mlx5_lag *ldev)\n \n static int mlx5_mpesw_teardown_sd_fdb(struct mlx5_lag *ldev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (!pf-\u003esd_fdb_active)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (!fn-\u003esd_fdb_active)\n \t\t\tcontinue;\n-\t\tmlx5_lag_shared_fdb_destroy(ldev, pf-\u003egroup_id);\n+\t\tmlx5_lag_shared_fdb_destroy(ldev, fn-\u003egroup_id);\n \t}\n \treturn 0;\n }\n \n static bool mlx5_lag_has_sd_group(struct mlx5_lag *ldev)\n {\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003egroup_id)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003egroup_id)\n \t\t\treturn true;\n \t}\n \treturn false;\n }\n \n+static bool mlx5_phys_mpesw_validate(struct mlx5_lag *ldev,\n+\t\t\t\t     struct mlx5_core_dev *dev0)\n+{\n+\tif (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||\n+\t    !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) ||\n+\t    !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) ||\n+\t    !mlx5_lag_check_prereq(ldev) ||\n+\t    !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL))\n+\t\treturn false;\n+\treturn true;\n+}\n+\n+static bool mlx5_virt_mpesw_validate(struct mlx5_lag *ldev,\n+\t\t\t\t     struct mlx5_core_dev *dev0)\n+{\n+\tif (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||\n+\t    !MLX5_CAP_PORT_SELECTION(dev0, port_select_eswitch) ||\n+\t    !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL))\n+\t\treturn false;\n+\treturn true;\n+}\n+\n static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)\n {\n-\tint idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);\n \tstruct mlx5_core_dev *dev0;\n+\tbool valid;\n+\tint idx;\n \tint err;\n \n \tif (ldev-\u003emode == MLX5_LAG_MODE_MPESW)\n@@ -119,20 +154,30 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)\n \tif (ldev-\u003emode != MLX5_LAG_MODE_NONE)\n \t\treturn -EINVAL;\n \n-\tif (idx \u003c 0)\n-\t\treturn -EINVAL;\n+\t/* VFs/SFs (nested LAG) are not marked by the LAG devcom PAIR event;\n+\t * select the master from the current snapshot before resolving it via\n+\t * mlx5_lag_get_dev_index_by_seq().\n+\t */\n+\tif (ldev-\u003evirt_lag)\n+\t\tmlx5_virt_lag_mark_master(ldev);\n \n-\tdev0 = mlx5_lag_pf(ldev, idx)-\u003edev;\n-\tif (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||\n-\t    !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) ||\n-\t    !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) ||\n-\t    !mlx5_lag_check_prereq(ldev) ||\n-\t    !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL))\n-\t\treturn -EOPNOTSUPP;\n+\tidx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);\n+\tif (idx \u003c 0) {\n+\t\terr = -EINVAL;\n+\t\tgoto err_clear_master;\n+\t}\n+\n+\tdev0 = mlx5_lag_fn(ldev, idx)-\u003edev;\n+\tvalid = ldev-\u003evirt_lag ? mlx5_virt_mpesw_validate(ldev, dev0) :\n+\t\tmlx5_phys_mpesw_validate(ldev, dev0);\n+\tif (!valid) {\n+\t\terr = -EOPNOTSUPP;\n+\t\tgoto err_clear_master;\n+\t}\n \n \terr = mlx5_mpesw_metadata_set(ldev);\n \tif (err)\n-\t\treturn err;\n+\t\tgoto err_clear_master;\n \n \tif (mlx5_lag_has_sd_group(ldev))\n \t\tmlx5_mpesw_teardown_sd_fdb(ldev);\n@@ -143,13 +188,19 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)\n \t\tmlx5_core_warn(dev0,\n \t\t\t       \"Failed to create LAG in MPESW mode (%d)\\n\",\n \t\t\t       err);\n-\t\tif (mlx5_lag_has_sd_group(ldev))\n-\t\t\tmlx5_mpesw_restore_sd_fdb(ldev);\n-\t\tmlx5_mpesw_metadata_cleanup(ldev);\n-\t\treturn err;\n+\t\tgoto err_restore;\n \t}\n \n \treturn 0;\n+\n+err_restore:\n+\tif (mlx5_lag_has_sd_group(ldev))\n+\t\tmlx5_mpesw_restore_sd_fdb(ldev);\n+\tmlx5_mpesw_metadata_cleanup(ldev);\n+err_clear_master:\n+\tif (ldev-\u003evirt_lag)\n+\t\tmlx5_lag_clear_master(ldev);\n+\treturn err;\n }\n \n void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev)\n@@ -157,10 +208,14 @@ void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev)\n \tif (ldev-\u003emode != MLX5_LAG_MODE_MPESW)\n \t\treturn;\n \n-\tmlx5_mpesw_metadata_cleanup(ldev);\n \tmlx5_lag_shared_fdb_destroy(ldev, MLX5_LAG_FILTER_ALL);\n \tif (mlx5_lag_has_sd_group(ldev))\n \t\tmlx5_mpesw_restore_sd_fdb(ldev);\n+\tmlx5_mpesw_metadata_cleanup(ldev);\n+\n+\t/* Pair with the master mark taken at MPESW create for VFs. */\n+\tif (ldev-\u003evirt_lag)\n+\t\tmlx5_lag_clear_master(ldev);\n }\n \n void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev)\n@@ -169,7 +224,7 @@ void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev)\n \tint i;\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tsd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)-\u003edev);\n+\t\tsd_devcom = mlx5_sd_get_devcom(mlx5_lag_fn(ldev, i)-\u003edev);\n \t\tif (sd_devcom)\n \t\t\tmlx5_devcom_comp_lock(sd_devcom);\n \t}\n@@ -180,8 +235,8 @@ void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev)\n \tstruct mlx5_devcom_comp_dev *sd_devcom;\n \tint i;\n \n-\tmlx5_ldev_for_each_reverse(i, MLX5_MAX_PORTS, 0, ldev) {\n-\t\tsd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)-\u003edev);\n+\tmlx5_ldev_for_each_reverse(i, ldev-\u003emax_fns, 0, ldev) {\n+\t\tsd_devcom = mlx5_sd_get_devcom(mlx5_lag_fn(ldev, i)-\u003edev);\n \t\tif (sd_devcom)\n \t\t\tmlx5_devcom_comp_unlock(sd_devcom);\n \t}\n@@ -281,7 +336,7 @@ bool mlx5_lag_is_mpesw(struct mlx5_core_dev *dev)\n \tstruct mlx5_lag *ldev = mlx5_lag_dev(dev);\n \n \treturn ldev \u0026\u0026 ldev-\u003emode == MLX5_LAG_MODE_MPESW \u0026\u0026\n-\t       __mlx5_lag_dev_is_port(ldev, dev);\n+\t       (ldev-\u003evirt_lag || __mlx5_lag_dev_is_port(ldev, dev));\n }\n EXPORT_SYMBOL(mlx5_lag_is_mpesw);\n \n@@ -305,10 +360,10 @@ int mlx5_lag_mpesw_port_change_event(struct notifier_block *nb,\n \t\t\t\t     unsigned long event, void *data)\n {\n \tstruct mlx5_nb *mlx5_nb = container_of(nb, struct mlx5_nb, nb);\n-\tstruct lag_func *lag_func = container_of(mlx5_nb,\n-\t\t\t\t\t\t struct lag_func,\n+\tstruct lag_fn *lag_fn = container_of(mlx5_nb,\n+\t\t\t\t\t\t struct lag_fn,\n \t\t\t\t\t\t port_change_nb);\n-\tstruct mlx5_core_dev *dev = lag_func-\u003edev;\n+\tstruct mlx5_core_dev *dev = lag_fn-\u003edev;\n \tstruct mlx5_lag *ldev = dev-\u003epriv.lag;\n \tstruct mlx5_eqe *eqe = data;\n \ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h\nindex 5099723ba0f749..6296e3edd38cdb 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h\n@@ -9,7 +9,7 @@\n \n struct lag_mpesw {\n \tstruct work_struct mpesw_work;\n-\tu32 pf_metadata[MLX5_MAX_PORTS];\n+\tu32 *pf_metadata;\n };\n \n enum mpesw_op {\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c\nindex 2a034b2a3eee3e..183e1891009513 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c\n@@ -50,7 +50,7 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev,\n \tif (first_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \tft_attr.max_fte = ldev-\u003eports * ldev-\u003ebuckets;\n \tft_attr.level = MLX5_LAG_FT_LEVEL_DEFINER;\n \n@@ -79,6 +79,7 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev,\n \tflow_act.flags |= FLOW_ACT_NO_APPEND;\n \tmlx5_ldev_for_each(i, 0, ldev) {\n \t\tfor (j = 0; j \u003c ldev-\u003ebuckets; j++) {\n+\t\t\tstruct mlx5_core_dev *mdev;\n \t\t\tu8 affinity;\n \n \t\t\tidx = i * ldev-\u003ebuckets + j;\n@@ -87,9 +88,8 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev,\n \t\t\t/* affinity is 1-indexed device index,\n \t\t\t * use reverse lookup.\n \t\t\t */\n-\t\t\tdest.vport.vhca_id =\n-\t\t\t\tMLX5_CAP_GEN(mlx5_lag_pf_by_dev_idx(ldev, affinity - 1)-\u003edev,\n-\t\t\t\t\t     vhca_id);\n+\t\t\tmdev = mlx5_lag_fn_by_dev_idx(ldev, affinity - 1)-\u003edev;\n+\t\t\tdest.vport.vhca_id = MLX5_CAP_GEN(mdev, vhca_id);\n \t\t\tlag_definer-\u003erules[idx] = mlx5_add_flow_rules(lag_definer-\u003eft,\n \t\t\t\t\t\t\t\t      NULL, \u0026flow_act,\n \t\t\t\t\t\t\t\t      \u0026dest, 1);\n@@ -311,7 +311,7 @@ mlx5_lag_create_definer(struct mlx5_lag *ldev, enum netdev_lag_hash hash,\n \tif (first_idx \u003c 0)\n \t\treturn ERR_PTR(-EINVAL);\n \n-\tdev = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \tlag_definer = kzalloc_obj(*lag_definer);\n \tif (!lag_definer)\n \t\treturn ERR_PTR(-ENOMEM);\n@@ -360,7 +360,7 @@ static void mlx5_lag_destroy_definer(struct mlx5_lag *ldev,\n \tif (first_idx \u003c 0)\n \t\treturn;\n \n-\tdev = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \tmlx5_ldev_for_each(i, 0, ldev) {\n \t\tfor (j = 0; j \u003c ldev-\u003ebuckets; j++) {\n \t\t\tidx = i * ldev-\u003ebuckets + j;\n@@ -524,7 +524,7 @@ static int mlx5_lag_create_ttc_table(struct mlx5_lag *ldev)\n \tif (first_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \tmlx5_lag_set_outer_ttc_params(ldev, \u0026ttc_params);\n \tport_sel-\u003eouter.ttc = mlx5_create_ttc_table(dev, \u0026ttc_params);\n \treturn PTR_ERR_OR_ZERO(port_sel-\u003eouter.ttc);\n@@ -540,7 +540,7 @@ static int mlx5_lag_create_inner_ttc_table(struct mlx5_lag *ldev)\n \tif (first_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev = mlx5_lag_pf(ldev, first_idx)-\u003edev;\n+\tdev = mlx5_lag_fn(ldev, first_idx)-\u003edev;\n \tmlx5_lag_set_inner_ttc_params(ldev, \u0026ttc_params);\n \tport_sel-\u003einner.ttc = mlx5_create_inner_ttc_table(dev, \u0026ttc_params);\n \treturn PTR_ERR_OR_ZERO(port_sel-\u003einner.ttc);\n@@ -594,6 +594,9 @@ static int __mlx5_lag_modify_definers_destinations(struct mlx5_lag *ldev,\n \n \tmlx5_ldev_for_each(i, 0, ldev) {\n \t\tfor (j = 0; j \u003c ldev-\u003ebuckets; j++) {\n+\t\t\tstruct mlx5_core_dev *mdev;\n+\t\t\tu8 affinity;\n+\n \t\t\tidx = i * ldev-\u003ebuckets + j;\n \t\t\tif (ldev-\u003ev2p_map[idx] == ports[idx])\n \t\t\t\tcontinue;\n@@ -601,9 +604,9 @@ static int __mlx5_lag_modify_definers_destinations(struct mlx5_lag *ldev,\n \t\t\t/* ports[] contains 1-indexed device indices,\n \t\t\t * use reverse lookup.\n \t\t\t */\n-\t\t\tdest.vport.vhca_id =\n-\t\t\t\tMLX5_CAP_GEN(mlx5_lag_pf_by_dev_idx(ldev, ports[idx] - 1)-\u003edev,\n-\t\t\t\t\t     vhca_id);\n+\t\t\taffinity = ports[idx];\n+\t\t\tmdev = mlx5_lag_fn_by_dev_idx(ldev, affinity - 1)-\u003edev;\n+\t\t\tdest.vport.vhca_id = MLX5_CAP_GEN(mdev, vhca_id);\n \t\t\terr = mlx5_modify_rule_destination(def-\u003erules[idx], \u0026dest, NULL);\n \t\t\tif (err)\n \t\t\t\treturn err;\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c\nindex 7c7e6c832a93d9..90209e4b99f78d 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c\n@@ -19,11 +19,11 @@ bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter)\n \tif (idx \u003c 0)\n \t\treturn false;\n \n-\tdev0 = mlx5_lag_pf(ldev, idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, idx)-\u003edev;\n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n \t\tif (i == idx)\n \t\t\tcontinue;\n-\t\tdev = mlx5_lag_pf(ldev, i)-\u003edev;\n+\t\tdev = mlx5_lag_fn(ldev, i)-\u003edev;\n \t\tif (is_mdev_switchdev_mode(dev) \u0026\u0026\n \t\t    mlx5_eswitch_vport_match_metadata_enabled(dev-\u003epriv.eswitch) \u0026\u0026\n \t\t    MLX5_CAP_GEN(dev, lag_native_fdb_selection) \u0026\u0026\n@@ -59,7 +59,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)\n \tif (master_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, master_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, master_idx)-\u003edev;\n \tmaster_esw = dev0-\u003epriv.eswitch;\n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n \t\tstruct mlx5_eswitch *slave_esw;\n@@ -67,11 +67,11 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)\n \t\tif (i == master_idx)\n \t\t\tcontinue;\n \n-\t\tslave_esw = mlx5_lag_pf(ldev, i)-\u003edev-\u003epriv.eswitch;\n+\t\tslave_esw = mlx5_lag_fn(ldev, i)-\u003edev-\u003epriv.eswitch;\n \n \t\terr = mlx5_eswitch_offloads_single_fdb_add_one(master_esw,\n \t\t\t\t\t\t\t       slave_esw,\n-\t\t\t\t\t\t\t       ldev-\u003eports);\n+\t\t\t\t\t\t\t       ldev-\u003emax_fns);\n \t\tif (err)\n \t\t\tgoto err;\n \t}\n@@ -82,7 +82,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)\n \n \t\tif (j == master_idx)\n \t\t\tcontinue;\n-\t\tslave_esw = mlx5_lag_pf(ldev, j)-\u003edev-\u003epriv.eswitch;\n+\t\tslave_esw = mlx5_lag_fn(ldev, j)-\u003edev-\u003epriv.eswitch;\n \t\tmlx5_eswitch_offloads_single_fdb_del_one(master_esw, slave_esw);\n \t}\n \treturn err;\n@@ -101,7 +101,7 @@ int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)\n \tif (master_idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, master_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, master_idx)-\u003edev;\n \tmaster_esw = dev0-\u003epriv.eswitch;\n \n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n@@ -110,7 +110,7 @@ int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)\n \t\tif (i == master_idx)\n \t\t\tcontinue;\n \n-\t\tslave_esw = mlx5_lag_pf(ldev, i)-\u003edev-\u003epriv.eswitch;\n+\t\tslave_esw = mlx5_lag_fn(ldev, i)-\u003edev-\u003epriv.eswitch;\n \t\terr = mlx5_eswitch_offloads_vport_lag_add_one(master_esw,\n \t\t\t\t\t\t\t      slave_esw);\n \t\tif (err)\n@@ -125,8 +125,9 @@ int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)\n \n \t\tif (j == master_idx)\n \t\t\tcontinue;\n-\t\tslave_esw = mlx5_lag_pf(ldev, j)-\u003edev-\u003epriv.eswitch;\n-\t\tmlx5_eswitch_offloads_vport_lag_del_one(master_esw, slave_esw);\n+\t\tslave_esw = mlx5_lag_fn(ldev, j)-\u003edev-\u003epriv.eswitch;\n+\t\tmlx5_eswitch_offloads_vport_lag_del_one(master_esw, slave_esw,\n+\t\t\t\t\t\t\tldev-\u003emax_fns);\n \t}\n \treturn err;\n }\n@@ -143,7 +144,7 @@ int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id)\n \tif (master_idx \u003c 0)\n \t\treturn 0;\n \n-\tdev0 = mlx5_lag_pf(ldev, master_idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, master_idx)-\u003edev;\n \tmaster_esw = dev0-\u003epriv.eswitch;\n \n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n@@ -151,9 +152,10 @@ int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id)\n \n \t\tif (i == master_idx)\n \t\t\tcontinue;\n-\t\tdev = mlx5_lag_pf(ldev, i)-\u003edev;\n+\t\tdev = mlx5_lag_fn(ldev, i)-\u003edev;\n \t\tmlx5_eswitch_offloads_vport_lag_del_one(master_esw,\n-\t\t\t\t\t\t\tdev-\u003epriv.eswitch);\n+\t\t\t\t\t\t\tdev-\u003epriv.eswitch,\n+\t\t\t\t\t\t\tldev-\u003emax_fns);\n \t}\n \treturn 0;\n }\n@@ -170,12 +172,12 @@ static void mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag *ldev,\n \tif (master_idx \u003c 0)\n \t\treturn;\n \n-\tmaster_esw = mlx5_lag_pf(ldev, master_idx)-\u003edev-\u003epriv.eswitch;\n+\tmaster_esw = mlx5_lag_fn(ldev, master_idx)-\u003edev-\u003epriv.eswitch;\n \tmlx5_lag_for_each(i, 0, ldev, filter) {\n \t\tif (i == master_idx)\n \t\t\tcontinue;\n \n-\t\tpeer_esw = mlx5_lag_pf(ldev, i)-\u003edev-\u003epriv.eswitch;\n+\t\tpeer_esw = mlx5_lag_fn(ldev, i)-\u003edev-\u003epriv.eswitch;\n \t\tmlx5_eswitch_offloads_single_fdb_del_one(master_esw, peer_esw);\n \t}\n }\n@@ -214,14 +216,14 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,\n \tint idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,\n \t\t\t\t\t\t       filter);\n \tstruct mlx5_core_dev *dev0;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint err;\n \tint i;\n \n \tif (idx \u003c 0)\n \t\treturn -EINVAL;\n \n-\tdev0 = mlx5_lag_pf(ldev, idx)-\u003edev;\n+\tdev0 = mlx5_lag_fn(ldev, idx)-\u003edev;\n \n \tmlx5_lag_remove_devices_filter(ldev, filter);\n \n@@ -242,8 +244,8 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,\n \t\t\tgoto err_add_devices;\n \t\t}\n \t\tmlx5_lag_for_each(i, 0, ldev, filter) {\n-\t\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\t\tpf-\u003esd_fdb_active = true;\n+\t\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\t\tfn-\u003esd_fdb_active = true;\n \t\t}\n \t\tBLOCKING_INIT_NOTIFIER_HEAD(\u0026dev0-\u003epriv.lag_nh);\n \t}\n@@ -265,8 +267,8 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,\n \t\tmlx5_deactivate_lag(ldev);\n \t} else {\n \t\tmlx5_lag_for_each(i, 0, ldev, filter) {\n-\t\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\t\tpf-\u003esd_fdb_active = false;\n+\t\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\t\tfn-\u003esd_fdb_active = false;\n \t\t}\n \t\tmlx5_lag_destroy_single_fdb_filter(ldev, group_id);\n \t\tmlx5_lag_unload_reps_from_locked(ldev, filter);\n@@ -280,7 +282,7 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,\n void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)\n {\n \tu32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint err;\n \tint i;\n \n@@ -292,8 +294,8 @@ void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)\n \t\t\treturn;\n \t} else {\n \t\tmlx5_lag_for_each(i, 0, ldev, filter) {\n-\t\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\t\tpf-\u003esd_fdb_active = false;\n+\t\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\t\tfn-\u003esd_fdb_active = false;\n \t\t}\n \t\tmlx5_lag_destroy_single_fdb_filter(ldev, group_id);\n \t\tmlx5_lag_unload_reps_from_locked(ldev, filter);\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c\nindex 4cdc50cd6f0382..f843efbca660cf 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c\n@@ -300,7 +300,7 @@ static int sd_lag_state_show(struct seq_file *file, void *priv)\n {\n \tstruct mlx5_core_dev *dev = file-\u003eprivate;\n \tstruct mlx5_lag *ldev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tbool active = false;\n \tint i;\n \n@@ -310,9 +310,9 @@ static int sd_lag_state_show(struct seq_file *file, void *priv)\n \n \tmutex_lock(\u0026ldev-\u003elock);\n \tmlx5_ldev_for_each(i, 0, ldev) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003edev == dev) {\n-\t\t\tactive = pf-\u003esd_fdb_active;\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003edev == dev) {\n+\t\t\tactive = fn-\u003esd_fdb_active;\n \t\t\tbreak;\n \t\t}\n \t}\n@@ -334,7 +334,7 @@ static void sd_lag_init(struct mlx5_core_dev *dev)\n \tstruct mlx5_sd *sd = mlx5_get_sd(primary);\n \tstruct mlx5_core_dev *pos, *to;\n \tstruct mlx5_lag *ldev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint err;\n \tint i;\n \n@@ -346,14 +346,14 @@ static void sd_lag_init(struct mlx5_core_dev *dev)\n \t}\n \n \tmutex_lock(\u0026ldev-\u003elock);\n-\tpf = mlx5_lag_pf_by_dev(ldev, primary);\n-\tif (!pf) {\n+\tfn = mlx5_lag_fn_by_dev(ldev, primary);\n+\tif (!fn) {\n \t\tsd_warn(primary, \"%s: primary not registered in ldev, skipping\\n\",\n \t\t\t__func__);\n \t\tgoto out;\n \t}\n \n-\tpf-\u003egroup_id = sd-\u003egroup_id;\n+\tfn-\u003egroup_id = sd-\u003egroup_id;\n \n \tmlx5_sd_for_each_secondary(i, primary, pos) {\n \t\terr = mlx5_ldev_add_mdev(ldev, pos, sd-\u003egroup_id);\n@@ -372,7 +372,7 @@ static void sd_lag_init(struct mlx5_core_dev *dev)\n \tto = pos;\n \tmlx5_sd_for_each_secondary_to(i, primary, to, pos)\n \t\tmlx5_ldev_remove_mdev(ldev, pos);\n-\tpf-\u003egroup_id = 0;\n+\tfn-\u003egroup_id = 0;\n \tmutex_unlock(\u0026ldev-\u003elock);\n }\n \n@@ -381,7 +381,7 @@ static void sd_lag_cleanup(struct mlx5_core_dev *dev)\n \tstruct mlx5_core_dev *primary = mlx5_sd_get_primary(dev);\n \tstruct mlx5_core_dev *pos;\n \tstruct mlx5_lag *ldev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint i;\n \n \tldev = mlx5_lag_dev(primary);\n@@ -392,9 +392,9 @@ static void sd_lag_cleanup(struct mlx5_core_dev *dev)\n \tmlx5_sd_for_each_secondary(i, primary, pos)\n \t\tmlx5_ldev_remove_mdev(ldev, pos);\n \n-\tpf = mlx5_lag_pf_by_dev(ldev, primary);\n-\tif (pf)\n-\t\tpf-\u003egroup_id = 0;\n+\tfn = mlx5_lag_fn_by_dev(ldev, primary);\n+\tif (fn)\n+\t\tfn-\u003egroup_id = 0;\n \tmutex_unlock(\u0026ldev-\u003elock);\n }\n \n@@ -989,7 +989,7 @@ static void mlx5_sd_activate_shared_fdb(struct mlx5_core_dev *primary)\n \tstruct mlx5_sd *sd = mlx5_get_sd(primary);\n \tstruct mlx5_core_dev *pos;\n \tstruct mlx5_lag *ldev;\n-\tstruct lag_func *pf;\n+\tstruct lag_fn *fn;\n \tint err;\n \tint i;\n \n@@ -1009,8 +1009,8 @@ static void mlx5_sd_activate_shared_fdb(struct mlx5_core_dev *primary)\n \n \t/* Check if SD FDB is already active for this group */\n \tmlx5_lag_for_each(i, 0, ldev, sd-\u003egroup_id) {\n-\t\tpf = mlx5_lag_pf(ldev, i);\n-\t\tif (pf-\u003esd_fdb_active)\n+\t\tfn = mlx5_lag_fn(ldev, i);\n+\t\tif (fn-\u003esd_fdb_active)\n \t\t\tgoto unlock;\n \t\tbreak;\n \t}\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h\nindex d84d0424d04e1c..f5f105762d884c 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h\n@@ -275,6 +275,7 @@ void mlx5_lag_add_mdev(struct mlx5_core_dev *dev);\n void mlx5_lag_remove_mdev(struct mlx5_core_dev *dev);\n void mlx5_lag_disable_change(struct mlx5_core_dev *dev);\n void mlx5_lag_enable_change(struct mlx5_core_dev *dev);\n+int mlx5_lag_get_member_idx(struct mlx5_core_dev *dev);\n \n int mlx5_events_init(struct mlx5_core_dev *dev);\n void mlx5_events_cleanup(struct mlx5_core_dev *dev);\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h\nindex 3ab0449c770cbb..382162baabdb35 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h\n@@ -19,6 +19,13 @@ struct mlx5_sf_dev {\n \tu16 fn_id;\n };\n \n+static inline u32 mlx5_sf_coredev_sfnum(struct mlx5_core_dev *mdev)\n+{\n+\tstruct auxiliary_device *adev = mlx5_sf_coredev_to_adev(mdev);\n+\n+\treturn container_of(adev, struct mlx5_sf_dev, adev)-\u003esfnum;\n+}\n+\n struct mlx5_sf_peer_devlink_event_ctx {\n \tu16 fn_id;\n \tstruct devlink *devlink;\n@@ -68,6 +75,11 @@ static inline bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev)\n \treturn false;\n }\n \n+static inline u32 mlx5_sf_coredev_sfnum(struct mlx5_core_dev *mdev)\n+{\n+\treturn 0;\n+}\n+\n #endif\n \n #endif\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c\nindex 78f3d15b599d00..50d2609374c4b1 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c\n@@ -38,6 +38,7 @@\n #include \"mlx5_core.h\"\n #include \"eswitch.h\"\n #include \"sf/sf.h\"\n+#include \"lag/lag.h\"\n \n /* Mutex to hold while enabling or disabling RoCE */\n static DEFINE_MUTEX(mlx5_roce_en_lock);\n@@ -1308,10 +1309,37 @@ u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev)\n }\n EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid);\n \n+static u8 mlx5_sw_guid_load_balance(const struct mlx5_core_dev *mdev)\n+{\n+\tif (MLX5_CAP_GEN_2(mdev, load_balance_id) \u0026\u0026\n+\t    MLX5_CAP_GEN_2(mdev, lag_per_mp_group))\n+\t\treturn MLX5_CAP_GEN_2(mdev, load_balance_id);\n+\treturn 0;\n+}\n+\n+static u8 mlx5_sw_guid_sf(const struct mlx5_core_dev *mdev)\n+{\n+\treturn mlx5_core_is_sf(mdev) \u0026\u0026 mlx5_virt_lag_is_supported(mdev);\n+}\n+\n+static u8 mlx5_sw_guid_vf(const struct mlx5_core_dev *mdev)\n+{\n+\treturn mlx5_core_is_vf(mdev) \u0026\u0026 mlx5_virt_lag_is_supported(mdev);\n+}\n+\n+static u8 (* const mlx5_sw_guid_fields[])(const struct mlx5_core_dev *mdev) = {\n+\tmlx5_sw_guid_load_balance,\n+\tmlx5_sw_guid_vf,\n+\tmlx5_sw_guid_sf,\n+\t/* New fields should be added at the end */\n+};\n+\n void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf,\n \t\t\t\t\t u8 *len)\n {\n \tu64 fw_system_image_guid;\n+\tu8 trim_len;\n+\tint i;\n \n \t*len = 0;\n \n@@ -1321,10 +1349,17 @@ void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf,\n \n \tmemcpy(buf, \u0026fw_system_image_guid, sizeof(fw_system_image_guid));\n \t*len += sizeof(fw_system_image_guid);\n+\ttrim_len = *len;\n \n-\tif (MLX5_CAP_GEN_2(mdev, load_balance_id) \u0026\u0026\n-\t    MLX5_CAP_GEN_2(mdev, lag_per_mp_group))\n-\t\tbuf[(*len)++] = MLX5_CAP_GEN_2(mdev, load_balance_id);\n+\tfor (i = 0; i \u003c ARRAY_SIZE(mlx5_sw_guid_fields); i++) {\n+\t\tu8 val = mlx5_sw_guid_fields[i](mdev);\n+\n+\t\tbuf[(*len)++] = val;\n+\t\tif (val)\n+\t\t\ttrim_len = *len;\n+\t}\n+\n+\t*len = trim_len;\n }\n \n bool mlx5_vport_use_vhca_id_as_func_id(struct mlx5_core_dev *dev,\ndiff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h\nindex abb99371ff1344..d9d91faa0a1dee 100644\n--- a/include/linux/mlx5/driver.h\n+++ b/include/linux/mlx5/driver.h\n@@ -1395,7 +1395,7 @@ static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev)\n \treturn devlink_net(priv_to_devlink(dev));\n }\n \n-#define MLX5_SW_IMAGE_GUID_MAX_BYTES 9\n+#define MLX5_SW_IMAGE_GUID_MAX_BYTES 11\n \n static inline bool mlx5_data_direct_supported(struct mlx5_core_dev *mdev)\n {\n"
}

2/1 2026/11/05 18:30 agent patch-evaluator
7m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Mellanox mlx5 core driver (specifically virt LAG and eswitch offload logic). Mellanox ConnectX/BlueField PCIe network devices require vendor-specific physical hardware that is not emulated in standard virtualized QEMU or GCE fuzzing environments. Consequently, the driver cannot bind to any device, making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 1406b515a69b4efca577c7d8754f446fd765e58a
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Nov 5 18:30:20 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c
index 8818f65d1fbc4c..79adb1ecc4506f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c
@@ -63,6 +63,11 @@ int mlx5e_devlink_port_register(struct mlx5e_dev *mlx5e_dev,
 		dl_port_index = mlx5_esw_vport_to_devlink_port_index(mdev,
 								     MLX5_VPORT_UPLINK);
 	} else {
+		if (MLX5_ESWITCH_MANAGER(mdev)) {
+			mlx5e_devlink_get_port_parent_id(mdev, &ppid);
+			memcpy(attrs.switch_id.id, ppid.id, ppid.id_len);
+			attrs.switch_id.id_len = ppid.id_len;
+		}
 		attrs.flavour = DEVLINK_PORT_FLAVOUR_VIRTUAL;
 		dl_port_index = mlx5_esw_vport_to_devlink_port_index(mdev, 0);
 	}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index e9cbcd73b23ffa..55f5ea522d4655 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -957,7 +957,8 @@ bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 cont
 int mlx5_eswitch_offloads_vport_lag_add_one(struct mlx5_eswitch *master_esw,
 					    struct mlx5_eswitch *slave_esw);
 void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw,
-					     struct mlx5_eswitch *slave_esw);
+					     struct mlx5_eswitch *slave_esw,
+					     int max_slaves);
 int mlx5_eswitch_offloads_single_fdb_add_one(struct mlx5_eswitch *master_esw,
 					     struct mlx5_eswitch *slave_esw, int max_slaves);
 void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index c712848b202db4..f1a5d7d036cb5c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -3000,6 +3000,26 @@ static int esw_set_slave_root_fdb(struct mlx5_core_dev *master,
 	return err;
 }
 
+static void mlx5_esw_set_spec_source_port_vhca(struct mlx5_flow_spec *spec,
+					       u16 vport, u16 vhca_id)
+{
+	void *misc;
+
+	spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
+
+	misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
+			    misc_parameters);
+	MLX5_SET(fte_match_set_misc, misc, source_port, vport);
+	MLX5_SET(fte_match_set_misc, misc, source_eswitch_owner_vhca_id,
+		 vhca_id);
+
+	misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
+			    misc_parameters);
+	MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
+	MLX5_SET_TO_ONES(fte_match_set_misc, misc,
+			 source_eswitch_owner_vhca_id);
+}
+
 static int __esw_set_master_egress_rule(struct mlx5_core_dev *master,
 					struct mlx5_core_dev *slave,
 					struct mlx5_vport *vport,
@@ -3011,22 +3031,22 @@ static int __esw_set_master_egress_rule(struct mlx5_core_dev *master,
 	struct mlx5_flow_act flow_act = {};
 	struct mlx5_flow_spec *spec;
 	int err = 0;
-	void *misc;
+
+	if (mlx5_virt_lag_is_supported(master) &&
+	    !mlx5_eswitch_vport_match_metadata_enabled(slave->priv.eswitch))
+		return -EINVAL;
 
 	spec = kvzalloc_obj(*spec);
 	if (!spec)
 		return -ENOMEM;
 
-	spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS;
-	misc = MLX5_ADDR_OF(fte_match_param, spec->match_value,
-			    misc_parameters);
-	MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_UPLINK);
-	MLX5_SET(fte_match_set_misc, misc, source_eswitch_owner_vhca_id, slave_index);
-
-	misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
-	MLX5_SET_TO_ONES(fte_match_set_misc, misc, source_port);
-	MLX5_SET_TO_ONES(fte_match_set_misc, misc,
-			 source_eswitch_owner_vhca_id);
+	if (mlx5_virt_lag_is_supported(master) &&
+	    mlx5_eswitch_vport_match_metadata_enabled(slave->priv.eswitch))
+		mlx5_esw_set_spec_source_port(slave->priv.eswitch,
+					      MLX5_VPORT_UPLINK, spec);
+	else
+		mlx5_esw_set_spec_source_port_vhca(spec, MLX5_VPORT_UPLINK,
+						   slave_index);
 
 	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
 	dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
@@ -3179,6 +3199,21 @@ static void esw_unset_slave_egress_rule(struct mlx5_core_dev *master,
 	esw_slave_egress_destroy_resources(slave_vport);
 }
 
+static void mlx5_esw_set_flow_group_source_port_vhca(u32 *flow_group_in)
+{
+	void *match_criteria = MLX5_ADDR_OF(create_flow_group_in,
+					    flow_group_in, match_criteria);
+
+	MLX5_SET_TO_ONES(fte_match_param, match_criteria,
+			 misc_parameters.source_port);
+	MLX5_SET_TO_ONES(fte_match_param, match_criteria,
+			 misc_parameters.source_eswitch_owner_vhca_id);
+	MLX5_SET(create_flow_group_in, flow_group_in,
+		 match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS);
+	MLX5_SET(create_flow_group_in, flow_group_in,
+		 source_eswitch_owner_vhca_id_valid, 1);
+}
+
 static int esw_master_egress_create_resources(struct mlx5_eswitch *esw,
 					      struct mlx5_flow_namespace *egress_ns,
 					      struct mlx5_vport *vport, size_t count)
@@ -3189,13 +3224,16 @@ static int esw_master_egress_create_resources(struct mlx5_eswitch *esw,
 	};
 	struct mlx5_flow_table *acl;
 	struct mlx5_flow_group *g;
-	void *match_criteria;
 	u32 *flow_group_in;
 	int err;
 
 	if (vport->egress.acl)
 		return 0;
 
+	if (mlx5_virt_lag_is_supported(esw->dev) &&
+	    !mlx5_eswitch_vport_match_metadata_enabled(esw))
+		return -EINVAL;
+
 	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
 	if (!flow_group_in)
 		return -ENOMEM;
@@ -3209,17 +3247,11 @@ static int esw_master_egress_create_resources(struct mlx5_eswitch *esw,
 		goto out;
 	}
 
-	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in,
-				      match_criteria);
-	MLX5_SET_TO_ONES(fte_match_param, match_criteria,
-			 misc_parameters.source_port);
-	MLX5_SET_TO_ONES(fte_match_param, match_criteria,
-			 misc_parameters.source_eswitch_owner_vhca_id);
-	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
-		 MLX5_MATCH_MISC_PARAMETERS);
-
-	MLX5_SET(create_flow_group_in, flow_group_in,
-		 source_eswitch_owner_vhca_id_valid, 1);
+	if (mlx5_virt_lag_is_supported(esw->dev) &&
+	    mlx5_eswitch_vport_match_metadata_enabled(esw))
+		mlx5_esw_set_flow_group_source_port(esw, flow_group_in, 0);
+	else
+		mlx5_esw_set_flow_group_source_port_vhca(flow_group_in);
 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, count);
 
@@ -3349,12 +3381,32 @@ void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw,
 int mlx5_eswitch_offloads_vport_lag_add_one(struct mlx5_eswitch *master_esw,
 					    struct mlx5_eswitch *slave_esw)
 {
-	return esw_set_slave_egress_rule(master_esw->dev, slave_esw->dev);
+	int err;
+
+	err = esw_set_slave_egress_rule(master_esw->dev, slave_esw->dev);
+	if (err)
+		return err;
+
+	esw_unset_master_egress_rule(master_esw->dev, slave_esw->dev);
+
+	return 0;
 }
 
 void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw,
-					     struct mlx5_eswitch *slave_esw)
+					     struct mlx5_eswitch *slave_esw,
+					     int max_slaves)
 {
+	int err;
+
+	if (mlx5_sd_is_primary(slave_esw->dev)) {
+		err = esw_set_master_egress_rule(master_esw->dev,
+						 slave_esw->dev, max_slaves);
+		if (err)
+			esw_warn(master_esw->dev,
+				 "Failed to restore master egress rule (%d)\n",
+				 err);
+	}
+
 	esw_unset_slave_egress_rule(master_esw->dev, slave_esw->dev);
 }
 
@@ -3573,6 +3625,10 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
 	xa_init(&esw->offloads.peer_flows);
 	mutex_init(&esw->offloads.peer_mutex);
 
+	/* do not pair a virt LAG capable VF/SF that is not part of a LAG */
+	if (!mlx5_core_is_pf(esw->dev) && mlx5_virt_lag_nonmember(esw->dev))
+		return;
+
 	if (!MLX5_CAP_ESW(esw->dev, merged_eswitch))
 		return;
 
@@ -3647,6 +3703,48 @@ static u32 mlx5_esw_match_metadata_reserved(struct mlx5_eswitch *esw)
 	return MLX5_ESW_METADATA_RSVD_UPLINK;
 }
 
+/* Nested (virt LAG) source-port metadata layout: the upper ESW_NESTED_IDX_BITS
+ * hold the VF/SF's slot in the LAG and the lower ESW_NESTED_VPORT_BITS hold a
+ * vport id unique within its eswitch. The two fields still sum to
+ * ESW_SOURCE_PORT_METADATA_BITS. The LAG slot (0-254, per the xa_alloc() limit
+ * in mlx5_ldev_add_mdev()) fits in ESW_NESTED_IDX_BITS; slots 0xf0 and up would
+ * drive the PFNUM bits to the 0xf reserved by internal port offload, so only
+ * 0-239 are used.
+ */
+#define ESW_NESTED_VPORT_BITS 8
+#define ESW_NESTED_IDX_BITS (ESW_SOURCE_PORT_METADATA_BITS - ESW_NESTED_VPORT_BITS)
+
+static u32 mlx5_nested_esw_match_metadata_alloc(struct mlx5_eswitch *esw)
+{
+	u32 vport_end_ida = (1 << ESW_NESTED_VPORT_BITS) - 1;
+	u32 max_idx = (1 << ESW_NESTED_IDX_BITS) -
+		      (1 << (ESW_NESTED_IDX_BITS - ESW_PFNUM_BITS)) - 1;
+	int idx;
+	int id;
+
+	idx = mlx5_lag_get_member_idx(esw->dev);
+	if (idx < 0 || idx > max_idx)
+		return 0;
+
+	/* Use only non-zero id's (2-255). */
+	id = ida_alloc_range(&esw->offloads.vport_metadata_ida,
+			     MLX5_ESW_METADATA_RSVD_UPLINK + 1,
+			     vport_end_ida, GFP_KERNEL);
+	if (id < 0)
+		return 0;
+	id = (idx << ESW_NESTED_VPORT_BITS) | id;
+	return id;
+}
+
+static void mlx5_nested_esw_match_metadata_free(struct mlx5_eswitch *esw,
+						u32 metadata)
+{
+	u32 vport_bit_mask = (1 << ESW_NESTED_VPORT_BITS) - 1;
+
+	/* Metadata holds only ESW_NESTED_VPORT_BITS of actual ida id. */
+	ida_free(&esw->offloads.vport_metadata_ida, metadata & vport_bit_mask);
+}
+
 u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw)
 {
 	u32 vport_end_ida = (1 << ESW_VPORT_BITS) - 1;
@@ -3655,6 +3753,9 @@ u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw)
 	int pf_num;
 	int id;
 
+	if (!mlx5_core_is_pf(esw->dev) && !mlx5_virt_lag_nonmember(esw->dev))
+		return mlx5_nested_esw_match_metadata_alloc(esw);
+
 	/* Only 4 bits of pf_num */
 	pf_num = mlx5_sd_pf_num_get(esw->dev);
 	if (pf_num < 0 || pf_num > max_pf_num)
@@ -3675,6 +3776,9 @@ void mlx5_esw_match_metadata_free(struct mlx5_eswitch *esw, u32 metadata)
 {
 	u32 vport_bit_mask = (1 << ESW_VPORT_BITS) - 1;
 
+	if (!mlx5_core_is_pf(esw->dev) && !mlx5_virt_lag_nonmember(esw->dev))
+		return mlx5_nested_esw_match_metadata_free(esw, metadata);
+
 	/* Metadata contains only 12 bits of actual ida id */
 	ida_free(&esw->offloads.vport_metadata_ida, metadata & vport_bit_mask);
 }
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
index b6125c7bd91bb6..116432cb7ce53c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
@@ -154,7 +154,7 @@ static int members_show(struct seq_file *file, void *priv)
 	mutex_lock(&ldev->lock);
 	mlx5_ldev_for_each(i, 0, ldev)
 		seq_printf(file, "%s\n",
-			   dev_name(mlx5_lag_pf(ldev, i)->dev->device));
+			   dev_name(mlx5_lag_fn(ldev, i)->dev->device));
 	mutex_unlock(&ldev->lock);
 
 	return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index 24086267b4a7f5..5532b2505161c7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -39,6 +39,7 @@
 #include "lib/mlx5.h"
 #include "lib/devcom.h"
 #include "mlx5_core.h"
+#include "sf/dev/dev.h"
 #include "eswitch.h"
 #include "esw/acl/ofld.h"
 #include "lag.h"
@@ -94,23 +95,25 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,
 	int port_sel_mode = get_port_sel_mode(mode, flags);
 	u32 in[MLX5_ST_SZ_DW(create_lag_in)] = {};
 	u8 *ports = ldev->v2p_map;
-	int idx0, idx1;
 	void *lag_ctx;
 
 	lag_ctx = MLX5_ADDR_OF(create_lag_in, in, ctx);
 	MLX5_SET(create_lag_in, in, opcode, MLX5_CMD_OP_CREATE_LAG);
 	MLX5_SET(lagc, lag_ctx, fdb_selection_mode, fdb_sel_mode);
-	idx0 = mlx5_lag_get_dev_index_by_seq(ldev, 0);
-	idx1 = mlx5_lag_get_dev_index_by_seq(ldev, 1);
+	switch (port_sel_mode) {
+	case MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY: {
+		int idx0, idx1;
 
-	if (idx0 < 0 || idx1 < 0)
-		return -EINVAL;
+		idx0 = mlx5_lag_get_dev_index_by_seq(ldev, 0);
+		idx1 = mlx5_lag_get_dev_index_by_seq(ldev, 1);
+
+		if (idx0 < 0 || idx1 < 0)
+			return -EINVAL;
 
-	switch (port_sel_mode) {
-	case MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY:
 		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: {
 		u8 active_port;
 		int ret;
@@ -158,23 +161,25 @@ static int mlx5_cmd_modify_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,
 static u32 mlx5_lag_dev_group_id(struct mlx5_core_dev *dev)
 {
 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	if (!ldev)
 		return 0;
 
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev == dev)
-			return pf->sd_fdb_active ? pf->group_id : 0;
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev == dev)
+			return fn->sd_fdb_active ? fn->group_id : 0;
 	}
 	return 0;
 }
 
-static int mlx5_lag_is_sw_lag(struct mlx5_core_dev *dev)
+bool mlx5_lag_is_sw_managed(struct mlx5_core_dev *dev)
 {
-	return mlx5_lag_is_sd(dev);
+	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
+
+	return mlx5_lag_is_sd(dev) || (ldev && ldev->virt_lag);
 }
 
 int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev)
@@ -183,7 +188,7 @@ int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev)
 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
 	int ret;
 
-	if (mlx5_lag_is_sw_lag(dev)) {
+	if (mlx5_lag_is_sw_managed(dev)) {
 		if (!ldev)
 			return -ENODEV;
 
@@ -205,7 +210,7 @@ int mlx5_cmd_destroy_vport_lag(struct mlx5_core_dev *dev)
 	u32 in[MLX5_ST_SZ_DW(destroy_vport_lag_in)] = {};
 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
 
-	if (mlx5_lag_is_sw_lag(dev)) {
+	if (mlx5_lag_is_sw_managed(dev)) {
 		if (!ldev)
 			return 0;
 
@@ -310,7 +315,7 @@ static void mlx5_do_bond_work(struct work_struct *work);
 static void mlx5_ldev_free(struct kref *ref)
 {
 	struct mlx5_lag *ldev = container_of(ref, struct mlx5_lag, ref);
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	struct net *net;
 	int i;
 
@@ -320,16 +325,16 @@ static void mlx5_ldev_free(struct kref *ref)
 	}
 
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->port_change_nb.nb.notifier_call) {
-			struct mlx5_nb *nb = &pf->port_change_nb;
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->port_change_nb.nb.notifier_call) {
+			struct mlx5_nb *nb = &fn->port_change_nb;
 
-			mlx5_eq_notifier_unregister(pf->dev, nb);
+			mlx5_eq_notifier_unregister(fn->dev, nb);
 		}
-		xa_erase(&ldev->pfs, i);
-		kfree(pf);
+		xa_erase(&ldev->fns, i);
+		kfree(fn);
 	}
-	xa_destroy(&ldev->pfs);
+	xa_destroy(&ldev->fns);
 
 	mlx5_lag_mp_cleanup(ldev);
 	cancel_delayed_work_sync(&ldev->bond_work);
@@ -361,6 +366,7 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
 
 	ldev->ports = MLX5_CAP_GEN(dev, num_lag_ports);
 	ldev->buckets = 1;
+	ldev->virt_lag = mlx5_virt_lag_is_supported(dev);
 	ldev->v2p_map = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
 				sizeof(*ldev->v2p_map), GFP_KERNEL);
 	if (!ldev->v2p_map)
@@ -372,11 +378,11 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
 
 	kref_init(&ldev->ref);
 	mutex_init(&ldev->lock);
-	xa_init_flags(&ldev->pfs, XA_FLAGS_ALLOC);
+	xa_init_flags(&ldev->fns, XA_FLAGS_ALLOC);
 	INIT_DELAYED_WORK(&ldev->bond_work, mlx5_do_bond_work);
 	INIT_WORK(&ldev->speed_update_work, mlx5_mpesw_speed_update_work);
 
-	if (!mlx5_sd_is_supported(dev)) {
+	if (!mlx5_sd_is_supported(dev) && !ldev->virt_lag) {
 		ldev->nb.notifier_call = mlx5_lag_netdev_event;
 		write_pnet(&ldev->net, mlx5_core_net(dev));
 		if (register_netdevice_notifier_net(read_pnet(&ldev->net),
@@ -392,6 +398,7 @@ 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->max_fns = ldev->ports;
 	return ldev;
 
 err_wq:
@@ -404,12 +411,12 @@ static struct mlx5_lag *mlx5_lag_dev_alloc(struct mlx5_core_dev *dev)
 int mlx5_lag_dev_get_netdev_idx(struct mlx5_lag *ldev,
 				struct net_device *ndev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->netdev == ndev)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->netdev == ndev)
 			return i;
 	}
 
@@ -424,7 +431,7 @@ static int mlx5_lag_get_master_idx(struct mlx5_lag *ldev)
 	if (!ldev)
 		return -ENOENT;
 
-	entry = xa_find(&ldev->pfs, &idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);
+	entry = xa_find(&ldev->fns, &idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);
 	if (!entry)
 		return -ENOENT;
 
@@ -462,15 +469,17 @@ int mlx5_lag_get_dev_index_by_seq(struct mlx5_lag *ldev, int seq)
 /* Return the appropriate iterator filter for a device in LAG:
  * - SD shared FDB active: iterate only the device's SD group
  * - SD group exists but shared FDB not active: iterate all devices
- * - No SD: iterate ports only
+ * - Virt LAG: iterate all devices, since its members are not marked as
+ *   ports, as they do not represent a physical port
+ * - Otherwise: iterate ports only
  */
 static u32 mlx5_lag_get_filter(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
 {
-	struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);
+	struct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);
 
-	if (pf && pf->sd_fdb_active)
-		return pf->group_id;
-	if (pf && pf->group_id)
+	if (fn && fn->sd_fdb_active)
+		return fn->group_id;
+	if (fn && (fn->group_id || ldev->virt_lag))
 		return MLX5_LAG_FILTER_ALL;
 	return MLX5_LAG_FILTER_PORTS;
 }
@@ -483,7 +492,7 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev)
 {
 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
 	int master_idx, i, num = 1;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	u32 filter;
 
 	if (!ldev)
@@ -494,15 +503,15 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev)
 	if (master_idx < 0)
 		return -ENOENT;
 
-	pf = mlx5_lag_pf(ldev, master_idx);
-	if (pf && pf->dev == dev)
+	fn = mlx5_lag_fn(ldev, master_idx);
+	if (fn && fn->dev == dev)
 		return 0;
 
 	mlx5_lag_for_each(i, 0, ldev, filter) {
 		if (i == master_idx)
 			continue;
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev == dev)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev == dev)
 			return num;
 		num++;
 	}
@@ -510,6 +519,23 @@ int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev)
 }
 EXPORT_SYMBOL(mlx5_lag_get_dev_seq);
 
+int mlx5_lag_get_member_idx(struct mlx5_core_dev *dev)
+{
+	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
+	struct lag_fn *fn;
+	int idx;
+
+	if (!ldev)
+		return -ENOENT;
+
+	xa_lock(&ldev->fns);
+	fn = mlx5_lag_fn_by_dev(ldev, dev);
+	idx = fn ? fn->idx : -ENOENT;
+	xa_unlock(&ldev->fns);
+
+	return idx;
+}
+
 /* seq 0 = master, then all remaining devices */
 static int mlx5_lag_get_dev_index_by_seq_all(struct mlx5_lag *ldev, int seq)
 {
@@ -540,7 +566,7 @@ static int mlx5_lag_get_dev_index_by_seq_group(struct mlx5_lag *ldev, int seq,
 	int i, num = 0;
 
 	mlx5_lag_for_each(i, 0, ldev, group_id) {
-		if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT)) {
+		if (xa_get_mark(&ldev->fns, i, MLX5_LAG_XA_MARK_PORT)) {
 			if (seq == 0)
 				return i;
 			num++;
@@ -549,7 +575,7 @@ static int mlx5_lag_get_dev_index_by_seq_group(struct mlx5_lag *ldev, int seq,
 	}
 
 	mlx5_lag_for_each(i, 0, ldev, group_id) {
-		if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT))
+		if (xa_get_mark(&ldev->fns, i, MLX5_LAG_XA_MARK_PORT))
 			continue;
 		if (num == seq)
 			return i;
@@ -580,14 +606,14 @@ int mlx5_lag_get_dev_index_by_seq_filter(struct mlx5_lag *ldev, int seq,
 static void mlx5_lag_mark_master(struct mlx5_lag *ldev)
 {
 	int lowest_dev_idx = INT_MAX;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int master_xa_idx = -1;
 	int dev_idx;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		dev_idx = mlx5_get_dev_index(pf->dev);
+		fn = mlx5_lag_fn(ldev, i);
+		dev_idx = mlx5_get_dev_index(fn->dev);
 		if (dev_idx < lowest_dev_idx) {
 			lowest_dev_idx = dev_idx;
 			master_xa_idx = i;
@@ -595,19 +621,55 @@ static void mlx5_lag_mark_master(struct mlx5_lag *ldev)
 	}
 
 	if (master_xa_idx >= 0)
-		xa_set_mark(&ldev->pfs, master_xa_idx, MLX5_LAG_XA_MARK_MASTER);
+		xa_set_mark(&ldev->fns, master_xa_idx, MLX5_LAG_XA_MARK_MASTER);
 }
 
-static void mlx5_lag_clear_master(struct mlx5_lag *ldev)
+static u64 mlx5_lag_member_tiebreak(struct mlx5_core_dev *dev)
+{
+	struct pci_dev *pdev = dev->pdev;
+
+	if (mlx5_core_is_sf(dev))
+		return mlx5_sf_coredev_sfnum(dev);
+
+	return ((u64)pci_domain_nr(pdev->bus) << 16) | pci_dev_id(pdev);
+}
+
+void mlx5_virt_lag_mark_master(struct mlx5_lag *ldev)
+{
+	u64 lowest_tiebreak = U64_MAX;
+	int lowest_dev_idx = INT_MAX;
+	int master_xa_idx = -1;
+	struct lag_fn *fn;
+	u64 tiebreak;
+	int dev_idx;
+	int i;
+
+	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
+		fn = mlx5_lag_fn(ldev, i);
+		dev_idx = mlx5_get_dev_index(fn->dev);
+		tiebreak = mlx5_lag_member_tiebreak(fn->dev);
+		if (dev_idx < lowest_dev_idx ||
+		    (dev_idx == lowest_dev_idx && tiebreak < lowest_tiebreak)) {
+			lowest_dev_idx = dev_idx;
+			lowest_tiebreak = tiebreak;
+			master_xa_idx = i;
+		}
+	}
+
+	if (master_xa_idx >= 0)
+		xa_set_mark(&ldev->fns, master_xa_idx, MLX5_LAG_XA_MARK_MASTER);
+}
+
+void mlx5_lag_clear_master(struct mlx5_lag *ldev)
 {
 	unsigned long idx = 0;
 	void *entry;
 
-	entry = xa_find(&ldev->pfs, &idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);
+	entry = xa_find(&ldev->fns, &idx, U8_MAX, MLX5_LAG_XA_MARK_MASTER);
 	if (!entry)
 		return;
 
-	xa_clear_mark(&ldev->pfs, idx, MLX5_LAG_XA_MARK_MASTER);
+	xa_clear_mark(&ldev->fns, idx, MLX5_LAG_XA_MARK_MASTER);
 }
 
 /* Devcom event handler to manage LAG master marking */
@@ -618,7 +680,7 @@ static int mlx5_lag_devcom_event(int event, void *my_data, void *event_data)
 	int idx;
 
 	ldev = mlx5_lag_dev(dev);
-	if (!ldev)
+	if (!ldev || ldev->virt_lag)
 		return 0;
 
 	mutex_lock(&ldev->lock);
@@ -658,15 +720,15 @@ int mlx5_lag_num_devs(struct mlx5_lag *ldev)
 
 int mlx5_lag_num_netdevs(struct mlx5_lag *ldev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i, num = 0;
 
 	if (!ldev)
 		return 0;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->netdev)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->netdev)
 			num++;
 	}
 	return num;
@@ -685,9 +747,9 @@ static bool __mlx5_lag_is_sriov(struct mlx5_lag *ldev)
 static bool __mlx5_lag_is_sd_active(struct mlx5_lag *ldev,
 				    struct mlx5_core_dev *dev)
 {
-	struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);
+	struct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);
 
-	return pf && pf->sd_fdb_active;
+	return fn && fn->sd_fdb_active;
 }
 
 /* Create a mapping between steering slots and active ports.
@@ -762,12 +824,12 @@ static int mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
 
 static bool mlx5_lag_has_drop_rule(struct mlx5_lag *ldev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->has_drop)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->has_drop)
 			return true;
 	}
 	return false;
@@ -775,17 +837,19 @@ static bool mlx5_lag_has_drop_rule(struct mlx5_lag *ldev)
 
 static void mlx5_lag_drop_rule_cleanup(struct mlx5_lag *ldev)
 {
-	struct lag_func *pf;
+	struct mlx5_eswitch *esw;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (!pf->has_drop)
+		fn = mlx5_lag_fn(ldev, i);
+		if (!fn->has_drop)
 			continue;
 
-		mlx5_esw_acl_ingress_vport_drop_rule_destroy(pf->dev->priv.eswitch,
+		esw = fn->dev->priv.eswitch;
+		mlx5_esw_acl_ingress_vport_drop_rule_destroy(esw,
 							     MLX5_VPORT_UPLINK);
-		pf->has_drop = false;
+		fn->has_drop = false;
 	}
 }
 
@@ -793,7 +857,7 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,
 				     struct lag_tracker *tracker)
 {
 	struct mlx5_core_dev *dev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int err;
 	int i;
 
@@ -809,12 +873,12 @@ static void mlx5_lag_drop_rule_setup(struct mlx5_lag *ldev,
 		if (tracker->netdev_state[i].tx_enabled &&
 		    tracker->netdev_state[i].link_up)
 			continue;
-		pf = mlx5_lag_pf(ldev, i);
-		dev = pf->dev;
+		fn = mlx5_lag_fn(ldev, i);
+		dev = fn->dev;
 		err = mlx5_esw_acl_ingress_vport_drop_rule_create(dev->priv.eswitch,
 								  MLX5_VPORT_UPLINK);
 		if (!err)
-			pf->has_drop = true;
+			fn->has_drop = true;
 		else
 			mlx5_core_err(dev,
 				      "Failed to create lag drop rule, error: %d",
@@ -848,7 +912,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev,
 	if (idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, idx)->dev;
 	if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &ldev->mode_flags)) {
 		ret = mlx5_lag_port_sel_modify(ldev, ports);
 		if (ret ||
@@ -868,7 +932,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev,
 static struct net_device *mlx5_lag_active_backup_get_netdev(struct mlx5_core_dev *dev)
 {
 	struct net_device *ndev = NULL;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	struct mlx5_lag *ldev;
 	unsigned long flags;
 	int i, last_idx;
@@ -880,16 +944,16 @@ static struct net_device *mlx5_lag_active_backup_get_netdev(struct mlx5_core_dev
 		goto unlock;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
+		fn = mlx5_lag_fn(ldev, i);
 		if (ldev->tracker.netdev_state[i].tx_enabled)
-			ndev = pf->netdev;
+			ndev = fn->netdev;
 	}
 	if (!ndev) {
 		last_idx = mlx5_lag_get_dev_index_by_seq(ldev, ldev->ports - 1);
 		if (last_idx < 0)
 			goto unlock;
-		pf = mlx5_lag_pf(ldev, last_idx);
-		ndev = pf->netdev;
+		fn = mlx5_lag_fn(ldev, last_idx);
+		ndev = fn->netdev;
 	}
 
 	dev_hold(ndev);
@@ -914,7 +978,7 @@ void mlx5_modify_lag(struct mlx5_lag *ldev,
 	if (first_idx < 0)
 		return;
 
-	dev0 = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, first_idx)->dev;
 
 	ports = kcalloc(ldev->ports * MLX5_LAG_MAX_HASH_BUCKETS,
 			sizeof(*ports), GFP_KERNEL);
@@ -982,7 +1046,7 @@ static int mlx5_lag_set_port_sel_mode(struct mlx5_lag *ldev,
 	    mode == MLX5_LAG_MODE_MULTIPATH)
 		return 0;
 
-	dev0 = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, first_idx)->dev;
 
 	if (!MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table)) {
 		if (ldev->ports > 2)
@@ -1040,7 +1104,7 @@ static int mlx5_create_lag(struct mlx5_lag *ldev,
 	if (first_idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, first_idx)->dev;
 	if (tracker)
 		mlx5_lag_print_mapping(dev0, ldev, tracker, flags);
 	mlx5_core_info(dev0, "shared_fdb:%d mode:%s\n",
@@ -1088,7 +1152,7 @@ int mlx5_activate_lag(struct mlx5_lag *ldev,
 	if (master_idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, master_idx)->dev;
 	err = mlx5_lag_set_flags(ldev, mode, tracker, shared_fdb, &flags);
 	if (err)
 		return err;
@@ -1151,7 +1215,7 @@ int mlx5_deactivate_lag(struct mlx5_lag *ldev)
 	if (master_idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, master_idx)->dev;
 	ldev->mode = MLX5_LAG_MODE_NONE;
 	ldev->mode_flags = 0;
 	mlx5_lag_mp_reset(ldev);
@@ -1192,7 +1256,7 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)
 	struct mlx5_core_dev *dev;
 	u8 mode;
 #endif
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	bool roce_support;
 	int i;
 
@@ -1201,35 +1265,35 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)
 
 #ifdef CONFIG_MLX5_ESWITCH
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		dev = pf->dev;
+		fn = mlx5_lag_fn(ldev, i);
+		dev = fn->dev;
 		if (mlx5_eswitch_num_vfs(dev->priv.eswitch) && !is_mdev_switchdev_mode(dev))
 			return false;
 	}
 
-	pf = mlx5_lag_pf(ldev, master_idx);
-	dev = pf->dev;
+	fn = mlx5_lag_fn(ldev, master_idx);
+	dev = fn->dev;
 	mode = mlx5_eswitch_mode(dev);
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (mlx5_eswitch_mode(pf->dev) != mode)
+		fn = mlx5_lag_fn(ldev, i);
+		if (mlx5_eswitch_mode(fn->dev) != mode)
 			return false;
 	}
 
 #else
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (mlx5_sriov_is_enabled(pf->dev))
+		fn = mlx5_lag_fn(ldev, i);
+		if (mlx5_sriov_is_enabled(fn->dev))
 			return false;
 	}
 #endif
-	pf = mlx5_lag_pf(ldev, master_idx);
-	roce_support = mlx5_get_roce_state(pf->dev);
+	fn = mlx5_lag_fn(ldev, master_idx);
+	roce_support = mlx5_get_roce_state(fn->dev);
 	mlx5_ldev_for_each(i, 0, ldev) {
 		if (i == master_idx)
 			continue;
-		pf = mlx5_lag_pf(ldev, i);
-		if (mlx5_get_roce_state(pf->dev) != roce_support)
+		fn = mlx5_lag_fn(ldev, i);
+		if (mlx5_get_roce_state(fn->dev) != roce_support)
 			return false;
 	}
 
@@ -1239,19 +1303,19 @@ bool mlx5_lag_check_prereq(struct mlx5_lag *ldev)
 static void mlx5_lag_assert_locked_transition(struct mlx5_lag *ldev, u32 filter)
 {
 	struct mlx5_devcom_comp_dev *devcom = NULL;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	lockdep_assert_held(&ldev->lock);
 
 	i = mlx5_get_next_lag_func(ldev, 0, filter);
-	if (i < MLX5_MAX_PORTS) {
-		pf = mlx5_lag_pf(ldev, i);
+	if (i < ldev->max_fns) {
+		fn = mlx5_lag_fn(ldev, i);
 		if (filter == MLX5_LAG_FILTER_PORTS ||
 		    filter == MLX5_LAG_FILTER_ALL)
-			devcom = pf->dev->priv.hca_devcom_comp;
+			devcom = fn->dev->priv.hca_devcom_comp;
 		else
-			devcom = mlx5_sd_get_devcom(pf->dev);
+			devcom = mlx5_sd_get_devcom(fn->dev);
 	}
 	mlx5_devcom_comp_assert_locked(devcom);
 }
@@ -1297,28 +1361,30 @@ void mlx5_lag_rescan_dev_locked(struct mlx5_lag *ldev,
 static void mlx5_lag_rescan_devices_locked_filter(struct mlx5_lag *ldev,
 						  bool enable, u32 filter)
 {
-	struct mlx5_core_dev *devs[MLX5_MAX_PORTS];
-	struct lag_func *pf;
-	int num_devs = 0;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_lag_assert_locked_transition(ldev, filter);
 
 	mlx5_lag_for_each(i, 0, ldev, filter) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)
 			continue;
 
 		if (enable)
-			pf->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
+			fn->dev->priv.flags &= ~MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
 		else
-			pf->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
-		devs[num_devs++] = pf->dev;
+			fn->dev->priv.flags |= MLX5_PRIV_FLAGS_DISABLE_IB_ADEV;
 	}
 
 	mlx5_lag_drop_lock_for_reps(ldev, filter);
-	for (i = 0; i < num_devs; i++)
-		mlx5_rescan_drivers_locked(devs[i]);
+	mlx5_lag_for_each(i, 0, ldev, filter) {
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev->priv.flags & MLX5_PRIV_FLAGS_DISABLE_ALL_ADEV)
+			continue;
+
+		mlx5_rescan_drivers_locked(fn->dev);
+	}
 	mlx5_lag_retake_lock_after_reps(ldev);
 }
 
@@ -1345,14 +1411,14 @@ void mlx5_lag_remove_devices(struct mlx5_lag *ldev)
 static int mlx5_lag_reload_ib_reps_idx(struct mlx5_lag *ldev, int idx,
 				       u32 flags)
 {
-	struct lag_func *pf = mlx5_lag_pf(ldev, idx);
+	struct lag_fn *fn = mlx5_lag_fn(ldev, idx);
 	struct mlx5_eswitch *esw;
 	int ret;
 
-	if (pf->dev->priv.flags & flags)
+	if (fn->dev->priv.flags & flags)
 		return 0;
 
-	esw = pf->dev->priv.eswitch;
+	esw = fn->dev->priv.eswitch;
 	mlx5_esw_reps_block(esw);
 	ret = mlx5_eswitch_reload_ib_reps(esw);
 	mlx5_esw_reps_unblock(esw);
@@ -1413,14 +1479,14 @@ int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags,
 
 static void mlx5_lag_unload_reps_unlocked(struct mlx5_lag *ldev, u32 filter)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_lag_for_each(i, 0, ldev, filter) {
 		struct mlx5_eswitch *esw;
 
-		pf = mlx5_lag_pf(ldev, i);
-		esw = pf->dev->priv.eswitch;
+		fn = mlx5_lag_fn(ldev, i);
+		esw = fn->dev->priv.eswitch;
 		mlx5_esw_reps_block(esw);
 		mlx5_eswitch_unload_reps(esw);
 		mlx5_esw_reps_unblock(esw);
@@ -1454,7 +1520,7 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)
 		return;
 	}
 
-	dev0 = mlx5_lag_pf(ldev, idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, idx)->dev;
 	roce_lag = __mlx5_lag_is_roce(ldev);
 
 	if (roce_lag) {
@@ -1462,7 +1528,7 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)
 		mlx5_ldev_for_each(i, 0, ldev) {
 			if (i == idx)
 				continue;
-			mlx5_nic_vport_disable_roce(mlx5_lag_pf(ldev, i)->dev);
+			mlx5_nic_vport_disable_roce(mlx5_lag_fn(ldev, i)->dev);
 		}
 	}
 
@@ -1477,18 +1543,18 @@ void mlx5_disable_lag(struct mlx5_lag *ldev)
 static bool mlx5_lag_is_roce_lag(struct mlx5_lag *ldev)
 {
 	bool roce_lag = true;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		roce_lag = roce_lag && !mlx5_sriov_is_enabled(pf->dev);
+		fn = mlx5_lag_fn(ldev, i);
+		roce_lag = roce_lag && !mlx5_sriov_is_enabled(fn->dev);
 	}
 
 #ifdef CONFIG_MLX5_ESWITCH
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		roce_lag = roce_lag && is_mdev_legacy_mode(pf->dev);
+		fn = mlx5_lag_fn(ldev, i);
+		roce_lag = roce_lag && is_mdev_legacy_mode(fn->dev);
 	}
 #endif
 
@@ -1517,15 +1583,15 @@ static int mlx5_lag_get_devices_oper_speed(struct mlx5_lag *ldev,
 	mlx5_ldev_for_each(pf_idx, 0, ldev) {
 		u8 opmod = MLX5_VPORT_STATE_OP_MOD_VNIC_VPORT;
 		struct mlx5_core_dev *pf_mdev;
-		struct lag_func *pf;
+		struct lag_fn *fn;
 		u32 speed;
 		u8 state;
 		int ret;
 
-		pf = mlx5_lag_pf(ldev, pf_idx);
-		if (!pf)
+		fn = mlx5_lag_fn(ldev, pf_idx);
+		if (!fn)
 			continue;
-		pf_mdev = pf->dev;
+		pf_mdev = fn->dev;
 		if (!pf_mdev)
 			continue;
 		ret = mlx5_query_vport_max_tx_speed(pf_mdev, opmod, 0, 0,
@@ -1564,14 +1630,14 @@ static int mlx5_lag_get_devices_max_speed(struct mlx5_lag *ldev, u32 *max_speed)
 	*max_speed = 0;
 	mlx5_ldev_for_each(pf_idx, 0, ldev) {
 		struct mlx5_core_dev *pf_mdev;
-		struct lag_func *pf;
+		struct lag_fn *fn;
 		u32 speed;
 		int ret;
 
-		pf = mlx5_lag_pf(ldev, pf_idx);
-		if (!pf)
+		fn = mlx5_lag_fn(ldev, pf_idx);
+		if (!fn)
 			continue;
-		pf_mdev = pf->dev;
+		pf_mdev = fn->dev;
 		if (!pf_mdev)
 			continue;
 
@@ -1631,7 +1697,7 @@ static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev,
 void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)
 {
 	struct mlx5_core_dev *mdev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	u32 speed;
 	int pf_idx;
 
@@ -1651,10 +1717,10 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)
 	speed = speed / MLX5_MAX_TX_SPEED_UNIT;
 
 	mlx5_ldev_for_each(pf_idx, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, pf_idx);
-		if (!pf)
+		fn = mlx5_lag_fn(ldev, pf_idx);
+		if (!fn)
 			continue;
-		mdev = pf->dev;
+		mdev = fn->dev;
 		if (!mdev)
 			continue;
 
@@ -1665,16 +1731,16 @@ void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev)
 void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev)
 {
 	struct mlx5_core_dev *mdev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	u32 speed;
 	int pf_idx;
 	int ret;
 
 	mlx5_ldev_for_each(pf_idx, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, pf_idx);
-		if (!pf)
+		fn = mlx5_lag_fn(ldev, pf_idx);
+		if (!fn)
 			continue;
-		mdev = pf->dev;
+		mdev = fn->dev;
 		if (!mdev)
 			continue;
 
@@ -1705,7 +1771,7 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
 	if (idx < 0)
 		return;
 
-	dev0 = mlx5_lag_pf(ldev, idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, idx)->dev;
 	if (!mlx5_lag_is_ready(ldev)) {
 		do_bond = false;
 	} else {
@@ -1750,7 +1816,7 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
 				mlx5_ldev_for_each(i, 0, ldev) {
 					if (i == idx)
 						continue;
-					dev = mlx5_lag_pf(ldev, i)->dev;
+					dev = mlx5_lag_fn(ldev, i)->dev;
 					if (mlx5_get_roce_state(dev))
 						mlx5_nic_vport_enable_roce(dev);
 				}
@@ -1784,14 +1850,17 @@ static void mlx5_do_bond(struct mlx5_lag *ldev)
 struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev)
 {
 	struct mlx5_devcom_comp_dev *devcom = NULL;
-	struct lag_func *pf;
+	struct lag_fn *fn;
+	u32 filter;
 	int i;
 
 	mutex_lock(&ldev->lock);
-	i = mlx5_get_next_lag_func(ldev, 0, MLX5_LAG_FILTER_PORTS);
-	if (i < MLX5_MAX_PORTS) {
-		pf = mlx5_lag_pf(ldev, i);
-		devcom = pf->dev->priv.hca_devcom_comp;
+	filter = ldev->virt_lag ? MLX5_LAG_FILTER_ALL :
+		MLX5_LAG_FILTER_PORTS;
+	i = mlx5_get_next_lag_func(ldev, 0, filter);
+	if (i < ldev->max_fns) {
+		fn = mlx5_lag_fn(ldev, i);
+		devcom = fn->dev->priv.hca_devcom_comp;
 	}
 	mutex_unlock(&ldev->lock);
 	return devcom;
@@ -1799,7 +1868,7 @@ struct mlx5_devcom_comp_dev *mlx5_lag_get_devcom_comp(struct mlx5_lag *ldev)
 
 static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev,
 				     struct mlx5_flow_table_attr *ft_attr,
-				     struct lag_func *pf)
+				     struct lag_fn *fn)
 {
 #ifdef CONFIG_MLX5_ESWITCH
 	struct mlx5_flow_namespace *ns;
@@ -1810,20 +1879,20 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev,
 	if (!ns)
 		return 0;
 
-	pf->lag_demux_ft = mlx5_create_flow_table(ns, ft_attr);
-	if (IS_ERR(pf->lag_demux_ft))
-		return PTR_ERR(pf->lag_demux_ft);
+	fn->lag_demux_ft = mlx5_create_flow_table(ns, ft_attr);
+	if (IS_ERR(fn->lag_demux_ft))
+		return PTR_ERR(fn->lag_demux_ft);
 
 	fg = mlx5_esw_lag_demux_fg_create(dev->priv.eswitch,
-					  pf->lag_demux_ft);
+					  fn->lag_demux_ft);
 	if (IS_ERR(fg)) {
 		err = PTR_ERR(fg);
-		mlx5_destroy_flow_table(pf->lag_demux_ft);
-		pf->lag_demux_ft = NULL;
+		mlx5_destroy_flow_table(fn->lag_demux_ft);
+		fn->lag_demux_ft = NULL;
 		return err;
 	}
 
-	pf->lag_demux_fg = fg;
+	fn->lag_demux_fg = fg;
 	return 0;
 #else
 	return -EOPNOTSUPP;
@@ -1832,7 +1901,7 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev,
 
 static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev,
 				  struct mlx5_flow_table_attr *ft_attr,
-				  struct lag_func *pf)
+				  struct lag_fn *fn)
 {
 	struct mlx5_flow_namespace *ns;
 	int err;
@@ -1841,12 +1910,12 @@ static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev,
 	if (!ns)
 		return 0;
 
-	pf->lag_demux_fg = NULL;
+	fn->lag_demux_fg = NULL;
 	ft_attr->max_fte = 1;
-	pf->lag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr);
-	if (IS_ERR(pf->lag_demux_ft)) {
-		err = PTR_ERR(pf->lag_demux_ft);
-		pf->lag_demux_ft = NULL;
+	fn->lag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr);
+	if (IS_ERR(fn->lag_demux_ft)) {
+		err = PTR_ERR(fn->lag_demux_ft);
+		fn->lag_demux_ft = NULL;
 		return err;
 	}
 
@@ -1857,7 +1926,7 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev,
 			struct mlx5_flow_table_attr *ft_attr)
 {
 	struct mlx5_lag *ldev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 
 	if (!ft_attr)
 		return -EINVAL;
@@ -1866,16 +1935,16 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev,
 	if (!ldev)
 		return -ENODEV;
 
-	pf = mlx5_lag_pf_by_dev(ldev, dev);
-	if (!pf)
+	fn = mlx5_lag_fn_by_dev(ldev, dev);
+	if (!fn)
 		return -ENODEV;
 
-	xa_init(&pf->lag_demux_rules);
+	xa_init(&fn->lag_demux_rules);
 
-	if (mlx5_lag_is_sw_lag(dev))
-		return mlx5_lag_demux_ft_fg_init(dev, ft_attr, pf);
+	if (mlx5_lag_is_sw_managed(dev))
+		return mlx5_lag_demux_ft_fg_init(dev, ft_attr, fn);
 
-	return mlx5_lag_demux_fw_init(dev, ft_attr, pf);
+	return mlx5_lag_demux_fw_init(dev, ft_attr, fn);
 }
 EXPORT_SYMBOL(mlx5_lag_demux_init);
 
@@ -1884,31 +1953,31 @@ void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev)
 	struct mlx5_flow_handle *rule;
 	struct mlx5_lag *ldev;
 	unsigned long vport_num;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 
 	ldev = mlx5_lag_dev(dev);
 	if (!ldev)
 		return;
 
-	pf = mlx5_lag_pf_by_dev(ldev, dev);
-	if (!pf)
+	fn = mlx5_lag_fn_by_dev(ldev, dev);
+	if (!fn)
 		return;
 
-	xa_for_each(&pf->lag_demux_rules, vport_num, rule)
+	xa_for_each(&fn->lag_demux_rules, vport_num, rule)
 		mlx5_del_flow_rules(rule);
-	xa_destroy(&pf->lag_demux_rules);
+	xa_destroy(&fn->lag_demux_rules);
 
-	if (pf->lag_demux_fg)
-		mlx5_destroy_flow_group(pf->lag_demux_fg);
-	if (pf->lag_demux_ft)
-		mlx5_destroy_flow_table(pf->lag_demux_ft);
-	pf->lag_demux_fg = NULL;
-	pf->lag_demux_ft = NULL;
+	if (fn->lag_demux_fg)
+		mlx5_destroy_flow_group(fn->lag_demux_fg);
+	if (fn->lag_demux_ft)
+		mlx5_destroy_flow_table(fn->lag_demux_ft);
+	fn->lag_demux_fg = NULL;
+	fn->lag_demux_ft = NULL;
 }
 EXPORT_SYMBOL(mlx5_lag_demux_cleanup);
 
-static struct lag_func *mlx5_lag_dev_get_master_pf(struct mlx5_lag *ldev,
-						   struct mlx5_core_dev *dev)
+static struct lag_fn *mlx5_lag_dev_get_master_fn(struct mlx5_lag *ldev,
+						 struct mlx5_core_dev *dev)
 {
 	u32 filter = mlx5_lag_get_filter(ldev, dev);
 	int idx;
@@ -1917,14 +1986,14 @@ static struct lag_func *mlx5_lag_dev_get_master_pf(struct mlx5_lag *ldev,
 	if (idx < 0)
 		return NULL;
 
-	return mlx5_lag_pf(ldev, idx);
+	return mlx5_lag_fn(ldev, idx);
 }
 
 int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num,
 			    int index)
 {
 	struct mlx5_flow_handle *rule;
-	struct lag_func *master;
+	struct lag_fn *master;
 	struct mlx5_lag *ldev;
 	int err;
 
@@ -1932,7 +2001,7 @@ int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num,
 	if (!ldev)
 		return 0;
 
-	master = mlx5_lag_dev_get_master_pf(ldev, vport_dev);
+	master = mlx5_lag_dev_get_master_fn(ldev, vport_dev);
 	if (!master || !master->lag_demux_fg)
 		return 0;
 
@@ -1965,18 +2034,18 @@ EXPORT_SYMBOL(mlx5_lag_demux_rule_add);
 void mlx5_lag_demux_rule_del(struct mlx5_core_dev *dev, int index)
 {
 	struct mlx5_flow_handle *rule;
-	struct lag_func *master_pf;
+	struct lag_fn *master_fn;
 	struct mlx5_lag *ldev;
 
 	ldev = mlx5_lag_dev(dev);
 	if (!ldev)
 		return;
 
-	master_pf = mlx5_lag_dev_get_master_pf(ldev, dev);
-	if (!master_pf || !master_pf->lag_demux_fg)
+	master_fn = mlx5_lag_dev_get_master_fn(ldev, dev);
+	if (!master_fn || !master_fn->lag_demux_fg)
 		return;
 
-	rule = xa_erase(&master_pf->lag_demux_rules, index);
+	rule = xa_erase(&master_fn->lag_demux_rules, index);
 	if (rule)
 		mlx5_del_flow_rules(rule);
 }
@@ -2026,7 +2095,7 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev,
 	struct netdev_lag_upper_info *lag_upper_info = NULL;
 	bool is_bonded, is_in_lag, mode_supported;
 	bool has_inactive = 0;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	struct slave *slave;
 	u8 bond_status = 0;
 	int num_slaves = 0;
@@ -2047,13 +2116,13 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev,
 	rcu_read_lock();
 	for_each_netdev_in_bond_rcu(upper, ndev_tmp) {
 		mlx5_ldev_for_each(i, 0, ldev) {
-			pf = mlx5_lag_pf(ldev, i);
-			if (pf->netdev == ndev_tmp) {
+			fn = mlx5_lag_fn(ldev, i);
+			if (fn->netdev == ndev_tmp) {
 				idx++;
 				break;
 			}
 		}
-		if (i < MLX5_MAX_PORTS) {
+		if (i < ldev->ports) {
 			if (netif_is_bond_master(upper)) {
 				slave = bond_slave_get_rcu(ndev_tmp);
 				if (slave)
@@ -2270,16 +2339,16 @@ static void mlx5_ldev_add_netdev(struct mlx5_lag *ldev,
 				struct mlx5_core_dev *dev,
 				struct net_device *netdev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	unsigned long flags;
 	int i;
 
 	spin_lock_irqsave(&lag_lock, flags);
-	/* Find pf entry by matching dev pointer */
+	/* Find fn entry by matching dev pointer */
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev == dev) {
-			pf->netdev = netdev;
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev == dev) {
+			fn->netdev = netdev;
 			ldev->tracker.netdev_state[i].link_up = 0;
 			ldev->tracker.netdev_state[i].tx_enabled = 0;
 			break;
@@ -2291,15 +2360,15 @@ static void mlx5_ldev_add_netdev(struct mlx5_lag *ldev,
 static void mlx5_ldev_remove_netdev(struct mlx5_lag *ldev,
 				    struct net_device *netdev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	unsigned long flags;
 	int i;
 
 	spin_lock_irqsave(&lag_lock, flags);
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->netdev == netdev) {
-			pf->netdev = NULL;
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->netdev == netdev) {
+			fn->netdev = NULL;
 			break;
 		}
 	}
@@ -2310,34 +2379,37 @@ int mlx5_ldev_add_mdev(struct mlx5_lag *ldev,
 		       struct mlx5_core_dev *dev,
 		       u32 group_id)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	u32 idx;
 	int err;
 
-	pf = kzalloc_obj(*pf);
-	if (!pf)
+	fn = kzalloc_obj(*fn);
+	if (!fn)
 		return -ENOMEM;
 
-	err = xa_alloc(&ldev->pfs, &idx, pf, XA_LIMIT(0, MLX5_MAX_PORTS - 1),
+	err = xa_alloc(&ldev->fns, &idx, fn, XA_LIMIT(0, U8_MAX - 1),
 		       GFP_KERNEL);
 	if (err) {
-		kfree(pf);
+		kfree(fn);
 		return err;
 	}
 
-	pf->idx = idx;
-	pf->dev = dev;
-	pf->group_id = group_id;
+	/* max_fns is the iteration ceiling; grow it, never shrink. */
+	if (idx >= ldev->max_fns)
+		ldev->max_fns = idx + 1;
+	fn->idx = idx;
+	fn->dev = dev;
+	fn->group_id = group_id;
 	dev->priv.lag = ldev;
 
-	if (group_id)
+	if (group_id || ldev->virt_lag)
 		return 0;
 
-	xa_set_mark(&ldev->pfs, idx, MLX5_LAG_XA_MARK_PORT);
+	xa_set_mark(&ldev->fns, idx, MLX5_LAG_XA_MARK_PORT);
 
-	MLX5_NB_INIT(&pf->port_change_nb,
+	MLX5_NB_INIT(&fn->port_change_nb,
 		     mlx5_lag_mpesw_port_change_event, PORT_CHANGE);
-	mlx5_eq_notifier_register(dev, &pf->port_change_nb);
+	mlx5_eq_notifier_register(dev, &fn->port_change_nb);
 
 	return 0;
 }
@@ -2345,24 +2417,24 @@ int mlx5_ldev_add_mdev(struct mlx5_lag *ldev,
 void mlx5_ldev_remove_mdev(struct mlx5_lag *ldev,
 			   struct mlx5_core_dev *dev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev == dev)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev == dev)
 			break;
 	}
-	if (i >= MLX5_MAX_PORTS)
+	if (i >= ldev->max_fns)
 		return;
 
-	if (pf->port_change_nb.nb.notifier_call)
-		mlx5_eq_notifier_unregister(dev, &pf->port_change_nb);
+	if (fn->port_change_nb.nb.notifier_call)
+		mlx5_eq_notifier_unregister(dev, &fn->port_change_nb);
 
-	pf->dev = NULL;
+	fn->dev = NULL;
 	dev->priv.lag = NULL;
-	xa_erase(&ldev->pfs, pf->idx);
-	kfree(pf);
+	xa_erase(&ldev->fns, fn->idx);
+	kfree(fn);
 }
 
 /* Must be called with HCA devcom component lock held */
@@ -2397,6 +2469,11 @@ static int __mlx5_lag_dev_add_mdev(struct mlx5_core_dev *dev)
 		mutex_unlock(&ldev->lock);
 		return -EAGAIN;
 	}
+
+	if (mlx5_virt_lag_is_supported(dev) && __mlx5_lag_is_active(ldev)) {
+		mutex_unlock(&ldev->lock);
+		return -EOPNOTSUPP;
+	}
 	mlx5_ldev_get(ldev);
 	err = mlx5_ldev_add_mdev(ldev, dev, 0);
 	if (err) {
@@ -2474,7 +2551,7 @@ void mlx5_lag_add_mdev(struct mlx5_core_dev *dev)
 {
 	int err;
 
-	if (!mlx5_lag_is_supported(dev))
+	if (!mlx5_lag_is_supported(dev) && !mlx5_virt_lag_is_supported(dev))
 		return;
 
 	if (mlx5_lag_register_hca_devcom_comp(dev))
@@ -2485,6 +2562,10 @@ void mlx5_lag_add_mdev(struct mlx5_core_dev *dev)
 	err = __mlx5_lag_dev_add_mdev(dev);
 	mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp);
 
+	if (err == -EOPNOTSUPP) {
+		mlx5_lag_unregister_hca_devcom_comp(dev);
+		return;
+	}
 	if (err) {
 		msleep(100);
 		goto recheck;
@@ -2505,6 +2586,9 @@ void mlx5_lag_remove_netdev(struct mlx5_core_dev *dev,
 	if (!ldev)
 		return;
 
+	if (ldev->virt_lag)
+		return;
+
 	mutex_lock(&ldev->lock);
 	mlx5_ldev_remove_netdev(ldev, netdev);
 	clear_bit(MLX5_LAG_FLAG_NDEVS_READY, &ldev->state_flags);
@@ -2526,6 +2610,9 @@ void mlx5_lag_add_netdev(struct mlx5_core_dev *dev,
 	if (!ldev)
 		return;
 
+	if (ldev->virt_lag)
+		return;
+
 	mutex_lock(&ldev->lock);
 	mlx5_ldev_add_netdev(ldev, dev, netdev);
 	num = mlx5_lag_num_netdevs(ldev);
@@ -2538,18 +2625,18 @@ void mlx5_lag_add_netdev(struct mlx5_core_dev *dev,
 int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx,
 			  u32 filter)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	for (i = start_idx; i >= end_idx; i--) {
-		pf = xa_load(&ldev->pfs, i);
-		if (!pf || !pf->dev)
+		fn = xa_load(&ldev->fns, i);
+		if (!fn || !fn->dev)
 			continue;
 		if (filter == MLX5_LAG_FILTER_PORTS) {
-			if (xa_get_mark(&ldev->pfs, i, MLX5_LAG_XA_MARK_PORT))
+			if (xa_get_mark(&ldev->fns, i, MLX5_LAG_XA_MARK_PORT))
 				return i;
 		} else if (filter == MLX5_LAG_FILTER_ALL ||
-			   filter == pf->group_id) {
+			   filter == fn->group_id) {
 			return i;
 		}
 	}
@@ -2558,25 +2645,25 @@ int mlx5_get_pre_lag_func(struct mlx5_lag *ldev, int start_idx, int end_idx,
 
 int mlx5_get_next_lag_func(struct mlx5_lag *ldev, int start_idx, u32 filter)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	unsigned long idx;
 
 	if (filter == MLX5_LAG_FILTER_PORTS) {
-		xa_for_each_marked_start(&ldev->pfs, idx, pf,
+		xa_for_each_marked_start(&ldev->fns, idx, fn,
 					 MLX5_LAG_XA_MARK_PORT, start_idx)
-			if (pf->dev)
+			if (fn->dev)
 				return idx;
-		return MLX5_MAX_PORTS;
+		return ldev->max_fns;
 	}
 
-	xa_for_each_start(&ldev->pfs, idx, pf, start_idx) {
-		if (!pf->dev)
+	xa_for_each_start(&ldev->fns, idx, fn, start_idx) {
+		if (!fn->dev)
 			continue;
 		if (filter == MLX5_LAG_FILTER_ALL ||
-		    filter == pf->group_id)
+		    filter == fn->group_id)
 			return idx;
 	}
-	return MLX5_MAX_PORTS;
+	return ldev->max_fns;
 }
 
 bool mlx5_lag_is_roce(struct mlx5_core_dev *dev)
@@ -2630,7 +2717,7 @@ bool mlx5_lag_is_master(struct mlx5_core_dev *dev)
 {
 	struct mlx5_lag *ldev;
 	unsigned long flags;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	bool res = false;
 	int idx;
 
@@ -2644,8 +2731,8 @@ bool mlx5_lag_is_master(struct mlx5_core_dev *dev)
 							   filter);
 		if ((__mlx5_lag_is_active(ldev) ||
 		     __mlx5_lag_is_sd_active(ldev, dev)) && idx >= 0) {
-			pf = mlx5_lag_pf(ldev, idx);
-			res = pf && dev == pf->dev;
+			fn = mlx5_lag_fn(ldev, idx);
+			res = fn && dev == fn->dev;
 		}
 	}
 	spin_unlock_irqrestore(&lag_lock, flags);
@@ -2708,7 +2795,7 @@ void mlx5_lag_disable_change(struct mlx5_core_dev *dev)
 	struct mlx5_devcom_comp_dev *sd_devcom = mlx5_sd_get_devcom(dev);
 	struct mlx5_core_dev *primary = dev;
 	struct mlx5_lag *ldev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	bool mpesw;
 	int i;
 
@@ -2747,9 +2834,9 @@ void mlx5_lag_disable_change(struct mlx5_core_dev *dev)
 	mlx5_devcom_comp_lock(sd_devcom);
 	mutex_lock(&ldev->lock);
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev == dev && pf->sd_fdb_active) {
-			mlx5_lag_shared_fdb_destroy(ldev, pf->group_id);
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev == dev && fn->sd_fdb_active) {
+			mlx5_lag_shared_fdb_destroy(ldev, fn->group_id);
 			break;
 		}
 	}
@@ -2776,7 +2863,7 @@ u8 mlx5_lag_get_slave_port(struct mlx5_core_dev *dev,
 {
 	struct mlx5_lag *ldev;
 	unsigned long flags;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	u8 port = 0;
 	int i;
 
@@ -2786,8 +2873,8 @@ u8 mlx5_lag_get_slave_port(struct mlx5_core_dev *dev,
 		goto unlock;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->netdev == slave) {
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->netdev == slave) {
 			port = i;
 			break;
 		}
@@ -2809,7 +2896,7 @@ u8 mlx5_lag_get_num_ports(struct mlx5_core_dev *dev)
 	if (!ldev)
 		return 0;
 
-	return ldev->ports;
+	return max_t(u8, ldev->ports, 1);
 }
 EXPORT_SYMBOL(mlx5_lag_get_num_ports);
 
@@ -2818,7 +2905,7 @@ struct mlx5_core_dev *mlx5_lag_get_next_peer_mdev(struct mlx5_core_dev *dev, int
 	struct mlx5_core_dev *peer_dev = NULL;
 	struct mlx5_lag *ldev;
 	unsigned long flags;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int idx;
 
 	spin_lock_irqsave(&lag_lock, flags);
@@ -2826,22 +2913,22 @@ struct mlx5_core_dev *mlx5_lag_get_next_peer_mdev(struct mlx5_core_dev *dev, int
 	if (!ldev)
 		goto unlock;
 
-	if (*i == MLX5_MAX_PORTS)
+	if (*i == ldev->max_fns)
 		goto unlock;
 	mlx5_lag_for_each(idx, *i, ldev, mlx5_lag_get_filter(ldev, dev)) {
-		pf = mlx5_lag_pf(ldev, idx);
-		if (pf->dev != dev)
+		fn = mlx5_lag_fn(ldev, idx);
+		if (fn->dev != dev)
 			break;
 	}
 
-	if (idx == MLX5_MAX_PORTS) {
+	if (idx == ldev->max_fns) {
 		*i = idx;
 		goto unlock;
 	}
 	*i = idx + 1;
 
-	pf = mlx5_lag_pf(ldev, idx);
-	peer_dev = pf->dev;
+	fn = mlx5_lag_fn(ldev, idx);
+	peer_dev = fn->dev;
 
 unlock:
 	spin_unlock_irqrestore(&lag_lock, flags);
@@ -2859,7 +2946,7 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
 	int ret = 0, i, j, idx = 0;
 	struct mlx5_lag *ldev;
 	unsigned long flags;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int num_ports;
 	void *out;
 
@@ -2867,7 +2954,9 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
 	if (!out)
 		return -ENOMEM;
 
-	mdev = kvzalloc(sizeof(mdev[0]) * MLX5_MAX_PORTS, GFP_KERNEL);
+	ldev = mlx5_lag_dev(dev);
+	num_ports = ldev ? max_t(int, ldev->ports, 1) : 1;
+	mdev = kcalloc(num_ports, sizeof(mdev[0]), GFP_KERNEL);
 	if (!mdev) {
 		ret = -ENOMEM;
 		goto free_out;
@@ -2876,20 +2965,17 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
 	memset(values, 0, sizeof(*values) * num_counters);
 
 	spin_lock_irqsave(&lag_lock, flags);
-	ldev = mlx5_lag_dev(dev);
-	if (ldev && __mlx5_lag_is_active(ldev)) {
-		num_ports = ldev->ports;
+	if (ldev && __mlx5_lag_is_active(ldev) && !ldev->virt_lag) {
 		mlx5_ldev_for_each(i, 0, ldev) {
-			pf = mlx5_lag_pf(ldev, i);
-			mdev[idx++] = pf->dev;
+			fn = mlx5_lag_fn(ldev, i);
+			mdev[idx++] = fn->dev;
 		}
 	} else {
-		num_ports = 1;
-		mdev[MLX5_LAG_P1] = dev;
+		mdev[idx++] = dev;
 	}
 	spin_unlock_irqrestore(&lag_lock, flags);
 
-	for (i = 0; i < num_ports; ++i) {
+	for (i = 0; i < idx; ++i) {
 		u32 in[MLX5_ST_SZ_DW(query_cong_statistics_in)] = {};
 
 		MLX5_SET(query_cong_statistics_in, in, opcode,
@@ -2904,7 +2990,7 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
 	}
 
 free_mdev:
-	kvfree(mdev);
+	kfree(mdev);
 free_out:
 	kvfree(out);
 	return ret;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
index 8552792b9035fd..385c767e24ae69 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
@@ -51,7 +51,7 @@ enum mlx5_lag_mode {
 	MLX5_LAG_MODE_MPESW,
 };
 
-struct lag_func {
+struct lag_fn {
 	struct mlx5_core_dev *dev;
 	struct net_device    *netdev;
 	bool has_drop;
@@ -83,11 +83,13 @@ struct mlx5_lag {
 	unsigned long		  mode_flags;
 	unsigned long		  state_flags;
 	u8			  ports;
+	u8			  max_fns;
 	u8			  buckets;
+	bool			  virt_lag;
 	int			  mode_changes_in_progress;
 	u8			  *v2p_map;
 	struct kref               ref;
-	struct xarray             pfs;
+	struct xarray             fns;
 	struct lag_tracker        tracker;
 	struct workqueue_struct   *wq;
 	struct delayed_work       bond_work;
@@ -107,44 +109,44 @@ mlx5_lag_dev(struct mlx5_core_dev *dev)
 	return dev->priv.lag;
 }
 
-static inline struct lag_func *
-mlx5_lag_pf(struct mlx5_lag *ldev, unsigned int idx)
+static inline struct lag_fn *
+mlx5_lag_fn(struct mlx5_lag *ldev, unsigned int idx)
 {
-	return xa_load(&ldev->pfs, idx);
+	return xa_load(&ldev->fns, idx);
 }
 
 /* Get device index (mlx5_get_dev_index) from xarray index */
 static inline int mlx5_lag_xa_to_dev_idx(struct mlx5_lag *ldev, int xa_idx)
 {
-	struct lag_func *pf = mlx5_lag_pf(ldev, xa_idx);
+	struct lag_fn *fn = mlx5_lag_fn(ldev, xa_idx);
 
-	return pf ? mlx5_get_dev_index(pf->dev) : -ENOENT;
+	return fn ? mlx5_get_dev_index(fn->dev) : -ENOENT;
 }
 
-/* Find lag_func by device index (reverse lookup from mlx5_get_dev_index) */
-static inline struct lag_func *
-mlx5_lag_pf_by_dev_idx(struct mlx5_lag *ldev, int dev_idx)
+/* Find lag_fn by device index (reverse lookup from mlx5_get_dev_index) */
+static inline struct lag_fn *
+mlx5_lag_fn_by_dev_idx(struct mlx5_lag *ldev, int dev_idx)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	unsigned long idx;
 
-	xa_for_each(&ldev->pfs, idx, pf) {
-		if (mlx5_get_dev_index(pf->dev) == dev_idx)
-			return pf;
+	xa_for_each(&ldev->fns, idx, fn) {
+		if (mlx5_get_dev_index(fn->dev) == dev_idx)
+			return fn;
 	}
 	return NULL;
 }
 
-/* Find lag_func by mlx5_core_dev pointer */
-static inline struct lag_func *
-mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
+/* Find lag_fn by mlx5_core_dev pointer */
+static inline struct lag_fn *
+mlx5_lag_fn_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	unsigned long idx;
 
-	xa_for_each(&ldev->pfs, idx, pf) {
-		if (pf->dev == dev)
-			return pf;
+	xa_for_each(&ldev->fns, idx, fn) {
+		if (fn->dev == dev)
+			return fn;
 	}
 	return NULL;
 }
@@ -152,17 +154,17 @@ mlx5_lag_pf_by_dev(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
 static inline bool
 __mlx5_lag_is_sd(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
 {
-	struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);
+	struct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);
 
-	return pf && pf->group_id != 0;
+	return fn && fn->group_id != 0;
 }
 
 static inline bool
 __mlx5_lag_dev_is_port(struct mlx5_lag *ldev, struct mlx5_core_dev *dev)
 {
-	struct lag_func *pf = mlx5_lag_pf_by_dev(ldev, dev);
+	struct lag_fn *fn = mlx5_lag_fn_by_dev(ldev, dev);
 
-	return pf && xa_get_mark(&ldev->pfs, pf->idx, MLX5_LAG_XA_MARK_PORT);
+	return fn && xa_get_mark(&ldev->fns, fn->idx, MLX5_LAG_XA_MARK_PORT);
 }
 
 static inline bool
@@ -225,7 +227,10 @@ static inline bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev)
 }
 #endif
 bool mlx5_lag_check_prereq(struct mlx5_lag *ldev);
+void mlx5_virt_lag_mark_master(struct mlx5_lag *ldev);
+void mlx5_lag_clear_master(struct mlx5_lag *ldev);
 bool mlx5_lag_is_sd(struct mlx5_core_dev *dev);
+bool mlx5_lag_is_sw_managed(struct mlx5_core_dev *dev);
 int mlx5_lag_demux_init(struct mlx5_core_dev *dev,
 			struct mlx5_flow_table_attr *ft_attr);
 void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev);
@@ -266,6 +271,20 @@ static inline void mlx5_lag_set_vports_agg_speed(struct mlx5_lag *ldev) {}
 static inline void mlx5_lag_reset_vports_speed(struct mlx5_lag *ldev) {}
 #endif
 
+static inline bool mlx5_virt_lag_is_supported(const struct mlx5_core_dev *dev)
+{
+	if (!MLX5_CAP_GEN(dev, multi_uplink_eswitch) ||
+	    !MLX5_CAP_GEN(dev, lag_master) ||
+	    MLX5_CAP_GEN(dev, num_lag_ports) >= 2)
+		return false;
+	return true;
+}
+
+static inline bool mlx5_virt_lag_nonmember(struct mlx5_core_dev *dev)
+{
+	return mlx5_virt_lag_is_supported(dev) && !mlx5_lag_dev(dev);
+}
+
 static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev)
 {
 	if (!MLX5_CAP_GEN(dev, vport_group_manager) ||
@@ -285,7 +304,7 @@ static inline bool mlx5_lag_is_supported(struct mlx5_core_dev *dev)
 #define mlx5_lag_for_each(i, start_index, ldev, filter) \
 	for (int tmp = start_index; \
 	     tmp = mlx5_get_next_lag_func(ldev, tmp, filter), \
-	     i = tmp, tmp < MLX5_MAX_PORTS; tmp++)
+	     i = tmp, tmp < (ldev)->max_fns; tmp++)
 
 #define mlx5_lag_for_each_reverse(i, start_index, end_index, ldev, filter) \
 	for (int tmp = start_index, tmp1 = end_index; \
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c
index 65c76bd748c6b2..587638278f342b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mp.c
@@ -26,15 +26,15 @@ static bool mlx5_lag_multipath_check_prereq(struct mlx5_lag *ldev)
 	if (__mlx5_lag_is_active(ldev) && !__mlx5_lag_is_multipath(ldev))
 		return false;
 
-	if (__mlx5_lag_is_sd(ldev, mlx5_lag_pf(ldev, idx0)->dev) ||
-	    __mlx5_lag_is_sd(ldev, mlx5_lag_pf(ldev, idx1)->dev))
+	if (mlx5_lag_is_sw_managed(mlx5_lag_fn(ldev, idx0)->dev) ||
+	    mlx5_lag_is_sw_managed(mlx5_lag_fn(ldev, idx1)->dev))
 		return false;
 
 	if (ldev->ports > MLX5_LAG_MULTIPATH_OFFLOADS_SUPPORTED_PORTS)
 		return false;
 
-	return mlx5_esw_multipath_prereq(mlx5_lag_pf(ldev, idx0)->dev,
-					 mlx5_lag_pf(ldev, idx1)->dev);
+	return mlx5_esw_multipath_prereq(mlx5_lag_fn(ldev, idx0)->dev,
+					 mlx5_lag_fn(ldev, idx1)->dev);
 }
 
 bool mlx5_lag_is_multipath(struct mlx5_core_dev *dev)
@@ -59,11 +59,15 @@ static void mlx5_lag_set_port_affinity(struct mlx5_lag *ldev,
 {
 	int idx0 = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);
 	int idx1 = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P2);
+	struct mlx5_core_dev *dev0, *dev1;
 	struct lag_tracker tracker = {};
 
 	if (idx0 < 0 || idx1 < 0 || !__mlx5_lag_is_multipath(ldev))
 		return;
 
+	dev0 = mlx5_lag_fn(ldev, idx0)->dev;
+	dev1 = mlx5_lag_fn(ldev, idx1)->dev;
+
 	switch (port) {
 	case MLX5_LAG_NORMAL_AFFINITY:
 		tracker.netdev_state[idx0].tx_enabled = true;
@@ -84,18 +88,17 @@ static void mlx5_lag_set_port_affinity(struct mlx5_lag *ldev,
 		tracker.netdev_state[idx1].link_up = true;
 		break;
 	default:
-		mlx5_core_warn(mlx5_lag_pf(ldev, idx0)->dev,
-			       "Invalid affinity port %d", port);
+		mlx5_core_warn(dev0, "Invalid affinity port %d", port);
 		return;
 	}
 
 	if (tracker.netdev_state[idx0].tx_enabled)
-		mlx5_notifier_call_chain(mlx5_lag_pf(ldev, idx0)->dev->priv.events,
+		mlx5_notifier_call_chain(dev0->priv.events,
 					 MLX5_DEV_EVENT_PORT_AFFINITY,
 					 (void *)0);
 
 	if (tracker.netdev_state[idx1].tx_enabled)
-		mlx5_notifier_call_chain(mlx5_lag_pf(ldev, idx1)->dev->priv.events,
+		mlx5_notifier_call_chain(dev1->priv.events,
 					 MLX5_DEV_EVENT_PORT_AFFINITY,
 					 (void *)0);
 
@@ -150,7 +153,7 @@ mlx5_lag_get_next_fib_dev(struct mlx5_lag *ldev,
 		fib_dev = fib_info_nh(fi, i)->fib_nh_dev;
 		ldev_idx = mlx5_lag_dev_get_netdev_idx(ldev, fib_dev);
 		if (ldev_idx >= 0)
-			return mlx5_lag_pf(ldev, ldev_idx)->netdev;
+			return mlx5_lag_fn(ldev, ldev_idx)->netdev;
 	}
 
 	return NULL;
@@ -182,7 +185,7 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event,
 	    mp->fib.dst_len <= fen_info->dst_len &&
 	    !(mp->fib.dst_len == fen_info->dst_len &&
 	      fi->fib_priority < mp->fib.priority)) {
-		mlx5_core_dbg(mlx5_lag_pf(ldev, idx)->dev,
+		mlx5_core_dbg(mlx5_lag_fn(ldev, idx)->dev,
 			      "Multipath entry with lower priority was rejected\n");
 		return;
 	}
@@ -198,7 +201,7 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event,
 	}
 
 	if (nh_dev0 == nh_dev1) {
-		mlx5_core_warn(mlx5_lag_pf(ldev, idx)->dev,
+		mlx5_core_warn(mlx5_lag_fn(ldev, idx)->dev,
 			       "Multipath offload doesn't support routes with multiple nexthops of the same device");
 		return;
 	}
@@ -207,7 +210,7 @@ static void mlx5_lag_fib_route_event(struct mlx5_lag *ldev, unsigned long event,
 		if (__mlx5_lag_is_active(ldev)) {
 			mlx5_ldev_for_each(i, 0, ldev) {
 				dev_idx++;
-				if (mlx5_lag_pf(ldev, i)->netdev == nh_dev0)
+				if (mlx5_lag_fn(ldev, i)->netdev == nh_dev0)
 					break;
 			}
 			mlx5_lag_set_port_affinity(ldev, dev_idx);
@@ -244,7 +247,7 @@ static void mlx5_lag_fib_nexthop_event(struct mlx5_lag *ldev,
 	/* nh added/removed */
 	if (event == FIB_EVENT_NH_DEL) {
 		mlx5_ldev_for_each(i, 0, ldev) {
-			if (mlx5_lag_pf(ldev, i)->netdev == fib_nh->fib_nh_dev)
+			if (mlx5_lag_fn(ldev, i)->netdev == fib_nh->fib_nh_dev)
 				break;
 			dev_idx++;
 		}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c
index abf72026c75133..6f575750e403d5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c
@@ -15,8 +15,11 @@ static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev)
 	u32 pf_metadata;
 	int i;
 
+	if (!ldev->lag_mpesw.pf_metadata)
+		return;
+
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		dev = mlx5_lag_pf(ldev, i)->dev;
+		dev = mlx5_lag_fn(ldev, i)->dev;
 		esw = dev->priv.eswitch;
 		pf_metadata = ldev->lag_mpesw.pf_metadata[i];
 		if (!pf_metadata)
@@ -27,6 +30,9 @@ static void mlx5_mpesw_metadata_cleanup(struct mlx5_lag *ldev)
 		mlx5_esw_match_metadata_free(esw, pf_metadata);
 		ldev->lag_mpesw.pf_metadata[i] = 0;
 	}
+
+	kfree(ldev->lag_mpesw.pf_metadata);
+	ldev->lag_mpesw.pf_metadata = NULL;
 }
 
 static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
@@ -36,8 +42,14 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
 	u32 pf_metadata;
 	int i, err;
 
+	ldev->lag_mpesw.pf_metadata =
+		kcalloc(ldev->max_fns, sizeof(*ldev->lag_mpesw.pf_metadata),
+			GFP_KERNEL);
+	if (!ldev->lag_mpesw.pf_metadata)
+		return -ENOMEM;
+
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		dev = mlx5_lag_pf(ldev, i)->dev;
+		dev = mlx5_lag_fn(ldev, i)->dev;
 		esw = dev->priv.eswitch;
 		pf_metadata = mlx5_esw_match_metadata_alloc(esw);
 		if (!pf_metadata) {
@@ -53,7 +65,7 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
 	}
 
 	mlx5_lag_for_each(i, 0, ldev, MLX5_LAG_FILTER_ALL) {
-		dev = mlx5_lag_pf(ldev, i)->dev;
+		dev = mlx5_lag_fn(ldev, i)->dev;
 		mlx5_notifier_call_chain(dev->priv.events, MLX5_DEV_EVENT_MULTIPORT_ESW,
 					 (void *)0);
 	}
@@ -67,14 +79,14 @@ static int mlx5_mpesw_metadata_set(struct mlx5_lag *ldev)
 
 static void mlx5_mpesw_restore_sd_fdb(struct mlx5_lag *ldev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int err, i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		err = mlx5_lag_shared_fdb_create(ldev, NULL, 0, pf->group_id);
+		fn = mlx5_lag_fn(ldev, i);
+		err = mlx5_lag_shared_fdb_create(ldev, NULL, 0, fn->group_id);
 		if (err)
-			mlx5_core_warn(pf->dev,
+			mlx5_core_warn(fn->dev,
 				       "Failed to restore SD shared FDB (%d)\n",
 				       err);
 	}
@@ -82,35 +94,58 @@ static void mlx5_mpesw_restore_sd_fdb(struct mlx5_lag *ldev)
 
 static int mlx5_mpesw_teardown_sd_fdb(struct mlx5_lag *ldev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (!pf->sd_fdb_active)
+		fn = mlx5_lag_fn(ldev, i);
+		if (!fn->sd_fdb_active)
 			continue;
-		mlx5_lag_shared_fdb_destroy(ldev, pf->group_id);
+		mlx5_lag_shared_fdb_destroy(ldev, fn->group_id);
 	}
 	return 0;
 }
 
 static bool mlx5_lag_has_sd_group(struct mlx5_lag *ldev)
 {
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->group_id)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->group_id)
 			return true;
 	}
 	return false;
 }
 
+static bool mlx5_phys_mpesw_validate(struct mlx5_lag *ldev,
+				     struct mlx5_core_dev *dev0)
+{
+	if (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||
+	    !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) ||
+	    !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) ||
+	    !mlx5_lag_check_prereq(ldev) ||
+	    !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL))
+		return false;
+	return true;
+}
+
+static bool mlx5_virt_mpesw_validate(struct mlx5_lag *ldev,
+				     struct mlx5_core_dev *dev0)
+{
+	if (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||
+	    !MLX5_CAP_PORT_SELECTION(dev0, port_select_eswitch) ||
+	    !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL))
+		return false;
+	return true;
+}
+
 static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)
 {
-	int idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);
 	struct mlx5_core_dev *dev0;
+	bool valid;
+	int idx;
 	int err;
 
 	if (ldev->mode == MLX5_LAG_MODE_MPESW)
@@ -119,20 +154,30 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)
 	if (ldev->mode != MLX5_LAG_MODE_NONE)
 		return -EINVAL;
 
-	if (idx < 0)
-		return -EINVAL;
+	/* VFs/SFs (nested LAG) are not marked by the LAG devcom PAIR event;
+	 * select the master from the current snapshot before resolving it via
+	 * mlx5_lag_get_dev_index_by_seq().
+	 */
+	if (ldev->virt_lag)
+		mlx5_virt_lag_mark_master(ldev);
 
-	dev0 = mlx5_lag_pf(ldev, idx)->dev;
-	if (mlx5_eswitch_mode(dev0) != MLX5_ESWITCH_OFFLOADS ||
-	    !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table) ||
-	    !MLX5_CAP_GEN(dev0, create_lag_when_not_master_up) ||
-	    !mlx5_lag_check_prereq(ldev) ||
-	    !mlx5_lag_shared_fdb_supported_filter(ldev, MLX5_LAG_FILTER_ALL))
-		return -EOPNOTSUPP;
+	idx = mlx5_lag_get_dev_index_by_seq(ldev, MLX5_LAG_P1);
+	if (idx < 0) {
+		err = -EINVAL;
+		goto err_clear_master;
+	}
+
+	dev0 = mlx5_lag_fn(ldev, idx)->dev;
+	valid = ldev->virt_lag ? mlx5_virt_mpesw_validate(ldev, dev0) :
+		mlx5_phys_mpesw_validate(ldev, dev0);
+	if (!valid) {
+		err = -EOPNOTSUPP;
+		goto err_clear_master;
+	}
 
 	err = mlx5_mpesw_metadata_set(ldev);
 	if (err)
-		return err;
+		goto err_clear_master;
 
 	if (mlx5_lag_has_sd_group(ldev))
 		mlx5_mpesw_teardown_sd_fdb(ldev);
@@ -143,13 +188,19 @@ static int mlx5_lag_enable_mpesw(struct mlx5_lag *ldev)
 		mlx5_core_warn(dev0,
 			       "Failed to create LAG in MPESW mode (%d)\n",
 			       err);
-		if (mlx5_lag_has_sd_group(ldev))
-			mlx5_mpesw_restore_sd_fdb(ldev);
-		mlx5_mpesw_metadata_cleanup(ldev);
-		return err;
+		goto err_restore;
 	}
 
 	return 0;
+
+err_restore:
+	if (mlx5_lag_has_sd_group(ldev))
+		mlx5_mpesw_restore_sd_fdb(ldev);
+	mlx5_mpesw_metadata_cleanup(ldev);
+err_clear_master:
+	if (ldev->virt_lag)
+		mlx5_lag_clear_master(ldev);
+	return err;
 }
 
 void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev)
@@ -157,10 +208,14 @@ void mlx5_lag_disable_mpesw(struct mlx5_lag *ldev)
 	if (ldev->mode != MLX5_LAG_MODE_MPESW)
 		return;
 
-	mlx5_mpesw_metadata_cleanup(ldev);
 	mlx5_lag_shared_fdb_destroy(ldev, MLX5_LAG_FILTER_ALL);
 	if (mlx5_lag_has_sd_group(ldev))
 		mlx5_mpesw_restore_sd_fdb(ldev);
+	mlx5_mpesw_metadata_cleanup(ldev);
+
+	/* Pair with the master mark taken at MPESW create for VFs. */
+	if (ldev->virt_lag)
+		mlx5_lag_clear_master(ldev);
 }
 
 void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev)
@@ -169,7 +224,7 @@ void mlx5_mpesw_sd_devcoms_lock(struct mlx5_lag *ldev)
 	int i;
 
 	mlx5_ldev_for_each(i, 0, ldev) {
-		sd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)->dev);
+		sd_devcom = mlx5_sd_get_devcom(mlx5_lag_fn(ldev, i)->dev);
 		if (sd_devcom)
 			mlx5_devcom_comp_lock(sd_devcom);
 	}
@@ -180,8 +235,8 @@ void mlx5_mpesw_sd_devcoms_unlock(struct mlx5_lag *ldev)
 	struct mlx5_devcom_comp_dev *sd_devcom;
 	int i;
 
-	mlx5_ldev_for_each_reverse(i, MLX5_MAX_PORTS, 0, ldev) {
-		sd_devcom = mlx5_sd_get_devcom(mlx5_lag_pf(ldev, i)->dev);
+	mlx5_ldev_for_each_reverse(i, ldev->max_fns, 0, ldev) {
+		sd_devcom = mlx5_sd_get_devcom(mlx5_lag_fn(ldev, i)->dev);
 		if (sd_devcom)
 			mlx5_devcom_comp_unlock(sd_devcom);
 	}
@@ -281,7 +336,7 @@ bool mlx5_lag_is_mpesw(struct mlx5_core_dev *dev)
 	struct mlx5_lag *ldev = mlx5_lag_dev(dev);
 
 	return ldev && ldev->mode == MLX5_LAG_MODE_MPESW &&
-	       __mlx5_lag_dev_is_port(ldev, dev);
+	       (ldev->virt_lag || __mlx5_lag_dev_is_port(ldev, dev));
 }
 EXPORT_SYMBOL(mlx5_lag_is_mpesw);
 
@@ -305,10 +360,10 @@ int mlx5_lag_mpesw_port_change_event(struct notifier_block *nb,
 				     unsigned long event, void *data)
 {
 	struct mlx5_nb *mlx5_nb = container_of(nb, struct mlx5_nb, nb);
-	struct lag_func *lag_func = container_of(mlx5_nb,
-						 struct lag_func,
+	struct lag_fn *lag_fn = container_of(mlx5_nb,
+						 struct lag_fn,
 						 port_change_nb);
-	struct mlx5_core_dev *dev = lag_func->dev;
+	struct mlx5_core_dev *dev = lag_fn->dev;
 	struct mlx5_lag *ldev = dev->priv.lag;
 	struct mlx5_eqe *eqe = data;
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h
index 5099723ba0f749..6296e3edd38cdb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h
@@ -9,7 +9,7 @@
 
 struct lag_mpesw {
 	struct work_struct mpesw_work;
-	u32 pf_metadata[MLX5_MAX_PORTS];
+	u32 *pf_metadata;
 };
 
 enum mpesw_op {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c
index 2a034b2a3eee3e..183e1891009513 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c
@@ -50,7 +50,7 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev,
 	if (first_idx < 0)
 		return -EINVAL;
 
-	dev = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev = mlx5_lag_fn(ldev, first_idx)->dev;
 	ft_attr.max_fte = ldev->ports * ldev->buckets;
 	ft_attr.level = MLX5_LAG_FT_LEVEL_DEFINER;
 
@@ -79,6 +79,7 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev,
 	flow_act.flags |= FLOW_ACT_NO_APPEND;
 	mlx5_ldev_for_each(i, 0, ldev) {
 		for (j = 0; j < ldev->buckets; j++) {
+			struct mlx5_core_dev *mdev;
 			u8 affinity;
 
 			idx = i * ldev->buckets + j;
@@ -87,9 +88,8 @@ static int mlx5_lag_create_port_sel_table(struct mlx5_lag *ldev,
 			/* affinity is 1-indexed device index,
 			 * use reverse lookup.
 			 */
-			dest.vport.vhca_id =
-				MLX5_CAP_GEN(mlx5_lag_pf_by_dev_idx(ldev, affinity - 1)->dev,
-					     vhca_id);
+			mdev = mlx5_lag_fn_by_dev_idx(ldev, affinity - 1)->dev;
+			dest.vport.vhca_id = MLX5_CAP_GEN(mdev, vhca_id);
 			lag_definer->rules[idx] = mlx5_add_flow_rules(lag_definer->ft,
 								      NULL, &flow_act,
 								      &dest, 1);
@@ -311,7 +311,7 @@ mlx5_lag_create_definer(struct mlx5_lag *ldev, enum netdev_lag_hash hash,
 	if (first_idx < 0)
 		return ERR_PTR(-EINVAL);
 
-	dev = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev = mlx5_lag_fn(ldev, first_idx)->dev;
 	lag_definer = kzalloc_obj(*lag_definer);
 	if (!lag_definer)
 		return ERR_PTR(-ENOMEM);
@@ -360,7 +360,7 @@ static void mlx5_lag_destroy_definer(struct mlx5_lag *ldev,
 	if (first_idx < 0)
 		return;
 
-	dev = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev = mlx5_lag_fn(ldev, first_idx)->dev;
 	mlx5_ldev_for_each(i, 0, ldev) {
 		for (j = 0; j < ldev->buckets; j++) {
 			idx = i * ldev->buckets + j;
@@ -524,7 +524,7 @@ static int mlx5_lag_create_ttc_table(struct mlx5_lag *ldev)
 	if (first_idx < 0)
 		return -EINVAL;
 
-	dev = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev = mlx5_lag_fn(ldev, first_idx)->dev;
 	mlx5_lag_set_outer_ttc_params(ldev, &ttc_params);
 	port_sel->outer.ttc = mlx5_create_ttc_table(dev, &ttc_params);
 	return PTR_ERR_OR_ZERO(port_sel->outer.ttc);
@@ -540,7 +540,7 @@ static int mlx5_lag_create_inner_ttc_table(struct mlx5_lag *ldev)
 	if (first_idx < 0)
 		return -EINVAL;
 
-	dev = mlx5_lag_pf(ldev, first_idx)->dev;
+	dev = mlx5_lag_fn(ldev, first_idx)->dev;
 	mlx5_lag_set_inner_ttc_params(ldev, &ttc_params);
 	port_sel->inner.ttc = mlx5_create_inner_ttc_table(dev, &ttc_params);
 	return PTR_ERR_OR_ZERO(port_sel->inner.ttc);
@@ -594,6 +594,9 @@ static int __mlx5_lag_modify_definers_destinations(struct mlx5_lag *ldev,
 
 	mlx5_ldev_for_each(i, 0, ldev) {
 		for (j = 0; j < ldev->buckets; j++) {
+			struct mlx5_core_dev *mdev;
+			u8 affinity;
+
 			idx = i * ldev->buckets + j;
 			if (ldev->v2p_map[idx] == ports[idx])
 				continue;
@@ -601,9 +604,9 @@ static int __mlx5_lag_modify_definers_destinations(struct mlx5_lag *ldev,
 			/* ports[] contains 1-indexed device indices,
 			 * use reverse lookup.
 			 */
-			dest.vport.vhca_id =
-				MLX5_CAP_GEN(mlx5_lag_pf_by_dev_idx(ldev, ports[idx] - 1)->dev,
-					     vhca_id);
+			affinity = ports[idx];
+			mdev = mlx5_lag_fn_by_dev_idx(ldev, affinity - 1)->dev;
+			dest.vport.vhca_id = MLX5_CAP_GEN(mdev, vhca_id);
 			err = mlx5_modify_rule_destination(def->rules[idx], &dest, NULL);
 			if (err)
 				return err;
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 7c7e6c832a93d9..90209e4b99f78d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c
@@ -19,11 +19,11 @@ bool mlx5_lag_shared_fdb_supported_filter(struct mlx5_lag *ldev, u32 filter)
 	if (idx < 0)
 		return false;
 
-	dev0 = mlx5_lag_pf(ldev, idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, idx)->dev;
 	mlx5_lag_for_each(i, 0, ldev, filter) {
 		if (i == idx)
 			continue;
-		dev = mlx5_lag_pf(ldev, i)->dev;
+		dev = mlx5_lag_fn(ldev, i)->dev;
 		if (is_mdev_switchdev_mode(dev) &&
 		    mlx5_eswitch_vport_match_metadata_enabled(dev->priv.eswitch) &&
 		    MLX5_CAP_GEN(dev, lag_native_fdb_selection) &&
@@ -59,7 +59,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
 	if (master_idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, master_idx)->dev;
 	master_esw = dev0->priv.eswitch;
 	mlx5_lag_for_each(i, 0, ldev, filter) {
 		struct mlx5_eswitch *slave_esw;
@@ -67,11 +67,11 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
 		if (i == master_idx)
 			continue;
 
-		slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch;
+		slave_esw = mlx5_lag_fn(ldev, i)->dev->priv.eswitch;
 
 		err = mlx5_eswitch_offloads_single_fdb_add_one(master_esw,
 							       slave_esw,
-							       ldev->ports);
+							       ldev->max_fns);
 		if (err)
 			goto err;
 	}
@@ -82,7 +82,7 @@ static int mlx5_lag_create_single_fdb_filter(struct mlx5_lag *ldev, u32 filter)
 
 		if (j == master_idx)
 			continue;
-		slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch;
+		slave_esw = mlx5_lag_fn(ldev, j)->dev->priv.eswitch;
 		mlx5_eswitch_offloads_single_fdb_del_one(master_esw, slave_esw);
 	}
 	return err;
@@ -101,7 +101,7 @@ int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)
 	if (master_idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, master_idx)->dev;
 	master_esw = dev0->priv.eswitch;
 
 	mlx5_lag_for_each(i, 0, ldev, filter) {
@@ -110,7 +110,7 @@ int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)
 		if (i == master_idx)
 			continue;
 
-		slave_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch;
+		slave_esw = mlx5_lag_fn(ldev, i)->dev->priv.eswitch;
 		err = mlx5_eswitch_offloads_vport_lag_add_one(master_esw,
 							      slave_esw);
 		if (err)
@@ -125,8 +125,9 @@ int mlx5_lag_create_vport_lag(struct mlx5_lag *ldev, u32 group_id)
 
 		if (j == master_idx)
 			continue;
-		slave_esw = mlx5_lag_pf(ldev, j)->dev->priv.eswitch;
-		mlx5_eswitch_offloads_vport_lag_del_one(master_esw, slave_esw);
+		slave_esw = mlx5_lag_fn(ldev, j)->dev->priv.eswitch;
+		mlx5_eswitch_offloads_vport_lag_del_one(master_esw, slave_esw,
+							ldev->max_fns);
 	}
 	return err;
 }
@@ -143,7 +144,7 @@ int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id)
 	if (master_idx < 0)
 		return 0;
 
-	dev0 = mlx5_lag_pf(ldev, master_idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, master_idx)->dev;
 	master_esw = dev0->priv.eswitch;
 
 	mlx5_lag_for_each(i, 0, ldev, filter) {
@@ -151,9 +152,10 @@ int mlx5_lag_destroy_vport_lag(struct mlx5_lag *ldev, u32 group_id)
 
 		if (i == master_idx)
 			continue;
-		dev = mlx5_lag_pf(ldev, i)->dev;
+		dev = mlx5_lag_fn(ldev, i)->dev;
 		mlx5_eswitch_offloads_vport_lag_del_one(master_esw,
-							dev->priv.eswitch);
+							dev->priv.eswitch,
+							ldev->max_fns);
 	}
 	return 0;
 }
@@ -170,12 +172,12 @@ static void mlx5_lag_destroy_single_fdb_filter(struct mlx5_lag *ldev,
 	if (master_idx < 0)
 		return;
 
-	master_esw = mlx5_lag_pf(ldev, master_idx)->dev->priv.eswitch;
+	master_esw = mlx5_lag_fn(ldev, master_idx)->dev->priv.eswitch;
 	mlx5_lag_for_each(i, 0, ldev, filter) {
 		if (i == master_idx)
 			continue;
 
-		peer_esw = mlx5_lag_pf(ldev, i)->dev->priv.eswitch;
+		peer_esw = mlx5_lag_fn(ldev, i)->dev->priv.eswitch;
 		mlx5_eswitch_offloads_single_fdb_del_one(master_esw, peer_esw);
 	}
 }
@@ -214,14 +216,14 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
 	int idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1,
 						       filter);
 	struct mlx5_core_dev *dev0;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int err;
 	int i;
 
 	if (idx < 0)
 		return -EINVAL;
 
-	dev0 = mlx5_lag_pf(ldev, idx)->dev;
+	dev0 = mlx5_lag_fn(ldev, idx)->dev;
 
 	mlx5_lag_remove_devices_filter(ldev, filter);
 
@@ -242,8 +244,8 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
 			goto err_add_devices;
 		}
 		mlx5_lag_for_each(i, 0, ldev, filter) {
-			pf = mlx5_lag_pf(ldev, i);
-			pf->sd_fdb_active = true;
+			fn = mlx5_lag_fn(ldev, i);
+			fn->sd_fdb_active = true;
 		}
 		BLOCKING_INIT_NOTIFIER_HEAD(&dev0->priv.lag_nh);
 	}
@@ -265,8 +267,8 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
 		mlx5_deactivate_lag(ldev);
 	} else {
 		mlx5_lag_for_each(i, 0, ldev, filter) {
-			pf = mlx5_lag_pf(ldev, i);
-			pf->sd_fdb_active = false;
+			fn = mlx5_lag_fn(ldev, i);
+			fn->sd_fdb_active = false;
 		}
 		mlx5_lag_destroy_single_fdb_filter(ldev, group_id);
 		mlx5_lag_unload_reps_from_locked(ldev, filter);
@@ -280,7 +282,7 @@ int mlx5_lag_shared_fdb_create(struct mlx5_lag *ldev,
 void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)
 {
 	u32 filter = group_id ? group_id : MLX5_LAG_FILTER_ALL;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int err;
 	int i;
 
@@ -292,8 +294,8 @@ void mlx5_lag_shared_fdb_destroy(struct mlx5_lag *ldev, u32 group_id)
 			return;
 	} else {
 		mlx5_lag_for_each(i, 0, ldev, filter) {
-			pf = mlx5_lag_pf(ldev, i);
-			pf->sd_fdb_active = false;
+			fn = mlx5_lag_fn(ldev, i);
+			fn->sd_fdb_active = false;
 		}
 		mlx5_lag_destroy_single_fdb_filter(ldev, group_id);
 		mlx5_lag_unload_reps_from_locked(ldev, filter);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
index 4cdc50cd6f0382..f843efbca660cf 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
@@ -300,7 +300,7 @@ static int sd_lag_state_show(struct seq_file *file, void *priv)
 {
 	struct mlx5_core_dev *dev = file->private;
 	struct mlx5_lag *ldev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	bool active = false;
 	int i;
 
@@ -310,9 +310,9 @@ static int sd_lag_state_show(struct seq_file *file, void *priv)
 
 	mutex_lock(&ldev->lock);
 	mlx5_ldev_for_each(i, 0, ldev) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->dev == dev) {
-			active = pf->sd_fdb_active;
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->dev == dev) {
+			active = fn->sd_fdb_active;
 			break;
 		}
 	}
@@ -334,7 +334,7 @@ static void sd_lag_init(struct mlx5_core_dev *dev)
 	struct mlx5_sd *sd = mlx5_get_sd(primary);
 	struct mlx5_core_dev *pos, *to;
 	struct mlx5_lag *ldev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int err;
 	int i;
 
@@ -346,14 +346,14 @@ static void sd_lag_init(struct mlx5_core_dev *dev)
 	}
 
 	mutex_lock(&ldev->lock);
-	pf = mlx5_lag_pf_by_dev(ldev, primary);
-	if (!pf) {
+	fn = mlx5_lag_fn_by_dev(ldev, primary);
+	if (!fn) {
 		sd_warn(primary, "%s: primary not registered in ldev, skipping\n",
 			__func__);
 		goto out;
 	}
 
-	pf->group_id = sd->group_id;
+	fn->group_id = sd->group_id;
 
 	mlx5_sd_for_each_secondary(i, primary, pos) {
 		err = mlx5_ldev_add_mdev(ldev, pos, sd->group_id);
@@ -372,7 +372,7 @@ static void sd_lag_init(struct mlx5_core_dev *dev)
 	to = pos;
 	mlx5_sd_for_each_secondary_to(i, primary, to, pos)
 		mlx5_ldev_remove_mdev(ldev, pos);
-	pf->group_id = 0;
+	fn->group_id = 0;
 	mutex_unlock(&ldev->lock);
 }
 
@@ -381,7 +381,7 @@ static void sd_lag_cleanup(struct mlx5_core_dev *dev)
 	struct mlx5_core_dev *primary = mlx5_sd_get_primary(dev);
 	struct mlx5_core_dev *pos;
 	struct mlx5_lag *ldev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int i;
 
 	ldev = mlx5_lag_dev(primary);
@@ -392,9 +392,9 @@ static void sd_lag_cleanup(struct mlx5_core_dev *dev)
 	mlx5_sd_for_each_secondary(i, primary, pos)
 		mlx5_ldev_remove_mdev(ldev, pos);
 
-	pf = mlx5_lag_pf_by_dev(ldev, primary);
-	if (pf)
-		pf->group_id = 0;
+	fn = mlx5_lag_fn_by_dev(ldev, primary);
+	if (fn)
+		fn->group_id = 0;
 	mutex_unlock(&ldev->lock);
 }
 
@@ -989,7 +989,7 @@ static void mlx5_sd_activate_shared_fdb(struct mlx5_core_dev *primary)
 	struct mlx5_sd *sd = mlx5_get_sd(primary);
 	struct mlx5_core_dev *pos;
 	struct mlx5_lag *ldev;
-	struct lag_func *pf;
+	struct lag_fn *fn;
 	int err;
 	int i;
 
@@ -1009,8 +1009,8 @@ static void mlx5_sd_activate_shared_fdb(struct mlx5_core_dev *primary)
 
 	/* Check if SD FDB is already active for this group */
 	mlx5_lag_for_each(i, 0, ldev, sd->group_id) {
-		pf = mlx5_lag_pf(ldev, i);
-		if (pf->sd_fdb_active)
+		fn = mlx5_lag_fn(ldev, i);
+		if (fn->sd_fdb_active)
 			goto unlock;
 		break;
 	}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index d84d0424d04e1c..f5f105762d884c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -275,6 +275,7 @@ void mlx5_lag_add_mdev(struct mlx5_core_dev *dev);
 void mlx5_lag_remove_mdev(struct mlx5_core_dev *dev);
 void mlx5_lag_disable_change(struct mlx5_core_dev *dev);
 void mlx5_lag_enable_change(struct mlx5_core_dev *dev);
+int mlx5_lag_get_member_idx(struct mlx5_core_dev *dev);
 
 int mlx5_events_init(struct mlx5_core_dev *dev);
 void mlx5_events_cleanup(struct mlx5_core_dev *dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
index 3ab0449c770cbb..382162baabdb35 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
@@ -19,6 +19,13 @@ struct mlx5_sf_dev {
 	u16 fn_id;
 };
 
+static inline u32 mlx5_sf_coredev_sfnum(struct mlx5_core_dev *mdev)
+{
+	struct auxiliary_device *adev = mlx5_sf_coredev_to_adev(mdev);
+
+	return container_of(adev, struct mlx5_sf_dev, adev)->sfnum;
+}
+
 struct mlx5_sf_peer_devlink_event_ctx {
 	u16 fn_id;
 	struct devlink *devlink;
@@ -68,6 +75,11 @@ static inline bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev)
 	return false;
 }
 
+static inline u32 mlx5_sf_coredev_sfnum(struct mlx5_core_dev *mdev)
+{
+	return 0;
+}
+
 #endif
 
 #endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
index 78f3d15b599d00..50d2609374c4b1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
@@ -38,6 +38,7 @@
 #include "mlx5_core.h"
 #include "eswitch.h"
 #include "sf/sf.h"
+#include "lag/lag.h"
 
 /* Mutex to hold while enabling or disabling RoCE */
 static DEFINE_MUTEX(mlx5_roce_en_lock);
@@ -1308,10 +1309,37 @@ u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev)
 }
 EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid);
 
+static u8 mlx5_sw_guid_load_balance(const struct mlx5_core_dev *mdev)
+{
+	if (MLX5_CAP_GEN_2(mdev, load_balance_id) &&
+	    MLX5_CAP_GEN_2(mdev, lag_per_mp_group))
+		return MLX5_CAP_GEN_2(mdev, load_balance_id);
+	return 0;
+}
+
+static u8 mlx5_sw_guid_sf(const struct mlx5_core_dev *mdev)
+{
+	return mlx5_core_is_sf(mdev) && mlx5_virt_lag_is_supported(mdev);
+}
+
+static u8 mlx5_sw_guid_vf(const struct mlx5_core_dev *mdev)
+{
+	return mlx5_core_is_vf(mdev) && mlx5_virt_lag_is_supported(mdev);
+}
+
+static u8 (* const mlx5_sw_guid_fields[])(const struct mlx5_core_dev *mdev) = {
+	mlx5_sw_guid_load_balance,
+	mlx5_sw_guid_vf,
+	mlx5_sw_guid_sf,
+	/* New fields should be added at the end */
+};
+
 void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf,
 					 u8 *len)
 {
 	u64 fw_system_image_guid;
+	u8 trim_len;
+	int i;
 
 	*len = 0;
 
@@ -1321,10 +1349,17 @@ void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf,
 
 	memcpy(buf, &fw_system_image_guid, sizeof(fw_system_image_guid));
 	*len += sizeof(fw_system_image_guid);
+	trim_len = *len;
 
-	if (MLX5_CAP_GEN_2(mdev, load_balance_id) &&
-	    MLX5_CAP_GEN_2(mdev, lag_per_mp_group))
-		buf[(*len)++] = MLX5_CAP_GEN_2(mdev, load_balance_id);
+	for (i = 0; i < ARRAY_SIZE(mlx5_sw_guid_fields); i++) {
+		u8 val = mlx5_sw_guid_fields[i](mdev);
+
+		buf[(*len)++] = val;
+		if (val)
+			trim_len = *len;
+	}
+
+	*len = trim_len;
 }
 
 bool mlx5_vport_use_vhca_id_as_func_id(struct mlx5_core_dev *dev,
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index abb99371ff1344..d9d91faa0a1dee 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -1395,7 +1395,7 @@ static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev)
 	return devlink_net(priv_to_devlink(dev));
 }
 
-#define MLX5_SW_IMAGE_GUID_MAX_BYTES 9
+#define MLX5_SW_IMAGE_GUID_MAX_BYTES 11
 
 static inline bool mlx5_data_direct_supported(struct mlx5_core_dev *mdev)
 {

3/2 2026/11/05 18:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 45642
							output: 37
							thoughts: 784
						

Thoughts:
**Evaluating the Patch's Suitability for Syzkaller Fuzzing**

Okay, so I need to decide if this patch warrants fuzzing with syzkaller. First things first, let's analyze the diff. Looks like it touches a lot of files in the Mellanox mlx5 driver directory. *That's a key detail.* Now I need to check the rules.

Hang on a second. Section 2, the negative criteria, mentions "structurally unreachable hardware," specifically calling out vendor-specific NIC drivers like mlxsw, which require hardware not emulated in standard QEMU. *This is where it gets tricky.* Does mlx5 fall into that category? Is there any virtual or emulated mlx5 hardware support in QEMU?

I know QEMU doesn't directly emulate ConnectX NICs (mlx4/mlx5). But, can mlx5 be utilized through soft-RoCE, virtio, or an auxiliary bus interface? Is it possible to instantiate mlx5 in a software-only environment? I remember mlx5 being a PCI device driver, specifically `MLX5_CORE`, that means it relies on PCI device functionality.

I need to confirm if syzkaller currently supports or considers mlx5 to be structurally unreachable. I need to make sure that mlx5 isn't treated the same way as mlxsw in this context. Perhaps there are virtual or mock drivers somewhere within the mlx5 ecosystem, or maybe something similar to mlxsw.

I'm going to look for any virtual device support or PCI ID emulation related to mlx5 in the kernel and in syzkaller. I'll need to check the kernel tree and syzkaller's source code for any signs of it. I'll need to get in there and use `grepper` to see if there's any mention of virtual or mock mlx5 devices. Let's see if there's any way to make this work in a fuzzing environment.



4/2 2026/11/05 18:30 tool grepper
0m Args:
{
  "Expression": "mlx5",
  "PathPrefixes": [
    "drivers/net/ethernet/mellanox/mlx5/"
  ]
}

Results:
{
  "Output": "\nFull output is too long, showing 200 out of 99737 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (407 files in total):\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig:5\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:37\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:110\ndrivers/net/ethernet/mellanox/mlx5/core/cmd.c:324\ndrivers/net/ethernet/mellanox/mlx5/core/cq.c:49\ndrivers/net/ethernet/mellanox/mlx5/core/data_direct.c:90\ndrivers/net/ethernet/mellanox/mlx5/core/debugfs.c:69\ndrivers/net/ethernet/mellanox/mlx5/core/dev.c:113\ndrivers/net/ethernet/mellanox/mlx5/core/devlink.c:241\ndrivers/net/ethernet/mellanox/mlx5/core/devlink.h:19\ndrivers/net/ethernet/mellanox/mlx5/core/diag/cmd_tracepoint.h:2\ndrivers/net/ethernet/mellanox/mlx5/core/diag/crdump.c:29\ndrivers/net/ethernet/mellanox/mlx5/core/diag/en_rep_tracepoint.h:4\ndrivers/net/ethernet/mellanox/mlx5/core/diag/en_tc_tracepoint.h:8\ndrivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c:10\ndrivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h:27\ndrivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c:194\ndrivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.h:19\ndrivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer_tracepoint.h:3\ndrivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.c:24\ndrivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.h:4\ndrivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.c:52\ndrivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.h:13\ndrivers/net/ethernet/mellanox/mlx5/core/dpll.c:115\ndrivers/net/ethernet/mellanox/mlx5/core/ecpf.c:29\ndrivers/net/ethernet/mellanox/mlx5/core/ecpf.h:10\ndrivers/net/ethernet/mellanox/mlx5/core/en.h:429\ndrivers/net/ethernet/mellanox/mlx5/core/en/channels.c:23\ndrivers/net/ethernet/mellanox/mlx5/core/en/channels.h:10\ndrivers/net/ethernet/mellanox/mlx5/core/en/dcbnl.h:13\ndrivers/net/ethernet/mellanox/mlx5/core/en/devlink.c:22\ndrivers/net/ethernet/mellanox/mlx5/core/en/devlink.h:6\ndrivers/net/ethernet/mellanox/mlx5/core/en/dim.h:10\ndrivers/net/ethernet/mellanox/mlx5/core/en/fs.h:89\ndrivers/net/ethernet/mellanox/mlx5/core/en/fs_ethtool.h:18\ndrivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c:104\ndrivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.h:10\ndrivers/net/ethernet/mellanox/mlx5/core/en/health.c:48\ndrivers/net/ethernet/mellanox/mlx5/core/en/health.h:30\ndrivers/net/ethernet/mellanox/mlx5/core/en/htb.c:145\ndrivers/net/ethernet/mellanox/mlx5/core/en/htb.h:17\ndrivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.c:42\ndrivers/net/ethernet/mellanox/mlx5/core/en/hv_vhca_stats.h:4\ndrivers/net/ethernet/mellanox/mlx5/core/en/mapping.c:1\ndrivers/net/ethernet/mellanox/mlx5/core/en/mod_hdr.c:22\ndrivers/net/ethernet/mellanox/mlx5/core/en/mod_hdr.h:18\ndrivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c:34\ndrivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.h:3\ndrivers/net/ethernet/mellanox/mlx5/core/en/params.c:312\ndrivers/net/ethernet/mellanox/mlx5/core/en/params.h:114\n... and 357 more files\n\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig=19=config MLX5_FPGA\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-25-\t  and an FPGA chip on one board. If you select this option, the\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig:26:\t  mlx5_core driver will include the Innova FPGA core and allow building\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-27-\t  sandbox-specific client drivers.\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig=77=config MLX5_BRIDGE\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-81-\thelp\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig:82:\t  mlx5 ConnectX offloads support for Ethernet Bridging (BRIDGE).\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-83-\t  Enable offloading FDB rules from a bridge device containing\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig:84:\t  representors of mlx5 uplink and VF ports. Supports VLANs (trunk and\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-85-\t  access modes).\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig=87=config MLX5_CLS_ACT\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-91-\thelp\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig:92:\t  mlx5 ConnectX offloads support for TC classifier action (NET_CLS_ACT),\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-93-\t  works in both native NIC mode and Switchdev SRIOV mode.\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig=210=config MLX5_EN_PSP\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-215-\thelp\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig:216:\t  mlx5 device offload support for Google PSP Security Protocol offload.\ndrivers/net/ethernet/mellanox/mlx5/core/Kconfig-217-\t  Adds support for PSP encryption offload and for SPI and key generation\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile=7=subdir-ccflags-y += -I$(src)\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-8-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:9:obj-$(CONFIG_MLX5_CORE) += mlx5_core.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-10-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-11-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:12:# mlx5 core basic\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-13-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:14:mlx5_core-y :=\tmain.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-15-\t\thealth.o mcg.o cq.o alloc.o port.o mr.o pd.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-25-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:26:mlx5_core-$(CONFIG_MLX5_CORE_EN) += en/rqt.o en/tir.o en/rss.o en/rx_res.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-27-\t\ten/channels.o en_main.o en_common.o en_fs.o en_ethtool.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-37-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:38:mlx5_core-$(CONFIG_MLX5_EN_ARFS)     += en_arfs.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:39:mlx5_core-$(CONFIG_MLX5_EN_RXNFC)    += en_fs_ethtool.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:40:mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:41:mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += en/hv_vhca_stats.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:42:mlx5_core-$(CONFIG_MLX5_ESWITCH)     += lag/mp.o lag/port_sel.o lib/geneve.o lib/port_tun.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-43-\t\t\t\t\ten_rep.o en/rep/bond.o en/mod_hdr.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-44-\t\t\t\t\ten/mapping.o lag/mpesw.o lag/shared_fdb.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:45:mlx5_core-$(CONFIG_MLX5_CLS_ACT)     += en_tc.o en/rep/tc.o en/rep/neigh.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-46-\t\t\t\t\tlib/fs_chains.o en/tc_tun.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-52-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:53:mlx5_core-$(CONFIG_MLX5_CLS_ACT)     += en/tc/act/act.o en/tc/act/drop.o en/tc/act/trap.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-54-\t\t\t\t\ten/tc/act/accept.o en/tc/act/mark.o en/tc/act/goto.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile=61=ifneq ($(CONFIG_MLX5_TC_CT),)\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:62:\tmlx5_core-y\t\t\t     += en/tc_ct.o en/tc/ct_fs_dmfs.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:63:\tmlx5_core-$(CONFIG_MLX5_SW_STEERING) += en/tc/ct_fs_smfs.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:64:\tmlx5_core-$(CONFIG_MLX5_HW_STEERING) += en/tc/ct_fs_hmfs.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-65-endif\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-66-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:67:mlx5_core-$(CONFIG_MLX5_TC_SAMPLE)   += en/tc/sample.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-68-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-71-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:72:mlx5_core-$(CONFIG_MLX5_ESWITCH)   += eswitch.o eswitch_offloads.o eswitch_offloads_termtbl.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-73-\t\t\t\t      ecpf.o rdma.o esw/legacy.o esw/adj_vport.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-75-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:76:mlx5_core-$(CONFIG_MLX5_ESWITCH)   += esw/acl/helper.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-77-\t\t\t\t      esw/acl/egress_lgcy.o esw/acl/egress_ofld.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile=80=ifneq ($(CONFIG_MLX5_EN_IPSEC),)\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:81:\tmlx5_core-$(CONFIG_MLX5_ESWITCH)   += esw/ipsec_fs.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-82-endif\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-83-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:84:mlx5_core-$(CONFIG_MLX5_BRIDGE)    += esw/bridge.o esw/bridge_mcast.o esw/bridge_debugfs.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-85-\t\t\t\t      en/rep/bridge.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-86-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:87:mlx5_core-$(CONFIG_HWMON)          += hwmon.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:88:mlx5_core-$(CONFIG_MLX5_MPFS)      += lib/mpfs.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-89-ifneq ($(CONFIG_VXLAN),)\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:90:\tmlx5_core-y\t\t   += lib/vxlan.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-91-endif\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:92:mlx5_core-$(CONFIG_PTP_1588_CLOCK) += lib/clock.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:93:mlx5_core-$(CONFIG_PCI_HYPERV_INTERFACE) += lib/hv.o lib/hv_vhca.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-94-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-97-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:98:mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o ipoib/ipoib_vlan.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-99-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-102-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:103:mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-104-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:105:mlx5_core-$(CONFIG_MLX5_MACSEC) += en_accel/macsec.o lib/macsec_fs.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-106-\t\t\t\t      en_accel/macsec_stats.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-107-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:108:mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-109-\t\t\t\t     en_accel/ipsec_stats.o en_accel/ipsec_fs.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-111-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:112:mlx5_core-$(CONFIG_MLX5_EN_TLS) += en_accel/ktls_stats.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-113-\t\t\t\t   en_accel/fs_tcp.o en_accel/ktls.o en_accel/ktls_txrx.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-115-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:116:mlx5_core-$(CONFIG_MLX5_EN_PSP) += en_accel/psp.o en_accel/psp_rxtx.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-117-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-120-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:121:mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/sws/dr_domain.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-122-\t\t\t\t\tsteering/sws/dr_table.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-145-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:146:mlx5_core-$(CONFIG_MLX5_HW_STEERING) += steering/hws/cmd.o \\\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-147-\t\t\t\t\tsteering/hws/context.o \\\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-167-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:168:mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o irq_affinity.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-169-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-172-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:173:mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-174-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-177-#\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:178:mlx5_core-$(CONFIG_PCIE_TPH) += lib/st.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile-179-\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:180:obj-$(CONFIG_MLX5_DPLL) += mlx5_dpll.o\ndrivers/net/ethernet/mellanox/mlx5/core/Makefile:181:mlx5_dpll-y :=\tdpll.o\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-42-#include \u003clinux/seq_file.h\u003e\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:43:#include \u003clinux/mlx5/driver.h\u003e\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-44-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:45:#include \"mlx5_core.h\"\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-46-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-50-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:51:struct mlx5_dma_pool {\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-52-\t/* Protects page_list and per-page allocation bitmaps. */\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-54-\tstruct list_head page_list;\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:55:\tstruct mlx5_core_dev *dev;\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-56-\tint node;\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-59-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:60:struct mlx5_dma_pool_page {\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:61:\tstruct mlx5_dma_pool *pool;\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-62-\tstruct list_head pool_link;\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-67-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:68:struct mlx5_frag_buf_node_pools {\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:69:\tstruct mlx5_dma_pool *pools[MLX5_FRAG_BUF_POOLS_NUM];\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-70-};\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-71-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:72:struct mlx5_dma_pool_stats {\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-73-\tint node;\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-82-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:83:static void *mlx5_dma_zalloc_coherent_node(struct mlx5_core_dev *dev,\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-84-\t\t\t\t\t   size_t size, dma_addr_t *dma_handle,\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-86-{\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:87:\tstruct device *device = mlx5_core_dma_dev(dev);\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:88:\tstruct mlx5_priv *priv = \u0026dev-\u003epriv;\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-89-\tint original_node;\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-101-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:102:static void mlx5_dma_pool_destroy(struct mlx5_dma_pool *pool)\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-103-{\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-107-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:108:static struct mlx5_dma_pool *mlx5_dma_pool_create(struct mlx5_core_dev *dev,\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-109-\t\t\t\t\t\t  int node, u8 block_shift)\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-110-{\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:111:\tstruct mlx5_dma_pool *pool;\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-112-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-124-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:125:static struct mlx5_dma_pool_page *\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:126:mlx5_dma_pool_page_alloc(struct mlx5_dma_pool *pool)\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-127-{\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-128-\tint blocks_per_page = BIT(PAGE_SHIFT - pool-\u003eblock_shift);\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:129:\tstruct mlx5_dma_pool_page *page;\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-130-\tint node = pool-\u003enode;\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-141-\tbitmap_fill(page-\u003ebitmap, blocks_per_page);\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:142:\tpage-\u003ebuf = mlx5_dma_zalloc_coherent_node(pool-\u003edev, PAGE_SIZE,\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-143-\t\t\t\t\t\t  \u0026page-\u003edma, node);\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-156-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:157:static void mlx5_dma_pool_page_free(struct mlx5_core_dev *dev,\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:158:\t\t\t\t    struct mlx5_dma_pool_page *page)\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-159-{\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:160:\tdma_free_coherent(mlx5_core_dma_dev(dev), PAGE_SIZE, page-\u003ebuf,\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-161-\t\t\t  page-\u003edma);\n--\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c-165-\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:166:static int mlx5_dma_pool_alloc_from_page(struct mlx5_dma_pool *pool,\ndrivers/net/ethernet/mellanox/mlx5/core/alloc.c:167:\t\t\t\t\t struct mlx5_dma_pool_page *page,\n\n"
}

5/2 2026/11/05 18:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54372
							output: 41
							thoughts: 381
						

Thoughts:
**My Reasoning on WorthFuzzing**

Okay, let's carefully revisit Section 2 of these instructions. Specifically, the "NEGATIVE CRITERIA" for returning `WorthFuzzing=false`. The crucial part says that we should only return `false` if *all* modified code falls into certain categories. The relevant one here is "Structurally unreachable hardware: Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU."

Aha, that's it! Now, that list provides examples. I see "mlxsw". Wait a second... is `mlx5` a vendor-specific SmartNIC or PCIe NIC driver that *requires* physical ASIC/PCIe cards? And is it not emulated in standard QEMU?

What exactly *is* `mlx5`? Right, that's Mellanox ConnectX-4, ConnectX-5, ConnectX-6, ConnectX-7, and BlueField, etc. Now the question is, does standard QEMU actually emulate these? I know QEMU pretty well... Nope. It handles things like e1000, igb, virtio-net, etc., but *no* emulation of Mellanox `mlx5` hardware.

Hmm, but what about subfunctions (SF) or virtual functions (VF) of `mlx5`? Can I probe or interact with `mlx5` in any meaningful way *without* a physical Mellanox PCI device? That's the real question. I need to check how `mlx5_core` does its probing. That's the key to figuring this out.



6/2 2026/11/05 18:30 tool grepper
0m Args:
{
  "Expression": "mlx5_init_one",
  "PathPrefixes": [
    "drivers/net/ethernet/mellanox/mlx5/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/mellanox/mlx5/core/devlink.c=243=static int mlx5_devlink_reload_up(struct devlink *devlink, enum devlink_reload_action action,\n--\ndrivers/net/ethernet/mellanox/mlx5/core/devlink.c-254-\t\t\tmlx5_fw_reporters_create(dev);\ndrivers/net/ethernet/mellanox/mlx5/core/devlink.c:255:\t\t\treturn mlx5_init_one_devl_locked(dev);\ndrivers/net/ethernet/mellanox/mlx5/core/devlink.c-256-\t\t}\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=1375=static void mlx5_unload(struct mlx5_core_dev *dev)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1398-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:1399:int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev)\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1400-{\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1462-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:1463:int mlx5_init_one(struct mlx5_core_dev *dev)\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1464-{\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1474-\tdevl_register(devlink);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:1475:\terr = mlx5_init_one_devl_locked(dev);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1476-\tif (err)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=1605=static int mlx5_query_hca_caps_light(struct mlx5_core_dev *dev)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1638-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:1639:int mlx5_init_one_light(struct mlx5_core_dev *dev)\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1640-{\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=1939=static int probe_one(struct pci_dev *pdev, const struct pci_device_id *id)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1981-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:1982:\terr = mlx5_init_one(dev);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1983-\tif (err) {\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:1984:\t\tmlx5_core_err(dev, \"mlx5_init_one failed with error code %d\\n\",\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-1985-\t\t\t      err);\n--\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h=443=void mlx5_db_pools_cleanup(struct mlx5_core_dev *dev);\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h:444:int mlx5_init_one(struct mlx5_core_dev *dev);\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h:445:int mlx5_init_one_devl_locked(struct mlx5_core_dev *dev);\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h-446-void mlx5_uninit_one(struct mlx5_core_dev *dev);\n--\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h=450=int mlx5_load_one_devl_locked(struct mlx5_core_dev *dev, bool recovery);\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h:451:int mlx5_init_one_light(struct mlx5_core_dev *dev);\ndrivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h-452-void mlx5_uninit_one_light(struct mlx5_core_dev *dev);\n--\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c=25=static int mlx5_sf_dev_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c-69-\tif (MLX5_ESWITCH_MANAGER(sf_dev-\u003eparent_mdev))\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c:70:\t\terr = mlx5_init_one_light(mdev);\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c-71-\telse\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c:72:\t\terr = mlx5_init_one(mdev);\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c-73-\tif (err) {\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c:74:\t\tmlx5_core_warn(mdev, \"mlx5_init_one err=%d\\n\", err);\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c-75-\t\tgoto init_one_err;\n"
}

7/2 2026/11/05 18:30 llm patch-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56817
							output: 41
							thoughts: 143
						

Thoughts:
**My Analysis of the Mellanox Driver Probe Sequence**

Okay, let's break this down. My focus is currently on the Mellanox Ethernet driver and specifically the device probing process. I'm looking at `probe_one`, specifically the `drivers/net/ethernet/mellanox/mlx5/core/main.c` file. I see that `probe_one(struct pci_dev *pdev, const struct pci_device_id *id)` is clearly registered as a probe function using `pci_register_driver(&mlx5_core_driver)`. That's standard PCI device enumeration, nothing surprising there.

Now, here's where it gets interesting. I know that `mlx5_sf_dev_probe` gets registered as well, but that's for the auxiliary bus. The crucial question is: *where do these auxiliary devices come from in this architecture?*  And here's the connection: they are created through `sf_dev->parent_mdev`. And the parent device, `sf_dev->parent_mdev`, is, in turn, created by `probe_one` on a *physical* PCIe device. It's a parent-child relationship, essentially. `probe_one` creates the parent, which then, in turn, leads to the auxiliary devices.

Given this, I should now examine `mlx5_core_driver` more closely to confirm this entire sequence, and ensure I fully understand the driver's initialization and device hierarchy. I need to trace this a little further to make sure I grasp the finer points of this probe mechanism.



8/2 2026/11/05 18:31 tool grepper
0m Args:
{
  "Expression": "mlx5_core_driver",
  "PathPrefixes": [
    "drivers/net/ethernet/mellanox/mlx5/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/mellanox/mlx5/core/main.c=2272=int mlx5_recover_device(struct mlx5_core_dev *dev)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2282-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2283:static struct pci_driver mlx5_core_driver = {\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2284-\t.name           = KBUILD_MODNAME,\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=2308=struct mlx5_core_dev *mlx5_vf_get_core_dev(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2311-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2312:\tmdev = pci_iov_get_pf_drvdata(pdev, \u0026mlx5_core_driver);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2313-\tif (IS_ERR(mdev))\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=2350=static int __init mlx5_init(void)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2373-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2374:\terr = pci_register_driver(\u0026mlx5_core_driver);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2375-\tif (err)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=2391=static void __exit mlx5_cleanup(void)\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2392-{\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2393:\tpci_unregister_driver(\u0026mlx5_core_driver);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2394-\tmlx5_data_direct_driver_unregister();\n"
}

9/2 2026/11/05 18:31 llm patch-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57970
							output: 42
							thoughts: 13
						

Thoughts:
Okay, here's my summarized thought process, presented as if I'm thinking it:

**PCI ID Table Check for mlx5**

Alright, let's get down to business. Time to delve into this mlx5 situation. My first move here is clear: I need to consult the PCI ID table. This is the bedrock, the authoritative source, for understanding the hardware in play and, more specifically, how the system identifies and interprets the mlx5 devices. I'm not just idly browsing; I'm looking for *specific* information. This is where the initial mapping happens, isn't it? I need to verify vendor and device IDs, perhaps revision IDs, and confirm that the system correctly recognizes them. I'll likely be cross-referencing this against my knowledge of the mlx5 hardware and software stack. Essentially, I want to make sure the PCI ID table provides the correct base information. It’s an essential first step. There's not much point in proceeding unless the system has correctly identified the hardware in the first place, and this is where that's verified.



10/2 2026/11/05 18:33 tool grepper
0m Args:
{
  "Expression": "mlx5_pci_table",
  "PathPrefixes": [
    "drivers/net/ethernet/mellanox/mlx5/"
  ]
}

Results:
{
  "Output": "no matches"
}

11/2 2026/11/05 18:33 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 58271
							output: 48
							thoughts: 19
						

Thoughts:
Here's my thought process, as if I were considering this myself:

**Diving into the mlx5_core_driver Definition**

Okay, time to get straight to the point. I need to understand how the `mlx5_core_driver` is defined, so I'm going to head directly to `main.c`. That's the obvious place to start. This is likely the entry point, or at least a good place to begin searching for initialization and structure definitions. I'll be looking for where this driver structure is declared, initialized, and possibly registered with the kernel. I'm expecting to find the key components that describe the driver's interface and the functions that handle its core functionality. Let's dig in and see what we find.



12/2 2026/11/05 18:33 tool read-file
0m Args:
{
  "File": "drivers/net/ethernet/mellanox/mlx5/core/main.c",
  "FirstLine": 2280,
  "LineCount": 35
}

Results:
{
  "Contents": "2280:\t\treturn mlx5_load_one_devl_locked(dev, true);\n2281:\t}\n2282:\t\n2283:\tstatic struct pci_driver mlx5_core_driver = {\n2284:\t\t.name           = KBUILD_MODNAME,\n2285:\t\t.id_table       = mlx5_core_pci_table,\n2286:\t\t.probe          = probe_one,\n2287:\t\t.remove         = remove_one,\n2288:\t\t.suspend        = mlx5_suspend,\n2289:\t\t.resume         = mlx5_resume,\n2290:\t\t.shutdown\t= shutdown,\n2291:\t\t.err_handler\t= \u0026mlx5_err_handler,\n2292:\t\t.sriov_configure   = mlx5_core_sriov_configure,\n2293:\t\t.sriov_get_vf_total_msix = mlx5_sriov_get_vf_total_msix,\n2294:\t\t.sriov_set_msix_vec_count = mlx5_core_sriov_set_msix_vec_count,\n2295:\t};\n2296:\t\n2297:\t/**\n2298:\t * mlx5_vf_get_core_dev - Get the mlx5 core device from a given VF PCI device if\n2299:\t *                     mlx5_core is its driver.\n2300:\t * @pdev: The associated PCI device.\n2301:\t *\n2302:\t * Upon return the interface state lock stay held to let caller uses it safely.\n2303:\t * Caller must ensure to use the returned mlx5 device for a narrow window\n2304:\t * and put it back with mlx5_vf_put_core_dev() immediately once usage was over.\n2305:\t *\n2306:\t * Return: Pointer to the associated mlx5_core_dev or NULL.\n2307:\t */\n2308:\tstruct mlx5_core_dev *mlx5_vf_get_core_dev(struct pci_dev *pdev)\n2309:\t{\n2310:\t\tstruct mlx5_core_dev *mdev;\n2311:\t\n2312:\t\tmdev = pci_iov_get_pf_drvdata(pdev, \u0026mlx5_core_driver);\n2313:\t\tif (IS_ERR(mdev))\n2314:\t\t\treturn NULL;\n"
}

13/2 2026/11/05 18:33 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 59166
							output: 44
							thoughts: 21
						

Thoughts:
Here's my thought process, as it stands:

**Diving into the mlx5_core_pci_table in main.c**

Okay, so I need to understand what's happening with these PCI devices. My immediate instinct is to look at how the system interacts with the Mellanox hardware. Specifically, the `mlx5_core_pci_table` looks like the place to start. It's almost certainly a crucial data structure for initializing and managing the PCI devices, given the naming convention and context. I'll need to understand how it's defined, populated, and used within `main.c`. This table probably holds critical information like vendor IDs, device IDs, and maybe even specific flags for different Mellanox NIC models. Understanding how the system determines which driver to load for which device starts here. I expect to find device ID lookups, perhaps some kind of hardware probe logic tied to entries within that table. It's the lynchpin for hardware discovery.



14/2 2026/11/05 18:34 tool grepper
0m Args:
{
  "Expression": "mlx5_core_pci_table",
  "PathPrefixes": [
    "drivers/net/ethernet/mellanox/mlx5/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/mellanox/mlx5/core/main.c=2220=static int mlx5_resume(struct pci_dev *pdev)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2226-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2227:static const struct pci_device_id mlx5_core_pci_table[] = {\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2228-\t{ PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTIB) },\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2263-\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2264:MODULE_DEVICE_TABLE(pci, mlx5_core_pci_table);\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2265-\n--\ndrivers/net/ethernet/mellanox/mlx5/core/main.c=2283=static struct pci_driver mlx5_core_driver = {\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2284-\t.name           = KBUILD_MODNAME,\ndrivers/net/ethernet/mellanox/mlx5/core/main.c:2285:\t.id_table       = mlx5_core_pci_table,\ndrivers/net/ethernet/mellanox/mlx5/core/main.c-2286-\t.probe          = probe_one,\n"
}

15/2 2026/11/05 18:34 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 59884
							output: 38
							thoughts: 347
						

Thoughts:
**Analyzing the mlx5 Driver and Its Implications for Testing**

Okay, let's dive into this. First, I need to remind myself of the hardware landscape for this `mlx5_core_pci_table` in `drivers/net/ethernet/mellanox/mlx5/core/main.c`. Right, it's all Mellanox ConnectIB, ConnectX-4, ConnectX-5, ConnectX-6, BlueField – all physical PCIe NICs or SmartNICs from Mellanox/NVIDIA.

Now, let's revisit Section 2. Ah, yes, the "Structurally unreachable hardware" – that's the key. They call out specific examples: `mlxsw` (Mellanox switches), `pds_core` (Pensando's Distributed Services SmartNIC), `qed` (QLogic FastLinQ), `ionic` (another Pensando SmartNIC), and `amdgpu` (AMD GPUs). And here's the kicker: `mlx5` is right there on the list! Mellanox ConnectX/BlueField, these are SmartNICs or NICs – the exact kind of physical hardware that's *not* emulated in standard QEMU or Google Compute Engine VMs used by Syzkaller.

But wait a minute, are we just assuming? Does `mlx5` have *any* sort of virtual test framework, a dummy device driver, or a mock implementation within the kernel itself? I need to verify. Let's start by checking the `drivers/net/ethernet/mellanox/mlx5/` directory. I'll look for files like `netdevsim`, `mlx5_mock`, or anything that suggests a virtual device. If that doesn't work, I'll `grep` for `auxiliary_device_add` or virtual device registration within `mlx5`. That should reveal whether there is *any* way to test this without requiring physical hardware.



16/2 2026/11/05 18:35 tool grepper
0m Args:
{
  "Expression": "auxiliary_device_add",
  "PathPrefixes": [
    "drivers/net/ethernet/mellanox/"
  ]
}

Results:
{
  "Output": "drivers/net/ethernet/mellanox/mlx4/intf.c=99=static struct mlx4_adev *add_adev(struct mlx4_dev *dev, int idx)\n--\ndrivers/net/ethernet/mellanox/mlx4/intf.c-124-\ndrivers/net/ethernet/mellanox/mlx4/intf.c:125:\tret = auxiliary_device_add(adev);\ndrivers/net/ethernet/mellanox/mlx4/intf.c-126-\tif (ret) {\n--\ndrivers/net/ethernet/mellanox/mlx5/core/dev.c=306=static struct mlx5_adev *add_adev(struct mlx5_core_dev *dev, int idx)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/dev.c-330-\ndrivers/net/ethernet/mellanox/mlx5/core/dev.c:331:\tret = auxiliary_device_add(adev);\ndrivers/net/ethernet/mellanox/mlx5/core/dev.c-332-\tif (ret) {\n--\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c=88=static void mlx5_sf_dev_add(struct mlx5_core_dev *dev, u16 sf_index, u16 fn_id, u32 sfnum)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c-128-\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c:129:\terr = auxiliary_device_add(\u0026sf_dev-\u003eadev);\ndrivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c-130-\tif (err) {\n--\ndrivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c=50=int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard)\n--\ndrivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c-77-\ndrivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c:78:\terr = auxiliary_device_add(\u0026linecard_bdev-\u003eadev);\ndrivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c-79-\tif (err) {\n"
}

17/2 2026/11/05 18:35 llm patch-evaluator
2m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 61327
							output: 107
							thoughts: 1147
						

Thoughts:
**Analysis of the Mellanox mlx5 Driver Patch for Syzkaller Fuzzing**

Okay, so I'm looking at this patch and immediately, the initial code paths, `add_adev` in `mlx5/core/dev.c` and `mlx5_sf_dev_add`, both require an existing `mlx5_core_dev`. That means a physical Mellanox PCI device needs to be probed first, and that's the key.  I need to figure out what the patch *actually* does. Let's dig in.

The changes are within the `drivers/net/ethernet/mellanox/mlx5/core/` directory.  Specifically: `mlx5e_devlink_port_register` in `devlink.c`, a bunch of ESW code in `eswitch.h` and `eswitch_offloads.c` with those nested ESW functions,  and virtual LAG support in the `lag/` directory, including `debugfs.c`, `lag.c`, and so on.  Also, `mlx5_query_nic_sw_system_image_guid` is in `vport.c`.  All internal to the Mellanox mlx5 core driver.

Is there *any* code outside that directory and its header, `include/linux/mlx5/driver.h`? Let me re-check that diff... nope! It's all confined to the mlx5 driver and its headers. This is all about "virt LAG" (virtual LAG) offload functionality for Mellanox NICs, which includes support for nested LAG on SmartNICs like VFs/SFs in LAG mode. The patch is touching internal code.  Specifically, it seems to be updating the metadata layout for Nested (virt LAG) source-port.

Now, let's go back and examine those Negative Criteria. The concern is with structurally unreachable hardware... things like vendor-specific PCIe switches, SmartNICs, or GPU drivers that require specific ASIC/PCIe cards not emulated in standard QEMU.  Wait a minute... that's *exactly* what this is: Mellanox `mlx5` is a vendor-specific driver for Mellanox ConnectX and BlueField NICs. It needs *physical* Mellanox hardware.  That's a very clear negative criterion match!

This code can't be executed in any syzkaller environment, because standard QEMU or GCE VMs don't emulate the necessary Mellanox ConnectX hardware. The driver won't bind or instantiate devices in these environments.  The code will never be reached during fuzzing.

Therefore,  `WorthFuzzing` is unequivocally `false`. The `EnableConfigs` list is empty, because none of these configurations will enable execution, and the `FocusSymbols` list is also empty because no symbols within this code have any impact for fuzzing in the target environment.

The reasoning is that the patch modifies the Mellanox mlx5 driver (`drivers/net/ethernet/mellanox/mlx5/`) to enable "virt LAG" offload, which is for Mellanox ConnectX/BlueField PCIe network adapters and SmartNICs. Since standard QEMU and GCE fuzzing environments do not emulate Mellanox mlx5 hardware, the driver cannot bind or instantiate devices, rendering the modified code structurally unreachable during syzkaller fuzzing.

Let's call `set-results` now.



18/2 2026/11/05 18:37 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Mellanox mlx5 core driver (specifically virt LAG and eswitch offload logic). Mellanox ConnectX/BlueField PCIe network devices require vendor-specific physical hardware that is not emulated in standard virtualized QEMU or GCE fuzzing environments. Consequently, the driver cannot bind to any device, making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies the Mellanox mlx5 core driver (specifically virt LAG and eswitch offload logic). Mellanox ConnectX/BlueField PCIe network devices require vendor-specific physical hardware that is not emulated in standard virtualized QEMU or GCE fuzzing environments. Consequently, the driver cannot bind to any device, making the modified code structurally unreachable.",
  "WorthFuzzing": false
}

19/1 2026/11/05 18:37 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)