diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-06-15 11:45:05 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-06-15 11:45:06 -0700 |
| commit | 75983f837d20af89df60b4e8f08e5ca4e0a6cb72 (patch) | |
| tree | a08904c346d0902cdec5304aa6ad90f2ee2f2a7c | |
| parent | 2319688890d97c63da423a3c57c23b4ab5952dfc (diff) | |
| parent | 7bcfb19465fca99efd09ecb5d3ef8f91179d7ff1 (diff) | |
Merge branch 'net-mlx5-add-switchdev-mode-support-for-socket-direct-single-netdev-part-2-2'
Tariq Toukan says:
====================
net/mlx5: Add switchdev mode support for Socket Direct single netdev, part 2/2
This is part 2. Find part 1 here:
https://lore.kernel.org/all/20260531113954.395443-1-tariqt@nvidia.com/
This series enables Socket Direct single netdev to operate in switchdev
mode with shared FDB. SD single netdev combines multiple PCI functions
behind a single netdev interface. To support switchdev offloads, these
functions must participate in virtual LAG (shared FDB).
Design
Rather than introducing a separate LAG instance for SD, this series
integrates SD secondary devices into the existing LAG structure
(priv.lag) created at probe time. Each lag_func entry carries a
group_id field that identifies its SD group membership (0 means not
part of any SD group). An xarray mark (XA_MARK_PORT) distinguishes
physical port entries from SD secondaries, enabling a single unified
iterator that filters by group:
- MLX5_LAG_FILTER_PORTS: iterate port-level entries only (existing
behavior, used by bonding, FW LAG commands, v2p_map)
- MLX5_LAG_FILTER_ALL: iterate all devices including SD secondaries
(used by MPESW shared FDB across all devices)
- specific group_id: iterate only devices in that SD group (used by
per-group SD shared FDB operations)
Existing callers use mlx5_ldev_for_each() which maps to
MLX5_LAG_FILTER_PORTS, preserving current behavior for non-SD
configurations.
Lifecycle and ownership
The SD LAG lifecycle is tied to the SD group, not to bonding events:
1. At PCI probe, mlx5_lag_add_mdev() creates the LAG structure
(priv.lag) for each LAG-capable PF. e.g.: SD primary devices
2. During mlx5_sd_init(), after the SD group is fully formed (primary
and secondaries paired), sd_lag_init() registers the secondary
devices into the primary's existing priv.lag by calling
mlx5_ldev_add_mdev() with the SD group_id. The primary's lag_func
also gets its group_id set. No separate LAG instance is created.
3. After all the devices in SD group transition to switchdev,
mlx5_lag_shared_fdb_create() is invoked with the group_id to create
a software-only shared FDB scoped to that SD group. This sets
sd_fdb_active on all lag_func entries in the group. No FW LAG
commands are issued since SD devices share the same physical port.
4. If MPESW (multi-port eswitch) is enabled on top of SD groups, the
per-group SD shared FDB is torn down first, then MPESW shared FDB is
created spanning all devices (ports + SD secondaries) using
MLX5_LAG_FILTER_ALL. On MPESW disable, per-group SD shared FDB is
restored.
5. On SD teardown (mlx5_sd_cleanup or device unbind), sd_lag_cleanup()
removes secondaries from priv.lag and clears the primary's group_id.
The LAG structure itself is not destroyed.
The sd_fdb_active flag is set on all lag_func entries in a group (not
just the primary), so any device can detect the SD shared FDB state
during lag_disable_change teardown without needing to look up peer
entries.
SD shared FDB is a pure software construct -- unlike regular LAG modes
(ROCE, SRIOV, MPESW), it does not issue FW create_lag/destroy_lag
commands. The software vport LAG for SD is implemented via eswitch
egress ACL bounce rules, managed by the IB layer through
mlx5_eth_lag_init(). And the software LAG demux is implemented via
steering rules that utilize new destination, VHCA_RX.
Patches
E-Switch preparation (patch 1):
- Skip uplink IB rep load for SD secondary devices
Devcom support (patches 2-3):
- Expose locked variant of send_event
- Add DEVCOM_CANT_FAIL for non-rollback events
SD core hardening (patches 4-6):
- Make primary/secondary role determination more robust
- Add L2 table silent mode query support
- Expand vport metadata for SD secondary devices
SD switchdev transition (patches 7-8):
- Support switchdev mode transition with shared FDB
- Notify SD on eswitch disable
LAG integration (patches 9-12):
- Store demux resources per master lag_func
- Disable both regular and SD LAG on lag_disable_change
- Introduce software vport LAG implementation
- Add MPESW over SD LAG support
Deferred init (patches 13-14):
- Tie rep load/unload to SD LAG state
- Defer vport metadata init until SD is ready
Enablement (patch 15):
- Enable SD over ECPF and allow switchdev transition
v2: https://lore.kernel.org/20260608135547.482825-1-tariqt@nvidia.com
v1: https://lore.kernel.org/20260604114455.434711-1-tariqt@nvidia.com
====================
Link: https://patch.msgid.link/20260612113904.537595-1-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 9 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 296 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 21 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c | 205 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h | 30 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.c | 95 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lag/mpesw.h | 4 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lag/shared_fdb.c | 75 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c | 36 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h | 5 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c | 410 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h | 8 |
14 files changed, 1075 insertions, 122 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index b531d1c226b0..a0e2ca87b8d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -2065,6 +2065,7 @@ void mlx5_eswitch_disable(struct mlx5_eswitch *esw) mlx5_esw_reps_unblock(esw); esw->mode = MLX5_ESWITCH_LEGACY; + mlx5_sd_eswitch_mode_set(esw->dev, MLX5_ESWITCH_LEGACY); mlx5_lag_enable_change(esw->dev); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 94a530d19828..fea72b1dedab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -440,6 +440,7 @@ struct mlx5_eswitch { void esw_offloads_disable(struct mlx5_eswitch *esw); int esw_offloads_enable(struct mlx5_eswitch *esw); +int mlx5_esw_offloads_init_deferred_metadata(struct mlx5_eswitch *esw); void esw_offloads_cleanup(struct mlx5_eswitch *esw); int esw_offloads_init(struct mlx5_eswitch *esw); @@ -950,11 +951,16 @@ void esw_vport_change_handle_locked(struct mlx5_vport *vport); bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 controller); +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); 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, struct mlx5_eswitch *slave_esw); int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw); +void mlx5_eswitch_unload_reps(struct mlx5_eswitch *esw); bool mlx5_eswitch_is_peer(struct mlx5_eswitch *esw, struct mlx5_eswitch *peer_esw); @@ -1059,6 +1065,9 @@ mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) return 0; } +static inline void +mlx5_eswitch_unload_reps(struct mlx5_eswitch *esw) {} + static inline bool mlx5_eswitch_block_encap(struct mlx5_core_dev *dev, bool from_fdb) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 830fc910a080..907ee83a722d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -46,6 +46,7 @@ #include "fs_core.h" #include "lib/mlx5.h" #include "lib/devcom.h" +#include "lib/sd.h" #include "lib/eq.h" #include "lib/fs_chains.h" #include "en_tc.h" @@ -2862,6 +2863,10 @@ static int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num) int rep_type; int err; + if (vport_num != MLX5_VPORT_UPLINK && + mlx5_get_sd(esw->dev) && !mlx5_lag_is_active(esw->dev)) + return 0; + rep = mlx5_eswitch_get_rep(esw, vport_num); for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) { err = __esw_offloads_load_rep(esw, rep, rep_type, @@ -3040,6 +3045,136 @@ static int __esw_set_master_egress_rule(struct mlx5_core_dev *master, return err; } +static int esw_slave_egress_create_resources(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + struct mlx5_flow_table_attr ft_attr = { + .max_fte = 1, .prio = 0, .level = 0, + }; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_namespace *ns; + struct mlx5_flow_table *acl; + struct mlx5_flow_group *g; + u32 *flow_group_in; + int err = 0; + + if (vport->egress.acl) + return 0; + + xa_init_flags(&vport->egress.offloads.bounce_rules, XA_FLAGS_ALLOC); + ns = mlx5_get_flow_vport_namespace(esw->dev, + MLX5_FLOW_NAMESPACE_ESW_EGRESS, + vport->index); + if (!ns) + return -EINVAL; + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) + return -ENOMEM; + + if (vport->vport || mlx5_core_is_ecpf(esw->dev)) + ft_attr.flags = MLX5_FLOW_TABLE_OTHER_VPORT; + + acl = mlx5_create_vport_flow_table(ns, &ft_attr, vport->vport); + if (IS_ERR(acl)) { + err = PTR_ERR(acl); + goto out; + } + + g = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + goto err_table; + } + + vport->egress.acl = acl; + vport->egress.offloads.bounce_grp = g; + vport->egress.type = VPORT_EGRESS_ACL_TYPE_SHARED_FDB; + err = 0; + +err_table: + if (err && !IS_ERR_OR_NULL(acl)) { + mlx5_destroy_flow_table(acl); + vport->egress.acl = NULL; + } +out: + kvfree(flow_group_in); + return err; +} + +static void esw_slave_egress_destroy_resources(struct mlx5_vport *vport) +{ + if (!IS_ERR_OR_NULL(vport->egress.offloads.bounce_grp)) { + mlx5_destroy_flow_group(vport->egress.offloads.bounce_grp); + vport->egress.offloads.bounce_grp = NULL; + } + if (!IS_ERR_OR_NULL(vport->egress.acl)) { + esw_acl_egress_ofld_cleanup(vport); + xa_destroy(&vport->egress.offloads.bounce_rules); + } +} + +static int esw_set_slave_egress_rule(struct mlx5_core_dev *master, + struct mlx5_core_dev *slave) +{ + struct mlx5_eswitch *slave_esw = slave->priv.eswitch; + u16 master_vhca = MLX5_CAP_GEN(master, vhca_id); + struct mlx5_flow_destination dest = {}; + struct mlx5_flow_handle *bounce_rule; + struct mlx5_flow_act flow_act = {}; + struct mlx5_vport *slave_vport; + int err; + + slave_vport = mlx5_eswitch_get_vport(slave_esw, + slave_esw->manager_vport); + if (IS_ERR(slave_vport)) + return PTR_ERR(slave_vport); + + err = esw_slave_egress_create_resources(slave_esw, slave_vport); + if (err) + return err; + + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; + dest.vport.num = master->priv.eswitch->manager_vport; + dest.vport.vhca_id = master_vhca; + dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID; + + bounce_rule = mlx5_add_flow_rules(slave_vport->egress.acl, NULL, + &flow_act, &dest, 1); + if (IS_ERR(bounce_rule)) { + err = PTR_ERR(bounce_rule); + goto err_rule; + } + err = xa_insert(&slave_vport->egress.offloads.bounce_rules, + master_vhca, bounce_rule, GFP_KERNEL); + if (err) + goto err_insert; + + return 0; +err_insert: + mlx5_del_flow_rules(bounce_rule); +err_rule: + esw_slave_egress_destroy_resources(slave_vport); + return err; +} + +static void esw_unset_slave_egress_rule(struct mlx5_core_dev *master, + struct mlx5_core_dev *slave) +{ + struct mlx5_eswitch *slave_esw = slave->priv.eswitch; + u16 master_vhca = MLX5_CAP_GEN(master, vhca_id); + struct mlx5_vport *slave_vport; + + slave_vport = mlx5_eswitch_get_vport(slave_esw, + slave_esw->manager_vport); + if (IS_ERR(slave_vport)) + return; + + esw_acl_egress_ofld_bounce_rule_destroy(slave_vport, master_vhca); + esw_slave_egress_destroy_resources(slave_vport); +} + static int esw_master_egress_create_resources(struct mlx5_eswitch *esw, struct mlx5_flow_namespace *egress_ns, struct mlx5_vport *vport, size_t count) @@ -3164,6 +3299,9 @@ static void esw_unset_master_egress_rule(struct mlx5_core_dev *dev, vport = mlx5_eswitch_get_vport(dev->priv.eswitch, dev->priv.eswitch->manager_vport); + if (!vport->egress.acl) + return; + esw_acl_egress_ofld_bounce_rule_destroy(vport, MLX5_CAP_GEN(slave_dev, vhca_id)); if (xa_empty(&vport->egress.offloads.bounce_rules)) { @@ -3182,6 +3320,9 @@ int mlx5_eswitch_offloads_single_fdb_add_one(struct mlx5_eswitch *master_esw, if (err) return err; + if (!mlx5_sd_is_primary(slave_esw->dev)) + return 0; + err = esw_set_master_egress_rule(master_esw->dev, slave_esw->dev, max_slaves); if (err) @@ -3201,6 +3342,18 @@ void mlx5_eswitch_offloads_single_fdb_del_one(struct mlx5_eswitch *master_esw, esw_unset_master_egress_rule(master_esw->dev, slave_esw->dev); } +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); +} + +void mlx5_eswitch_offloads_vport_lag_del_one(struct mlx5_eswitch *master_esw, + struct mlx5_eswitch *slave_esw) +{ + esw_unset_slave_egress_rule(master_esw->dev, slave_esw->dev); +} + #define ESW_OFFLOADS_DEVCOM_PAIR (0) #define ESW_OFFLOADS_DEVCOM_UNPAIR (1) @@ -3401,7 +3554,7 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw, return; if ((MLX5_VPORT_MANAGER(esw->dev) || mlx5_core_is_ecpf_esw_manager(esw->dev)) && - !mlx5_lag_is_supported(esw->dev)) + (!mlx5_lag_is_supported(esw->dev) && !mlx5_get_sd(esw->dev))) return; xa_init(&esw->paired); @@ -3472,12 +3625,12 @@ u32 mlx5_esw_match_metadata_alloc(struct mlx5_eswitch *esw) u32 vport_end_ida = (1 << ESW_VPORT_BITS) - 1; /* Reserve 0xf for internal port offload */ u32 max_pf_num = (1 << ESW_PFNUM_BITS) - 2; - u32 pf_num; + int pf_num; int id; /* Only 4 bits of pf_num */ - pf_num = mlx5_get_dev_index(esw->dev); - if (pf_num > max_pf_num) + pf_num = mlx5_sd_pf_num_get(esw->dev); + if (pf_num < 0 || pf_num > max_pf_num) return 0; /* Metadata is 4 bits of PFNUM and 12 bits of unique id */ @@ -3522,6 +3675,7 @@ static void esw_offloads_vport_metadata_cleanup(struct mlx5_eswitch *esw, WARN_ON(vport->metadata != vport->default_metadata); mlx5_esw_match_metadata_free(esw, vport->default_metadata); + vport->default_metadata = 0; } static void esw_offloads_metadata_uninit(struct mlx5_eswitch *esw) @@ -3558,6 +3712,73 @@ metadata_err: return err; } +/* Deferred metadata init for SD devices: allocate vport metadata and + * refresh the ingress ACL for every vport whose ACL was created with + * metadata=0 in esw_create_offloads_acl_tables() / esw_vport_setup(). + * + * No Rep is loaded at this point ==> no Rep net-dev exists, so no need + * to take rtnl lock. + * + * Safe to call multiple times - subsequent calls are no-ops. + */ +int mlx5_esw_offloads_init_deferred_metadata(struct mlx5_eswitch *esw) +{ + struct mlx5_vport *manager, *vport; + unsigned long i; + int err; + + if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) + return 0; + + manager = mlx5_eswitch_get_vport(esw, esw->manager_vport); + if (IS_ERR(manager)) + return PTR_ERR(manager); + + /* Sanity check: skip if metadata was already initialized */ + if (manager->default_metadata) + return 0; + + err = esw_offloads_metadata_init(esw); + if (err) + return err; + + mutex_lock(&esw->state_lock); + /* Manager vport doesn't have a rep/netdev loaded but its ingress ACL + * was programmed with metadata=0 - refresh it explicitly. + */ + err = mlx5_esw_acl_ingress_vport_metadata_update(esw, + esw->manager_vport, + 0); + if (err) + goto err_acl; + + /* UPLINK is never marked enabled but its ACL is programmed in + * esw_create_offloads_acl_tables(); refresh it explicitly. + */ + err = mlx5_esw_acl_ingress_vport_metadata_update(esw, MLX5_VPORT_UPLINK, + 0); + if (err) + goto err_acl; + + mlx5_esw_for_each_vport(esw, i, vport) { + if (!vport || !vport->enabled) + continue; + err = mlx5_esw_acl_ingress_vport_metadata_update(esw, + vport->vport, + 0); + if (err) + goto err_acl; + } + + mutex_unlock(&esw->state_lock); + return 0; + +err_acl: + esw_offloads_metadata_uninit(esw); + mutex_unlock(&esw->state_lock); + return err; +} + int esw_vport_create_offloads_acl_tables(struct mlx5_eswitch *esw, struct mlx5_vport *vport) @@ -3630,6 +3851,21 @@ static void esw_destroy_offloads_acl_tables(struct mlx5_eswitch *esw) esw_vport_destroy_offloads_acl_tables(esw, vport); } +void mlx5_eswitch_unload_reps(struct mlx5_eswitch *esw) +{ + struct mlx5_eswitch_rep *rep; + unsigned long i; + + if (!esw || esw->mode != MLX5_ESWITCH_OFFLOADS) + return; + + mlx5_esw_for_each_rep(esw, i, rep) { + if (rep->vport == MLX5_VPORT_UPLINK) + continue; + mlx5_esw_offloads_rep_unload(esw, rep->vport); + } +} + int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) { struct mlx5_eswitch_rep *rep; @@ -3643,11 +3879,23 @@ int mlx5_eswitch_reload_ib_reps(struct mlx5_eswitch *esw) if (atomic_read(&rep->rep_data[REP_ETH].state) != REP_LOADED) return 0; - ret = __esw_offloads_load_rep(esw, rep, REP_IB, NULL); - if (ret) - return ret; + /* SD secondary devices share the primary's uplink and do not + * have their own uplink representor. Only load VF/SF vports. + */ + if (mlx5_sd_is_primary(esw->dev)) { + ret = __esw_offloads_load_rep(esw, rep, REP_IB, NULL); + if (ret) + return ret; + } mlx5_esw_for_each_rep(esw, i, rep) { + if (!mlx5_sd_is_primary(esw->dev) && + rep->vport == MLX5_VPORT_UPLINK) + continue; + if (rep->vport != MLX5_VPORT_UPLINK && + mlx5_get_sd(esw->dev) && !mlx5_lag_is_active(esw->dev)) + continue; + if (atomic_read(&rep->rep_data[REP_ETH].state) == REP_LOADED) __esw_offloads_load_rep(esw, rep, REP_IB, NULL); } @@ -3892,9 +4140,14 @@ int esw_offloads_enable(struct mlx5_eswitch *esw) if (err) goto err_roce; - err = esw_offloads_metadata_init(esw); - if (err) - goto err_metadata; + /* SD devices defer metadata init until SD is ready and + * mlx5_sd_pf_num_get() can return the correct pf_num. + */ + if (!mlx5_get_sd(esw->dev)) { + err = esw_offloads_metadata_init(esw); + if (err) + goto err_metadata; + } err = esw_set_passing_vport_metadata(esw, true); if (err) @@ -4219,12 +4472,6 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, if (esw_mode_from_devlink(mode, &mlx5_mode)) return -EINVAL; - if (mlx5_mode == MLX5_ESWITCH_OFFLOADS && mlx5_get_sd(esw->dev)) { - NL_SET_ERR_MSG_MOD(extack, - "Can't change E-Switch mode to switchdev when multi-PF netdev (Socket Direct) is configured."); - return -EPERM; - } - /* Avoid try_lock, active/inactive mode change is not restricted */ if (mlx5_devlink_switchdev_active_mode_change(esw, mode)) return 0; @@ -4298,6 +4545,9 @@ unlock: mlx5_esw_unlock(esw); enable_lag: mlx5_lag_enable_change(esw->dev); + /* Shared FDB activation is creating LAG which is changing reps. */ + if (!err) + mlx5_sd_eswitch_mode_set(esw->dev, mlx5_mode); return err; } @@ -4586,13 +4836,25 @@ mlx5_eswitch_register_vport_reps_blocked(struct mlx5_eswitch *esw, static void mlx5_eswitch_reload_reps_blocked(struct mlx5_eswitch *esw) { + struct mlx5_eswitch_rep *uplink; struct mlx5_vport *vport; + bool newly_loaded; unsigned long i; if (esw->mode != MLX5_ESWITCH_OFFLOADS) return; - if (mlx5_esw_offloads_rep_load(esw, MLX5_VPORT_UPLINK)) + uplink = mlx5_eswitch_get_rep(esw, MLX5_VPORT_UPLINK); + if (__esw_offloads_load_rep(esw, uplink, REP_ETH, &newly_loaded)) + return; + if (mlx5_sd_is_primary(esw->dev) && + __esw_offloads_load_rep(esw, uplink, REP_IB, NULL)) { + if (newly_loaded) + __esw_offloads_unload_rep(esw, uplink, REP_ETH); + return; + } + + if (mlx5_get_sd(esw->dev) && !mlx5_lag_is_active(esw->dev)) return; mlx5_esw_for_each_vport(esw, i, vport) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 1cd4cd898ec2..8af73393770c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -1217,3 +1217,24 @@ int mlx5_fs_cmd_set_tx_flow_table_root(struct mlx5_core_dev *dev, u32 ft_id, boo return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } + +int mlx5_fs_cmd_query_l2table_silent(struct mlx5_core_dev *dev, u8 *silent_mode) +{ + u32 out[MLX5_ST_SZ_DW(query_l2_table_entry_out)] = {}; + u32 in[MLX5_ST_SZ_DW(query_l2_table_entry_in)] = {}; + int err; + + if (!MLX5_CAP_GEN(dev, silent_mode_query)) + return -EOPNOTSUPP; + + MLX5_SET(query_l2_table_entry_in, in, opcode, + MLX5_CMD_OP_QUERY_L2_TABLE_ENTRY); + MLX5_SET(query_l2_table_entry_in, in, silent_mode_query, 1); + + err = mlx5_cmd_exec_inout(dev, query_l2_table_entry, in, out); + if (err) + return err; + + *silent_mode = MLX5_GET(query_l2_table_entry_out, out, silent_mode); + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 7eb7b3ffe3d8..60280ff7da50 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -124,6 +124,8 @@ const struct mlx5_flow_cmds *mlx5_fs_cmd_get_fw_cmds(void); int mlx5_fs_cmd_set_l2table_entry_silent(struct mlx5_core_dev *dev, u8 silent_mode); int mlx5_fs_cmd_set_tx_flow_table_root(struct mlx5_core_dev *dev, u32 ft_id, bool disconnect); +int mlx5_fs_cmd_query_l2table_silent(struct mlx5_core_dev *dev, + u8 *silent_mode); static inline bool mlx5_fs_cmd_is_fw_term_table(struct mlx5_flow_table *ft) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index dd3f18f85466..28d16fdc3f06 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -139,9 +139,44 @@ static int mlx5_cmd_modify_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev, return mlx5_cmd_exec_in(dev, modify_lag, in); } +static u32 mlx5_lag_dev_group_id(struct mlx5_core_dev *dev) +{ + struct mlx5_lag *ldev = mlx5_lag_dev(dev); + struct lag_func *pf; + 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; + } + return 0; +} + +static int mlx5_lag_is_sw_lag(struct mlx5_core_dev *dev) +{ + return mlx5_lag_is_sd(dev); +} + int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev) { u32 in[MLX5_ST_SZ_DW(create_vport_lag_in)] = {}; + struct mlx5_lag *ldev = mlx5_lag_dev(dev); + int ret; + + if (mlx5_lag_is_sw_lag(dev)) { + if (!ldev) + return -ENODEV; + + mutex_lock(&ldev->lock); + ret = mlx5_lag_create_vport_lag(mlx5_lag_dev(dev), + mlx5_lag_dev_group_id(dev)); + mutex_unlock(&ldev->lock); + return ret; + } MLX5_SET(create_vport_lag_in, in, opcode, MLX5_CMD_OP_CREATE_VPORT_LAG); @@ -152,6 +187,18 @@ EXPORT_SYMBOL(mlx5_cmd_create_vport_lag); 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 (!ldev) + return 0; + + mutex_lock(&ldev->lock); + mlx5_lag_destroy_vport_lag(mlx5_lag_dev(dev), + mlx5_lag_dev_group_id(dev)); + mutex_unlock(&ldev->lock); + return 0; + } MLX5_SET(destroy_vport_lag_in, in, opcode, MLX5_CMD_OP_DESTROY_VPORT_LAG); @@ -1265,6 +1312,32 @@ int mlx5_lag_reload_ib_reps_from_locked(struct mlx5_lag *ldev, u32 flags, return mlx5_lag_reload_ib_reps(ldev, flags, filter, cont_on_fail); } +static void mlx5_lag_unload_reps_unlocked(struct mlx5_lag *ldev, u32 filter) +{ + struct lag_func *pf; + 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; + mlx5_esw_reps_block(esw); + mlx5_eswitch_unload_reps(esw); + mlx5_esw_reps_unblock(esw); + } +} + +void mlx5_lag_unload_reps_from_locked(struct mlx5_lag *ldev, u32 filter) +{ + /* Same lock dance as mlx5_lag_reload_ib_reps: drop ldev->lock around + * the per-eswitch reps_lock to keep the reps_lock -> ldev->lock order. + */ + mlx5_lag_drop_lock_for_reps(ldev, filter); + mlx5_lag_unload_reps_unlocked(ldev, filter); + mlx5_lag_retake_lock_after_reps(ldev); +} + void mlx5_disable_lag(struct mlx5_lag *ldev) { bool shared_fdb = test_bit(MLX5_LAG_MODE_FLAG_SHARED_FDB, &ldev->mode_flags); @@ -1590,7 +1663,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 mlx5_lag *ldev) + struct lag_func *pf) { #ifdef CONFIG_MLX5_ESWITCH struct mlx5_flow_namespace *ns; @@ -1601,20 +1674,20 @@ static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev, if (!ns) return 0; - ldev->lag_demux_ft = mlx5_create_flow_table(ns, ft_attr); - if (IS_ERR(ldev->lag_demux_ft)) - return PTR_ERR(ldev->lag_demux_ft); + 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); fg = mlx5_esw_lag_demux_fg_create(dev->priv.eswitch, - ldev->lag_demux_ft); + pf->lag_demux_ft); if (IS_ERR(fg)) { err = PTR_ERR(fg); - mlx5_destroy_flow_table(ldev->lag_demux_ft); - ldev->lag_demux_ft = NULL; + mlx5_destroy_flow_table(pf->lag_demux_ft); + pf->lag_demux_ft = NULL; return err; } - ldev->lag_demux_fg = fg; + pf->lag_demux_fg = fg; return 0; #else return -EOPNOTSUPP; @@ -1623,7 +1696,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 mlx5_lag *ldev) + struct lag_func *pf) { struct mlx5_flow_namespace *ns; int err; @@ -1632,12 +1705,12 @@ static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev, if (!ns) return 0; - ldev->lag_demux_fg = NULL; + pf->lag_demux_fg = NULL; ft_attr->max_fte = 1; - ldev->lag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr); - if (IS_ERR(ldev->lag_demux_ft)) { - err = PTR_ERR(ldev->lag_demux_ft); - ldev->lag_demux_ft = NULL; + 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; return err; } @@ -1648,6 +1721,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; if (!ft_attr) return -EINVAL; @@ -1656,12 +1730,16 @@ int mlx5_lag_demux_init(struct mlx5_core_dev *dev, if (!ldev) return -ENODEV; - xa_init(&ldev->lag_demux_rules); + pf = mlx5_lag_pf_by_dev(ldev, dev); + if (!pf) + return -ENODEV; + + xa_init(&pf->lag_demux_rules); - if (mlx5_get_sd(dev)) - return mlx5_lag_demux_ft_fg_init(dev, ft_attr, ldev); + if (mlx5_lag_is_sw_lag(dev)) + return mlx5_lag_demux_ft_fg_init(dev, ft_attr, pf); - return mlx5_lag_demux_fw_init(dev, ft_attr, ldev); + return mlx5_lag_demux_fw_init(dev, ft_attr, pf); } EXPORT_SYMBOL(mlx5_lag_demux_init); @@ -1670,40 +1748,63 @@ 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; ldev = mlx5_lag_dev(dev); if (!ldev) return; - xa_for_each(&ldev->lag_demux_rules, vport_num, rule) + pf = mlx5_lag_pf_by_dev(ldev, dev); + if (!pf) + return; + + xa_for_each(&pf->lag_demux_rules, vport_num, rule) mlx5_del_flow_rules(rule); - xa_destroy(&ldev->lag_demux_rules); + xa_destroy(&pf->lag_demux_rules); - if (ldev->lag_demux_fg) - mlx5_destroy_flow_group(ldev->lag_demux_fg); - if (ldev->lag_demux_ft) - mlx5_destroy_flow_table(ldev->lag_demux_ft); - ldev->lag_demux_fg = NULL; - ldev->lag_demux_ft = NULL; + 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; } 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) +{ + u32 filter = mlx5_lag_get_filter(ldev, dev); + int idx; + + idx = mlx5_lag_get_dev_index_by_seq_filter(ldev, MLX5_LAG_P1, filter); + if (idx < 0) + return NULL; + + return mlx5_lag_pf(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 mlx5_lag *ldev; int err; ldev = mlx5_lag_dev(vport_dev); - if (!ldev || !ldev->lag_demux_fg) + if (!ldev) + return 0; + + master = mlx5_lag_dev_get_master_pf(ldev, vport_dev); + if (!master || !master->lag_demux_fg) return 0; - if (xa_load(&ldev->lag_demux_rules, index)) + if (xa_load(&master->lag_demux_rules, index)) return 0; rule = mlx5_esw_lag_demux_rule_create(vport_dev->priv.eswitch, - vport_num, ldev->lag_demux_ft); + vport_num, master->lag_demux_ft); if (IS_ERR(rule)) { err = PTR_ERR(rule); mlx5_core_warn(vport_dev, @@ -1712,7 +1813,7 @@ int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num, return err; } - err = xa_err(xa_store(&ldev->lag_demux_rules, index, rule, + err = xa_err(xa_store(&master->lag_demux_rules, index, rule, GFP_KERNEL)); if (err) { mlx5_del_flow_rules(rule); @@ -1728,13 +1829,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 mlx5_lag *ldev; ldev = mlx5_lag_dev(dev); - if (!ldev || !ldev->lag_demux_fg) + if (!ldev) + return; + + master_pf = mlx5_lag_dev_get_master_pf(ldev, dev); + if (!master_pf || !master_pf->lag_demux_fg) return; - rule = xa_erase(&ldev->lag_demux_rules, index); + rule = xa_erase(&master_pf->lag_demux_rules, index); if (rule) mlx5_del_flow_rules(rule); } @@ -2461,13 +2567,26 @@ EXPORT_SYMBOL(mlx5_lag_is_shared_fdb); 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; + bool mpesw; + int i; ldev = mlx5_lag_dev(dev); if (!ldev) return; - mlx5_devcom_comp_lock(dev->priv.hca_devcom_comp); + if (sd_devcom) { + mlx5_devcom_comp_lock(sd_devcom); + primary = mlx5_sd_get_primary(dev) ?: dev; + mlx5_devcom_comp_unlock(sd_devcom); + } + mlx5_devcom_comp_lock(primary->priv.hca_devcom_comp); + mpesw = ldev->mode == MLX5_LAG_MODE_MPESW; + if (mpesw) + mlx5_mpesw_sd_devcoms_lock(ldev); mutex_lock(&ldev->lock); ldev->mode_changes_in_progress++; @@ -2479,7 +2598,25 @@ void mlx5_lag_disable_change(struct mlx5_core_dev *dev) } mutex_unlock(&ldev->lock); - mlx5_devcom_comp_unlock(dev->priv.hca_devcom_comp); + if (mpesw) + mlx5_mpesw_sd_devcoms_unlock(ldev); + mlx5_devcom_comp_unlock(primary->priv.hca_devcom_comp); + + if (!sd_devcom) + return; + + /* Teardown SD shared FDB for this device's group if active */ + mlx5_devcom_comp_lock(sd_devcom); + mutex_lock(&ldev->lock); |
